Reset RubberBandStretcher in init.
[stretchplayer:stretchplayer.git] / src / Engine.cpp
1 /*
2  * Copyright(c) 2009 by Gabriel M. Beddingfield <gabriel@teuton.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY, without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  */
19
20 #include "Engine.hpp"
21 #include <sndfile.h>
22 #include <rubberband/RubberBandStretcher.h>
23 #include <stdexcept>
24 #include <cassert>
25 #include <cstring>
26 #include <cmath>
27 #include <cstdlib>
28 #include <algorithm>
29 #include <iostream>
30 #include <QFileInfo>
31 #include <AudioSystem.hpp>
32 #include <JackAudioSystem.hpp>
33 #include <QString>
34
35 using RubberBand::RubberBandStretcher;
36 using namespace std;
37
38 namespace StretchPlayer
39 {
40     Engine::Engine()
41         : _playing(false),
42           _state_changed(false),
43           _position(0),
44           _loop_a(0),
45           _loop_b(0),
46           _sample_rate(48000.0),
47           _stretch(1.0),
48           _pitch(0),
49           _gain(1.0)
50     {
51         QString err;
52         QMutexLocker lk(&_audio_lock);
53
54         _audio_system.reset( new JackAudioSystem );
55         QString app_name("StretchPlayer");
56         _audio_system->init( &app_name, &err);
57         _audio_system->set_process_callback(Engine::static_process_callback, this);
58
59         if( ! err.isNull() )
60             throw std::runtime_error(err.toLocal8Bit().data());
61
62
63         uint32_t sample_rate = _audio_system->sample_rate();
64
65         _stretcher.reset(
66             new RubberBandStretcher( sample_rate,
67                                      2,
68                                      RubberBandStretcher::OptionProcessRealTime
69                                      | RubberBandStretcher::OptionThreadingAuto
70                 )
71             );
72         _stretcher->setMaxProcessSize(16384);
73         _stretcher->reset();
74
75         if( _audio_system->activate(&err) )
76             throw std::runtime_error(err.toLocal8Bit().data());
77
78     }
79
80     Engine::~Engine()
81     {
82         QMutexLocker lk(&_audio_lock);
83
84         _audio_system->deactivate();
85         _audio_system->cleanup();
86
87         callback_seq_t::iterator it;
88         QMutexLocker lk_cb(&_callback_lock);
89         for( it=_error_callbacks.begin() ; it!=_error_callbacks.end() ; ++it ) {
90             (*it)->_parent = 0;
91         }
92         for( it=_message_callbacks.begin() ; it!=_message_callbacks.end() ; ++it ) {
93             (*it)->_parent = 0;
94         }
95     }
96
97     void Engine::_zero_buffers(uint32_t nframes)
98     {
99         // MUTEX MUST ALREADY BE LOCKED
100
101         // Just zero the buffers
102         void *buf_L = 0, *buf_R = 0;
103         buf_L = _audio_system->output_buffer(0);
104         if(buf_L) {
105             memset(buf_L, 0, nframes * sizeof(float));
106         }
107         buf_R = _audio_system->output_buffer(1);
108         if(buf_R) {
109             memset(buf_R, 0, nframes * sizeof(float));
110         }
111     }
112
113     int Engine::process_callback(uint32_t nframes)
114     {
115         bool locked = false;
116
117         try {
118             locked = _audio_lock.tryLock();
119             if(_state_changed) {
120                 _state_changed = false;
121                 _stretcher->reset();
122             }
123             if(locked) {
124                 if(_playing) {
125                     if(_left.size()) {
126                         _process_playing(nframes);
127                     } else {
128                         _playing = false;
129                     }
130                 } else {
131                     _zero_buffers(nframes);
132                 }
133             } else {
134                 _zero_buffers(nframes);
135             }
136         } catch (...) {
137         }
138
139         if(locked) _audio_lock.unlock();
140
141         return 0;
142     }
143
144     void Engine::_process_playing(uint32_t nframes)
145     {
146         // MUTEX MUST ALREADY BE LOCKED
147         float *buf_L = 0, *buf_R = 0;
148
149         buf_L = _audio_system->output_buffer(0);
150         buf_R = _audio_system->output_buffer(1);
151         float* rb_buf_in[2];
152         float* rb_buf_out[2] = { buf_L, buf_R };
153
154         uint32_t srate = _audio_system->sample_rate();
155
156         _stretcher->setTimeRatio( srate / _sample_rate / _stretch );
157         _stretcher->setPitchScale( ::pow(2.0, double(_pitch)/12.0) * _sample_rate / srate );
158
159         uint32_t frame;
160         uint32_t reqd, gend, zeros, feed;
161
162         frame = 0;
163         while( frame < nframes ) {
164             reqd = _stretcher->getSamplesRequired();
165             int avail = _stretcher->available();
166             if( avail <= 0 ) avail = 0;
167             if( unsigned(avail) >= nframes ) reqd = 0;
168             zeros = 0;
169             feed = reqd;
170             if( looping() && ((_position + reqd) >=_loop_b) ) {
171                 assert( _loop_b >= _position );
172                 reqd = _loop_b - _position;
173             }
174             if( _position + reqd > _left.size() ) {
175                 feed = _left.size() - _position;
176                 zeros = reqd - feed;
177             }
178             rb_buf_in[0] = &_left[_position];
179             rb_buf_in[1] = &_right[_position];
180             if( reqd == 0 ) {
181                 feed = 0;
182                 zeros = 0;
183             }
184             _stretcher->process( rb_buf_in, feed, false);
185             if(reqd && zeros) {
186                 float l[zeros], r[zeros];
187                 float* z[2] = { l, r };
188                 memset(l, 0, zeros * sizeof(float));
189                 memset(r, 0, zeros * sizeof(float));
190                 _stretcher->process(z, zeros, false);
191             }
192             gend = 1;
193             while( gend && frame < nframes ) {
194                 gend = _stretcher->retrieve(rb_buf_out, (nframes-frame));
195                 rb_buf_out[0] += gend;
196                 rb_buf_out[1] += gend;
197                 frame += gend;
198             }
199             _position += feed;
200             if( looping() && _position >= _loop_b ) {
201                 _position = _loop_a;
202             }
203         }
204
205         // Apply gain and clip
206         for( frame=0 ; frame<nframes ; ++frame ) {
207             buf_L[frame] *= _gain;
208             if(buf_L[frame] > 1.0) buf_L[frame] = 1.0;
209             if(buf_L[frame] < -1.0) buf_L[frame] = -1.0;
210             buf_R[frame] *= _gain;
211             if(buf_R[frame] > 1.0) buf_R[frame] = 1.0;
212             if(buf_R[frame] < -1.0) buf_R[frame] = -1.0;
213         }
214
215         if(_position >= _left.size()) {
216             _playing = false;
217             _position = 0;
218         }
219     }
220
221     /**
222      * Load a file
223      *
224      * \return Name of song
225      */
226     QString Engine::load_song(const QString& filename)
227     {
228         QMutexLocker lk(&_audio_lock);
229         stop();
230         _left.clear();
231         _right.clear();
232         _position = 0;
233
234         SNDFILE *sf = 0;
235         SF_INFO sf_info;
236         memset(&sf_info, 0, sizeof(sf_info));
237
238         _message( QString("Opening file...") );
239         sf = sf_open(filename.toLocal8Bit().data(), SFM_READ, &sf_info);
240         if( !sf ) {
241             _error( QString("Error opening file '%1': %2")
242                     .arg(filename)
243                     .arg( sf_strerror(sf) ) );
244             return QString();
245         }
246
247         _sample_rate = sf_info.samplerate;
248         _left.reserve( sf_info.frames );
249         _right.reserve( sf_info.frames );
250
251         if(sf_info.frames == 0) {
252             _error( QString("Error opening file '%1': File is empty")
253                     .arg(filename) );
254             sf_close(sf);
255             return QString();
256         }
257
258         _message( QString("Opening file...") );
259         std::vector<float> buf(4096, 0.0f);
260         sf_count_t read, k;
261         unsigned mod;
262         while(true) {
263             read = sf_read_float(sf, &buf[0], buf.size());
264             if( read < 1 ) break;
265             for(k=0 ; k<read ; ++k) {
266                 mod = k % sf_info.channels;
267                 if( mod == 0 ) {
268                     _left.push_back( buf[k] );
269                 } else if( mod == 1 ) {
270                     _right.push_back( buf[k] );
271                 } else {
272                     // remaining channels ignored
273                 }
274             }
275         }
276
277         if( _left.size() != sf_info.frames ) {
278             _error( QString("Warning: not all of the file data was read.") );
279         }
280
281         sf_close(sf);
282         QFileInfo f_info(filename);
283         return f_info.fileName();
284     }
285
286     void Engine::play()
287     {
288         if( ! _playing ) {
289             _state_changed = true;
290             _playing = true;
291         }
292     }
293
294     void Engine::play_pause()
295     {
296         _playing = (_playing) ? false : true;
297         _state_changed = true;
298     }
299
300     void Engine::stop()
301     {
302         if( _playing ) {
303             _playing = false;
304             _state_changed = true;
305         }
306     }
307
308     float Engine::get_position()
309     {
310         if(_left.size() > 0) {
311             return float(_position) / _sample_rate;
312         }
313         return 0;
314     }
315
316     void Engine::loop_ab()
317     {
318         if( _loop_b > _loop_a ) {
319             _loop_b = 0;
320             _loop_a = 0;
321         } else if( _loop_a == 0 ) {
322             _loop_a = _position;
323             if(_position == 0) {
324                 _loop_a = 1;
325             }
326         } else if( _loop_a != 0 ) {
327             if( _position > _loop_a ) {
328                 _loop_b = _position;
329             } else {
330                 _loop_a = _position;
331             }
332         } else {
333             assert(false);  // invalid state
334         }
335     }
336
337     float Engine::get_length()
338     {
339         if(_left.size() > 0) {
340             return float(_left.size()) / _sample_rate;
341         }
342         return 0;
343     }
344
345     void Engine::locate(double secs)
346     {
347         unsigned long pos = secs * _sample_rate;
348         QMutexLocker lk(&_audio_lock);
349         _position = pos;
350         _state_changed = true;
351     }
352
353     void Engine::_dispatch_message(const Engine::callback_seq_t& seq, const QString& msg) const
354     {
355         QMutexLocker lk(&_callback_lock);
356         Engine::callback_seq_t::const_iterator it;
357         for( it=seq.begin() ; it!=seq.end() ; ++it ) {
358             (**it)(msg);
359         }
360     }
361
362     void Engine::_subscribe_list(Engine::callback_seq_t& seq, EngineMessageCallback* obj)
363     {
364         if( obj == 0 ) return;
365         QMutexLocker lk(&_callback_lock);
366         obj->_parent = this;
367         seq.insert(obj);
368     }
369
370     void Engine::_unsubscribe_list(Engine::callback_seq_t& seq, EngineMessageCallback* obj)
371     {
372         if( obj == 0 ) return;
373         QMutexLocker lk(&_callback_lock);
374         obj->_parent = 0;
375         seq.erase(obj);
376     }
377
378     float Engine::get_cpu_load()
379     {
380         return _audio_system->dsp_load();
381     }
382
383 } // namespace StretchPlayer