Create drivers using a factory system.
[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 "AudioSystem.hpp"
22 #include "RubberBandServer.hpp"
23 #include "Configuration.hpp"
24 #include <sndfile.h>
25 #include <stdexcept>
26 #include <cassert>
27 #include <cstring>
28 #include <cmath>
29 #include <cstdlib>
30 #include <algorithm>
31 #include <QFileInfo>
32 #include <QString>
33
34 #include "config.h"
35
36 using RubberBand::RubberBandStretcher;
37
38 namespace StretchPlayer
39 {
40     Engine::Engine(Configuration *config)
41         : _config(config),
42           _playing(false),
43           _hit_end(false),
44           _state_changed(false),
45           _position(0),
46           _loop_a(0),
47           _loop_b(0),
48           _sample_rate(48000.0),
49           _stretch(1.0),
50           _pitch(0),
51           _gain(1.0),
52           _output_position(0)
53     {
54         QString err;
55
56         QMutexLocker lk(&_audio_lock);
57
58         Configuration::driver_t pref_driver;
59
60         if(_config) {
61             pref_driver = _config->driver();
62         }
63
64         _audio_system.reset( audio_system_factory(pref_driver) );
65
66         QString app_name("StretchPlayer");
67         _audio_system->init( &app_name, _config, &err );
68         _audio_system->set_process_callback(Engine::static_process_callback, this);
69         _audio_system->set_segment_size_callback(Engine::static_segment_size_callback, this);
70
71         if( ! err.isNull() ) {
72             char msg[513];
73             strncpy(msg, err.toLocal8Bit().data(), 512); 
74             throw std::runtime_error(msg);
75         }
76
77
78         uint32_t sample_rate = _audio_system->sample_rate();
79
80         _stretcher.reset( new RubberBandServer(sample_rate) );
81         _stretcher->set_segment_size( _audio_system->current_segment_size() );
82         _stretcher->start();
83
84         if( _audio_system->activate(&err) )
85             throw std::runtime_error(err.toLocal8Bit().data());
86
87     }
88
89     Engine::~Engine()
90     {
91         QMutexLocker lk(&_audio_lock);
92
93         _stretcher->go_idle();
94         _stretcher->shutdown();
95
96         _audio_system->deactivate();
97         _audio_system->cleanup();
98
99         callback_seq_t::iterator it;
100         QMutexLocker lk_cb(&_callback_lock);
101         for( it=_error_callbacks.begin() ; it!=_error_callbacks.end() ; ++it ) {
102             (*it)->_parent = 0;
103         }
104         for( it=_message_callbacks.begin() ; it!=_message_callbacks.end() ; ++it ) {
105             (*it)->_parent = 0;
106         }
107
108         _stretcher->wait();
109     }
110
111     void Engine::_zero_buffers(uint32_t nframes)
112     {
113         // MUTEX MUST ALREADY BE LOCKED
114
115         // Just zero the buffers
116         void *buf_L = 0, *buf_R = 0;
117         buf_L = _audio_system->output_buffer(0);
118         if(buf_L) {
119             memset(buf_L, 0, nframes * sizeof(float));
120         }
121         buf_R = _audio_system->output_buffer(1);
122         if(buf_R) {
123             memset(buf_R, 0, nframes * sizeof(float));
124         }
125     }
126
127     int Engine::segment_size_callback(uint32_t nframes)
128     {
129         _stretcher->set_segment_size(nframes);
130     }
131
132     int Engine::process_callback(uint32_t nframes)
133     {
134         bool locked = false;
135
136         if( _loop_ab_pressed > 0 ) {
137             _handle_loop_ab();
138         }
139
140         try {
141             locked = _audio_lock.tryLock();
142             if(_state_changed) {
143                 _state_changed = false;
144                 _stretcher->reset();
145                 float left[64], right[64];
146                 while( _stretcher->available_read() > 0 )
147                     _stretcher->read_audio(left, right, 64);
148                 assert( 0 == _stretcher->available_read() );
149                 _position = _output_position;
150             }
151             if(locked) {
152                 if(_playing) {
153                     if(_left.size()) {
154                         _process_playing(nframes);
155                     } else {
156                         _playing = false;
157                     }
158                 } else {
159                     _zero_buffers(nframes);
160                 }
161             } else {
162                 _zero_buffers(nframes);
163             }
164         } catch (...) {
165         }
166
167         if(locked) _audio_lock.unlock();
168
169         return 0;
170     }
171
172     static void apply_gain_to_buffer(float *buf, uint32_t frames, float gain);
173
174     void Engine::_process_playing(uint32_t nframes)
175     {
176         // MUTEX MUST ALREADY BE LOCKED
177         float *buf_L = 0, *buf_R = 0;
178
179         buf_L = _audio_system->output_buffer(0);
180         buf_R = _audio_system->output_buffer(1);
181
182         uint32_t srate = _audio_system->sample_rate();
183         float time_ratio = srate / _sample_rate / _stretch;
184
185         _stretcher->time_ratio( time_ratio );
186         _stretcher->pitch_scale( ::pow(2.0, double(_pitch)/12.0) * _sample_rate / srate );
187
188         uint32_t frame;
189         uint32_t reqd, gend, zeros, feed;
190
191         assert( _stretcher->is_running() );
192
193         // Determine how much data to push into the stretcher
194         int32_t write_space, written, input_frames;
195         write_space = _stretcher->available_write();
196         written = _stretcher->written();
197         if(written < _stretcher->feed_block_min()
198            && write_space >= _stretcher->feed_block_max() ) {
199             input_frames = _stretcher->feed_block_max();
200         } else {
201             input_frames = 0;
202         }
203
204         // Push data into the stretcher, observing A/B loop points
205         while( input_frames > 0 ) {
206             feed = input_frames;
207             if( looping() && ((_position + feed) >= _loop_b) ) {
208                 if( _position >= _loop_b ) {
209                     _position = _loop_a;
210                     if( _loop_a + feed > _loop_b ) {
211                         assert(_loop_b > _loop_a );
212                         feed = _loop_b - _loop_a;
213                     }
214                 } else {
215                     assert( _loop_b >= _position );
216                     feed = _loop_b - _position;
217                 }
218             }
219             if( _position + feed > _left.size() ) {
220                 feed = _left.size() - _position;
221                 input_frames = feed;
222             }
223             _stretcher->write_audio( &_left[_position], &_right[_position], feed );
224             _position += feed;
225             assert( input_frames >= feed );
226             input_frames -= feed;
227             if( looping() && _position >= _loop_b ) {
228                 _position = _loop_a;
229             }
230         }
231
232         // Pull generated data off the stretcher
233         uint32_t read_space;
234         read_space = _stretcher->available_read();
235
236         if( read_space >= nframes ) {
237             _stretcher->read_audio(buf_L, buf_R, nframes);
238         } else if ( (read_space > 0) && _hit_end ) {
239             _zero_buffers(nframes);
240             _stretcher->read_audio(buf_L, buf_R, read_space);
241         } else {
242             _zero_buffers(nframes);
243         }
244
245         // Update our estimation of the output position.
246         unsigned n_feed_buf = _stretcher->latency();
247         if(_position > n_feed_buf) {
248             _output_position = _position - n_feed_buf;
249         } else {
250             _output_position = 0;
251         }
252         assert( (_output_position > _position) ? (_output_position - _position) <= n_feed_buf : true );
253         assert( (_output_position < _position) ? (_position - _output_position) <= n_feed_buf : true );
254
255         // Apply gain... unroll loop manually so GCC will use SSE
256         if(nframes & 0xf) {  // nframes < 16
257             unsigned f = nframes;
258             while(f--) {
259                 (*buf_L++) *= _gain;
260                 (*buf_R++) *= _gain;
261             }
262         } else {
263             apply_gain_to_buffer(buf_L, nframes, _gain);
264             apply_gain_to_buffer(buf_R, nframes, _gain);
265         }
266
267         if(_position >= _left.size()) {
268             _hit_end = true;
269         }
270         if( (_hit_end == true) && (read_space == 0) ) {
271             _hit_end = false;
272             _playing = false;
273             _position = 0;
274             _stretcher->reset();
275         }
276
277         // Wake up, lazybones!
278         _stretcher->nudge();
279     }
280
281     /**
282      * Load a file
283      *
284      * \return Name of song
285      */
286     QString Engine::load_song(const QString& filename)
287     {
288         QMutexLocker lk(&_audio_lock);
289         stop();
290         _left.clear();
291         _right.clear();
292         _position = 0;
293         _output_position = 0;
294         _stretcher->reset();
295
296         SNDFILE *sf = 0;
297         SF_INFO sf_info;
298         memset(&sf_info, 0, sizeof(sf_info));
299
300         _message( QString("Opening file...") );
301         sf = sf_open(filename.toLocal8Bit().data(), SFM_READ, &sf_info);
302         if( !sf ) {
303             _error( QString("Error opening file '%1': %2")
304                     .arg(filename)
305                     .arg( sf_strerror(sf) ) );
306             return QString();
307         }
308
309         _sample_rate = sf_info.samplerate;
310         _left.reserve( sf_info.frames );
311         _right.reserve( sf_info.frames );
312
313         if(sf_info.frames == 0) {
314             _error( QString("Error opening file '%1': File is empty")
315                     .arg(filename) );
316             sf_close(sf);
317             return QString();
318         }
319
320         _message( QString("Opening file...") );
321         std::vector<float> buf(4096, 0.0f);
322         sf_count_t read, k;
323         unsigned mod;
324         while(true) {
325             read = sf_read_float(sf, &buf[0], buf.size());
326             if( read < 1 ) break;
327             for(k=0 ; k<read ; ++k) {
328                 mod = k % sf_info.channels;
329                 if( mod == 0 ) {
330                     _left.push_back( buf[k] );
331                 } else if( mod == 1 ) {
332                     _right.push_back( buf[k] );
333                 } else {
334                     // remaining channels ignored
335                 }
336             }
337         }
338
339         if( _left.size() != sf_info.frames ) {
340             _error( QString("Warning: not all of the file data was read.") );
341         }
342
343         sf_close(sf);
344         QFileInfo f_info(filename);
345         return f_info.fileName();
346     }
347
348     void Engine::play()
349     {
350         if( ! _playing ) {
351             _state_changed = true;
352             _playing = true;
353         }
354     }
355
356     void Engine::play_pause()
357     {
358         _playing = (_playing) ? false : true;
359         _state_changed = true;
360     }
361
362     void Engine::stop()
363     {
364         if( _playing ) {
365             _playing = false;
366             _state_changed = true;
367         }
368     }
369
370     float Engine::get_position()
371     {
372         if(_left.size() > 0) {
373             return float(_output_position) / _sample_rate;
374         }
375         return 0;
376     }
377
378     void Engine::loop_ab()
379     {
380         _loop_ab_pressed.fetchAndAddRelaxed(1);
381     }
382
383     void Engine::_handle_loop_ab()
384     {
385         while( _loop_ab_pressed > 0 ) {
386             uint32_t pos, lat;
387             uint32_t pressed_frame, seg_frame;
388
389             assert( _stretcher->time_ratio() > 0 );
390             pos = _output_position;
391
392             if(pos > lat) pos -= lat;
393
394             if( _loop_b > _loop_a ) {
395                 _loop_b = 0;
396                 _loop_a = 0;
397             } else if( _loop_a == 0 ) {
398                 _loop_a = pos;
399                 if(pos == 0) {
400                     _loop_a = 1;
401                 }
402             } else if( _loop_a != 0 ) {
403                 if( pos > _loop_a ) {
404                     _loop_b = pos;
405                 } else {
406                     _loop_a = pos;
407                 }
408             } else {
409                 assert(false);  // invalid state
410             }
411             _loop_ab_pressed.fetchAndAddOrdered(-1);
412         }
413     }
414
415     float Engine::get_length()
416     {
417         if(_left.size() > 0) {
418             return float(_left.size()) / _sample_rate;
419         }
420         return 0;
421     }
422
423     void Engine::locate(double secs)
424     {
425         unsigned long pos = secs * _sample_rate;
426         QMutexLocker lk(&_audio_lock);
427         _output_position = _position = pos;
428         _state_changed = true;
429         _stretcher->reset();
430     }
431
432     void Engine::_dispatch_message(const Engine::callback_seq_t& seq, const QString& msg) const
433     {
434         QMutexLocker lk(&_callback_lock);
435         Engine::callback_seq_t::const_iterator it;
436         for( it=seq.begin() ; it!=seq.end() ; ++it ) {
437             (**it)(msg);
438         }
439     }
440
441     void Engine::_subscribe_list(Engine::callback_seq_t& seq, EngineMessageCallback* obj)
442     {
443         if( obj == 0 ) return;
444         QMutexLocker lk(&_callback_lock);
445         obj->_parent = this;
446         seq.insert(obj);
447     }
448
449     void Engine::_unsubscribe_list(Engine::callback_seq_t& seq, EngineMessageCallback* obj)
450     {
451         if( obj == 0 ) return;
452         QMutexLocker lk(&_callback_lock);
453         obj->_parent = 0;
454         seq.erase(obj);
455     }
456
457     float Engine::get_cpu_load()
458     {
459         float audio_load, worker_load;
460
461         audio_load = _audio_system->dsp_load();
462         if(_playing) {
463             worker_load = _stretcher->cpu_load();
464         } else {
465             worker_load = 0.0;
466         }
467         return  audio_load + worker_load;
468     }
469
470     /* SIMD code for optimizing the gain application.
471      *
472      * Below is vectorized (SSE, SIMD) code for applying
473      * the gain.  If you enable >= SSE2 optimization, then
474      * this will calculate 4 floats at a time.  If you do
475      * not, then it will still work.
476      *
477      * This syntax is a GCC extension, but more portable
478      * than writing x86 assembly.
479      */
480
481     typedef float __vf4 __attribute__((vector_size(16)));
482     typedef union {
483         float f[4];
484         __vf4 v;
485     } vf4;
486
487     /**
488      * \brief Multiply each element in a buffer by a scalar.
489      *
490      * For each element in buf[0..nframes-1], buf[i] *= gain.
491      *
492      * This function detects the buffer alignment, and if it's 4-byte
493      * aligned and SSE optimization is enabled, it will use the
494      * optimized code-path.  However, it will still work with 1-byte
495      * aligned buffers.
496      *
497      * \param buf - Pointer to a buffer of floats.
498      */
499     static void apply_gain_to_buffer(float *buf, uint32_t nframes, float gain)
500     {
501         vf4* opt;
502         vf4 gg = {gain, gain, gain, gain};
503         int alignment;
504         unsigned ctr = nframes/4;
505
506         alignment = (((int)buf)%16);
507
508         switch(alignment) {
509         case 4: (*buf++) *= gain;
510         case 8: (*buf++) *= gain;
511         case 12:(*buf++) *= gain;
512             --ctr;
513         case 0:
514             break;
515         default:
516           goto LAME;
517         }
518
519         assert( (((int)buf)&0xf) == 0 );
520         opt = (vf4*) buf;
521         while(ctr--) {
522             opt->v *= gg.v; ++opt;
523         }
524
525         buf = (float*) opt;
526         switch(alignment) {
527         case 12: (*buf++) *= gain;
528         case 8:  (*buf++) *= gain;
529         case 4:  (*buf++) *= gain;
530         }
531
532         return;
533
534     LAME:
535         // If it's not even 4-byte aligned
536         // then this is is the un-optimized code.
537         while(nframes--) {
538           (*buf++) *= gain;
539         }
540     }
541
542 } // namespace StretchPlayer