1 /*
2 * Copyright (C) 2002 - David W. Durham
3 *
4 * This file is part of ReZound, an audio editing application.
5 *
6 * ReZound is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * ReZound is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #include "ASoundPlayer.h"
22
23 #include <stdexcept>
24 #include <algorithm>
25
26 #include <math.h>
27
28 #include "CSound.h"
29 #include "CSoundPlayerChannel.h"
30
31 #include "settings.h"
32
33 #include "unit_conv.h"
34
ASoundPlayer()35 ASoundPlayer::ASoundPlayer()
36 {
37 #ifdef HAVE_FFTW
38 analyzerPlan=NULL;
39 #endif
40 }
41
~ASoundPlayer()42 ASoundPlayer::~ASoundPlayer()
43 {
44 #ifdef HAVE_FFTW
45 for(map<size_t,TAutoBuffer<fftw_real> *>::iterator i=hammingWindows.begin();i!=hammingWindows.end();i++)
46 delete i->second;
47 #endif
48 }
49
initialize()50 void ASoundPlayer::initialize()
51 {
52 for(unsigned t=0;t<MAX_CHANNELS;t++)
53 {
54 maxRMSLevels[t]=peakLevels[t]=0;
55 resetMaxRMSLevels[t]=resetPeakLevels[t]=false;
56 }
57 samplingForStereoPhaseMeters.setSize(gStereoPhaseMeterPointCount*devices[0].channelCount); // ??? only device zero
58
59 // only handling the first device ???
60 for(unsigned t=0;t<devices[0].channelCount;t++)
61 RMSLevelDetectors[t].setWindowTime(ms_to_samples(gMeterRMSWindowTime,devices[0].sampleRate));
62
63
64
65 #ifdef HAVE_FFTW
66 frequencyAnalysisBufferPrepared=false;
67 for(size_t t=0;t<ASP_ANALYSIS_BUFFER_SIZE;t++)
68 frequencyAnalysisBuffer[t]=0.0;
69
70 analyzerPlan = fftw_plan_r2r_1d(ASP_ANALYSIS_BUFFER_SIZE, frequencyAnalysisBuffer, data, FFTW_HC2R, FFTW_ESTIMATE);
71
72 // this causes calculateAnalyzerBandIndexRanges() to be called later when getting the analysis
73 bandLowerIndexes.clear();
74 bandUpperIndexes.clear();
75 #endif
76 }
77
deinitialize()78 void ASoundPlayer::deinitialize()
79 {
80 stopAll();
81
82 #ifdef HAVE_FFTW
83 if(analyzerPlan!=NULL)
84 {
85 fftw_destroy_plan(analyzerPlan);
86 analyzerPlan=NULL;
87 }
88 #endif
89 }
90
newSoundPlayerChannel(CSound * sound)91 CSoundPlayerChannel *ASoundPlayer::newSoundPlayerChannel(CSound *sound)
92 {
93 return(new CSoundPlayerChannel(this,sound));
94 }
95
addSoundPlayerChannel(CSoundPlayerChannel * soundPlayerChannel)96 void ASoundPlayer::addSoundPlayerChannel(CSoundPlayerChannel *soundPlayerChannel)
97 {
98 CMutexLocker ml(m);
99 if(!soundPlayerChannels.insert(soundPlayerChannel).second)
100 throw(runtime_error(string(__func__)+" -- sound player channel already in list"));
101 }
102
removeSoundPlayerChannel(CSoundPlayerChannel * soundPlayerChannel)103 void ASoundPlayer::removeSoundPlayerChannel(CSoundPlayerChannel *soundPlayerChannel)
104 {
105 CMutexLocker ml(m);
106
107 soundPlayerChannel->stop();
108
109 set<CSoundPlayerChannel *>::const_iterator i=soundPlayerChannels.find(soundPlayerChannel);
110 if(i!=soundPlayerChannels.end())
111 soundPlayerChannels.erase(i);
112 }
113
mixSoundPlayerChannels(const unsigned nChannels,sample_t * const buffer,const size_t bufferSize)114 void ASoundPlayer::mixSoundPlayerChannels(const unsigned nChannels,sample_t * const buffer,const size_t bufferSize)
115 {
116 memset(buffer,0,bufferSize*sizeof(*buffer)*nChannels);
117
118 // ??? it might be nice that if no sound player channel object is playing that this method would not return
119 // so that the caller wouldn't eat any CPU time doing anything with the silence returned
120
121 {
122 CMutexLocker ml(m);
123 for(set<CSoundPlayerChannel *>::iterator i=soundPlayerChannels.begin();i!=soundPlayerChannels.end();i++)
124 (*i)->mixOntoBuffer(nChannels,buffer,bufferSize);
125 }
126
127
128 // ??? could just schedule this to occur (by making a copy of the buffer) the next time getLevel or getAnalysis is called rather than doing it here in the callback for mixing audio
129 // calculate the peak levels and max RMS levels for this chunk
130 if(gLevelMetersEnabled)
131 {
132 for(unsigned i=0;i<nChannels;i++)
133 {
134 size_t p=i;
135 sample_t peak=resetPeakLevels[i] ? 0 : peakLevels[i];
136 resetPeakLevels[i]=false;
137 sample_t maxRMSLevel=resetMaxRMSLevels[i] ? 0 : maxRMSLevels[i];
138 resetMaxRMSLevels[i]=false;
139 for(size_t t=0;t<bufferSize;t++)
140 {
141 // m = max(m,abs(buffer[p]);
142 sample_t s=buffer[p];
143
144 s= s<0 ? -s : s; // s=abs(s)
145
146 // peak=max(peak,s)
147 if(peak<s)
148 peak=s;
149
150 // update the RMS level detectors
151 sample_t RMSLevel=RMSLevelDetectors[i].readLevel(s);
152
153 // RMSLevel=max(maxRMSLevel,RMSLevel)
154 if(maxRMSLevel<RMSLevel)
155 maxRMSLevel=RMSLevel;
156
157 p+=nChannels;
158 }
159
160 maxRMSLevels[i]=maxRMSLevel;
161 peakLevels[i]=peak;
162 }
163
164 // set peak levels to zero for values above the current number of channels
165 for(unsigned i=nChannels;i<MAX_CHANNELS;i++)
166 peakLevels[i]=0;
167 }
168
169 if(gStereoPhaseMetersEnabled)
170 samplingForStereoPhaseMeters.write(buffer,bufferSize*nChannels, false); // NOTE: nChannels is supposed to equal devices[0].channelCount
171
172 #ifdef HAVE_FFTW
173 if(gFrequencyAnalyzerEnabled)
174 {
175 CMutexLocker l(frequencyAnalysisBufferMutex);
176
177 // put data into the frequency analysis buffer
178 const size_t copyAmount=min((size_t)ASP_ANALYSIS_BUFFER_SIZE,(size_t)bufferSize); // only copy part of the output buffer (whichever is smaller)
179
180 // mix all the output channels onto the buffer (copying the first channel and adding the others
181 size_t q=0;
182 for(size_t t=0;t<copyAmount;t++)
183 {
184 frequencyAnalysisBuffer[t]=buffer[q];
185 q+=nChannels;
186 }
187 for(size_t i=1;i<nChannels;i++)
188 {
189 q=i;
190 for(size_t t=0;t<copyAmount;t++)
191 {
192 frequencyAnalysisBuffer[t]+=buffer[q];
193 q+=nChannels;
194 }
195 }
196
197 // I would normalize and apply the Hamming window here, but I'll defer that until the call to getFrequencyAnalysis()
198 frequencyAnalysisBufferPrepared=false;
199 frequencyAnalysisBufferLength=copyAmount;
200 }
201 #endif
202
203 }
204
stopAll()205 void ASoundPlayer::stopAll()
206 {
207 CMutexLocker ml(m);
208 for(set<CSoundPlayerChannel *>::iterator i=soundPlayerChannels.begin();i!=soundPlayerChannels.end();i++)
209 (*i)->stop();
210 }
211
getRMSLevel(unsigned channel) const212 const sample_t ASoundPlayer::getRMSLevel(unsigned channel) const
213 {
214 if(!isInitialized())
215 return 0;
216
217 //return RMSLevelDetectors[channel].readCurrentLevel();
218 const sample_t r=maxRMSLevels[channel];
219 resetMaxRMSLevels[channel]=true;
220 return r;
221 }
222
getPeakLevel(unsigned channel) const223 const sample_t ASoundPlayer::getPeakLevel(unsigned channel) const
224 {
225 if(!isInitialized())
226 return 0;
227
228 const sample_t p=peakLevels[channel];
229 resetPeakLevels[channel]=true;
230 return p;
231 }
232
233
getSamplingForStereoPhaseMeters(sample_t * buffer,size_t bufferSizeInSamples) const234 const size_t ASoundPlayer::getSamplingForStereoPhaseMeters(sample_t *buffer,size_t bufferSizeInSamples) const
235 {
236 return samplingForStereoPhaseMeters.read(buffer,min(bufferSizeInSamples,(size_t)(gStereoPhaseMeterPointCount*devices[0].channelCount)), false)/devices[0].channelCount; // ??? only device zero
237 }
238
239 #ifdef HAVE_FFTW
240
241 // NOTE: when I say 'band', a band is expressed in terms of an
242 // octave (where octave is a real number) and each band is a
243 // multiple of deltaOctave (i.e when deltaOctave is 0.5, then
244 // the bands are defined as octaves: 0, 0.5, 1.0, 1.5, 2.0, 2.5, etc)
245
246 // ??? these need to be settings in the registry and have enforced limits
247 static const float baseOctave=40; // bottom frequency of analyzer (actually the first band contains from 0Hz to upperFreqAtOctave(0) )
248 static const size_t octaveStride=6; // 6 bands per octave
249 static const float deltaOctave=1.0f/octaveStride;
250
251 // returns the frequency (in Hz) given the octave
freqAtOctave(float octave)252 static float freqAtOctave(float octave)
253 {
254 return baseOctave*powf(2.0f,octave);
255 }
256
257 // return middle of the previous band's frequency and our band's frequency
lowerFreqAtOctave(float octave)258 static float lowerFreqAtOctave(float octave)
259 {
260 return (freqAtOctave(octave-deltaOctave)+freqAtOctave(octave))/2.0f;
261 }
262
263 // return middle of the our band's frequency and the next band's frequency
upperFreqAtOctave(float octave)264 static float upperFreqAtOctave(float octave)
265 {
266 return (freqAtOctave(octave)+freqAtOctave(octave+deltaOctave))/2.0f;
267 }
268
269 // returns the index (into an frequency domain array) given a frequency (but doesn't always return an integer, it returns what index we would wish to be there (perhaps between two elements))
indexAtFreq(float freq,unsigned sampleRate)270 static float indexAtFreq(float freq,unsigned sampleRate)
271 {
272 return (2.0f*(ASP_ANALYSIS_BUFFER_SIZE/2)*freq)/(float)sampleRate;
273 }
274
275 // returns the (integer) lower index of the given band (expressed as an octave) into a frequency domain array
lowerIndexAtOctave(float octave,unsigned sampleRate)276 static size_t lowerIndexAtOctave(float octave,unsigned sampleRate)
277 {
278 return (size_t)floor(indexAtFreq(lowerFreqAtOctave(octave),sampleRate));
279 }
280
281 // returns the (integer) upper index of the given band (expressed as an octave) into a frequency domain array
upperIndexAtOctave(float octave,unsigned sampleRate)282 static size_t upperIndexAtOctave(float octave,unsigned sampleRate)
283 {
284 return (size_t)(floor(indexAtFreq(upperFreqAtOctave(octave),sampleRate)));
285 }
286
calculateAnalyzerBandIndexRanges() const287 void ASoundPlayer::calculateAnalyzerBandIndexRanges() const
288 {
289 bandLowerIndexes.clear();
290 bandUpperIndexes.clear();
291
292 float octave=0;
293 while(freqAtOctave(octave)<(devices[0].sampleRate/2))
294 {
295 bandLowerIndexes.push_back(lowerIndexAtOctave(octave,devices[0].sampleRate));
296 bandUpperIndexes.push_back(upperIndexAtOctave(octave,devices[0].sampleRate));
297
298 octave+=deltaOctave;
299 }
300 if(bandLowerIndexes.size()>0)
301 {
302 // make sure the first band includes the first element (but not actually 0Hz because that's just the DC offset)
303 bandLowerIndexes[0]=1;
304
305 // make sure all the indexes are in range
306 for(size_t t=0;t<bandUpperIndexes.size();t++)
307 {
308 bandLowerIndexes[t]=max((int)1,min((int)ASP_ANALYSIS_BUFFER_SIZE/2,(int)bandLowerIndexes[t]));
309 bandUpperIndexes[t]=max((int)1,min((int)ASP_ANALYSIS_BUFFER_SIZE/2,(int)bandUpperIndexes[t]));
310 }
311 }
312 else
313 {
314 // shouldn't happen, but if it does, do this to avoid calling this method over and over by adding soemthing to the vectors
315 bandLowerIndexes.push_back(0);
316 bandUpperIndexes.push_back(0);
317 }
318
319 /*
320 for(size_t t=0;t<bandLowerIndexes.size();t++)
321 printf("%d .. %d\n",bandLowerIndexes[t],bandUpperIndexes[t]);
322 */
323 }
324
createHammingWindow(size_t windowSize)325 TAutoBuffer<fftw_real> *ASoundPlayer::createHammingWindow(size_t windowSize)
326 {
327 //printf("creating for length %d\n",windowSize);
328 TAutoBuffer<fftw_real> *h=new TAutoBuffer<fftw_real>(windowSize, true);
329
330 for(size_t t=0;t<windowSize;t++)
331 (*h)[t]=0.54-0.46*cos(2.0*M_PI*t/windowSize);
332
333 return h;
334 }
335
336 #endif
337
338
339 #include <stdlib.h>
getFrequencyAnalysis() const340 const vector<float> ASoundPlayer::getFrequencyAnalysis() const
341 {
342 vector<float> v;
343
344 if(!isInitialized())
345 return v;
346
347 #ifdef HAVE_FFTW
348 CMutexLocker l(frequencyAnalysisBufferMutex);
349
350 if(!frequencyAnalysisBufferPrepared)
351 {
352 // now divide by MAX_SAMPLE to normalize the values
353 // and multiply by the Hamming window
354 const fftw_real k=1.0/MAX_SAMPLE;
355
356 if(hammingWindows.find(frequencyAnalysisBufferLength)==hammingWindows.end())
357 hammingWindows[frequencyAnalysisBufferLength]=createHammingWindow(frequencyAnalysisBufferLength);
358 const fftw_real *hammingWindow=*(hammingWindows[frequencyAnalysisBufferLength]);
359
360 for(size_t t=0;t<frequencyAnalysisBufferLength;t++)
361 frequencyAnalysisBuffer[t]*=k*hammingWindow[t];
362
363
364 // in case bufferSize was less than ASP_ANALYSIS_BUFFER_SIZE pad the data with zeros
365 for(size_t t=frequencyAnalysisBufferLength;t<ASP_ANALYSIS_BUFFER_SIZE;t++)
366 frequencyAnalysisBuffer[t]=0.0;
367
368 frequencyAnalysisBufferPrepared=true;
369 }
370
371 /*
372 for(size_t t=0;t<ASP_ANALYSIS_BUFFER_SIZE;t++)
373 ((fftw_real *)frequencyAnalysisBuffer)[t]=((float)(rand()*2.0)/RAND_MAX)-1.0;
374 */
375
376 if(bandLowerIndexes.size()<=0) // calculate the index ranges for each band in the returned analysis since we haven't done it yet (I would do it on initialization, but I might not know the sampling rate at that point)
377 calculateAnalyzerBandIndexRanges();
378
379
380 /*
381 for(int t=0;t<ASP_ANALYSIS_BUFFER_SIZE;t++)
382 {
383 frequencyAnalysisBuffer[t]=sin(5.0*(t*M_PI*2.0/(ASP_ANALYSIS_BUFFER_SIZE)));
384 frequencyAnalysisBuffer[t]+=0.5*sin(15.0*(t*M_PI*2.0/(ASP_ANALYSIS_BUFFER_SIZE)));
385 }
386 */
387
388 fftw_execute(analyzerPlan);
389
390 for(size_t t=0;t<bandLowerIndexes.size();t++)
391 {
392 const size_t l=bandLowerIndexes[t];
393 const size_t u=bandUpperIndexes[t];
394
395 float sum=0;
396 for(size_t i=l;i<u;i++)
397 {
398 const fftw_real re=data[i];
399 const fftw_real im= (i==ASP_ANALYSIS_BUFFER_SIZE/2) ? 0 : data[ASP_ANALYSIS_BUFFER_SIZE-i];
400
401 const fftw_real power=sqrt(re*re+im*im)/(ASP_ANALYSIS_BUFFER_SIZE/2);
402
403 //sum+=power*power;
404
405 // find max magnitude of a frequency within band i
406 if(sum<power)
407 sum=power;
408 }
409 //sum/=(u-l);
410 //sum=sqrt(sum);
411 //v.push_back(sum);
412
413 v.push_back(sum);
414 }
415
416 #endif
417
418
419 #if 1
420 // this enables the returned values to be averaged with the previous NUM_AVG-1 analysies
421 #define NUM_AVG 2
422 static vector<float> prevVs[NUM_AVG];
423 static size_t currentPrevV=0;
424
425 // overwrite the oldest prev vector
426 prevVs[currentPrevV]=v;
427
428 currentPrevV++;
429 currentPrevV%=NUM_AVG;
430
431 vector<float> temp;
432
433 if(prevVs[NUM_AVG-1].size()>0)
434 {
435 // create initial values in temp
436 temp=prevVs[0];
437
438 // add all over prev vectors to temp
439 for(size_t i=1;i<NUM_AVG;i++)
440 {
441 for(size_t t=0;t<v.size();t++)
442 temp[t]+=prevVs[i][t];
443 }
444
445 // divide by number of sums for an average
446 for(size_t t=0;t<v.size();t++)
447 temp[t]/=NUM_AVG;
448 }
449
450 return temp;
451 #else
452 return v;
453 #endif
454 }
455
getFrequency(size_t index) const456 const size_t ASoundPlayer::getFrequency(size_t index) const
457 {
458 #ifdef HAVE_FFTW
459 return (size_t)freqAtOctave((float)index/(float)octaveStride);
460 #else
461 return 0;
462 #endif
463 }
464
getFrequencyAnalysisOctaveStride() const465 const size_t ASoundPlayer::getFrequencyAnalysisOctaveStride() const
466 {
467 #ifdef HAVE_FFTW
468 return octaveStride;
469 #else
470 return 1;
471 #endif
472 }
473
474
475 // ----------------------------
476
477 #include <stdio.h> // just for fprintf
478 #include <vector>
479 #include <string>
480
481 #include "CNULLSoundPlayer.h"
482 #include "COSSSoundPlayer.h"
483 #include "CALSASoundPlayer.h"
484 #include "CPortAudioSoundPlayer.h"
485 #include "CJACKSoundPlayer.h"
486 #include "CPulseSoundPlayer.h"
487
488 #include "AStatusComm.h"
489
490 #include <CNestedDataFile/CNestedDataFile.h>
491
createInitializedSoundPlayer()492 ASoundPlayer *ASoundPlayer::createInitializedSoundPlayer()
493 {
494 // if the registry doesn't already contain a methods setting, then create the default one
495 if(gSettingsRegistry->keyExists("AudioOutputMethods")!=CNestedDataFile::ktValue)
496 {
497 vector<string> methods;
498 methods.push_back("pulse");
499 methods.push_back("oss");
500 methods.push_back("alsa");
501 methods.push_back("jack");
502 methods.push_back("portaudio");
503 gSettingsRegistry->setValue("AudioOutputMethods",methods);
504 }
505
506
507 bool initializeThrewException=false;
508 ASoundPlayer *soundPlayer=NULL;
509
510 vector<string> methods=gSettingsRegistry->getValue<vector<string> >("AudioOutputMethods");
511
512 // add --audio-method=... to the beginning
513 if(gDefaultAudioMethod!="")
514 methods.insert(methods.begin(),gDefaultAudioMethod);
515
516 // try this as a last resort (it just holds the pointer place (so it's not NULL throughout the rest of the code) but it is written to fail to initialize
517 methods.push_back("null");
518
519 // for each requested method in registry.AudioOutputMethods try each until one succeeds
520 // 'suceeding' is true if the method was enabled at build time and it can initialize now at run-time
521 for(size_t t=0;t<methods.size();t++)
522 {
523 const string method=methods[t];
524 try
525 {
526 #define INITIALIZE_PLAYER(ClassName) \
527 { \
528 initializeThrewException=false; \
529 delete soundPlayer; \
530 soundPlayer=new ClassName(); \
531 soundPlayer->initialize(); \
532 break; /* no exception thrown from initialize() so we're good to go */ \
533 }
534
535 if(method=="oss")
536 {
537 #ifdef ENABLE_OSS
538 INITIALIZE_PLAYER(COSSSoundPlayer)
539 #endif
540 }
541 else if(method=="alsa")
542 {
543 #ifdef ENABLE_ALSA
544 INITIALIZE_PLAYER(CALSASoundPlayer)
545 #endif
546 }
547 else if(method=="jack")
548 {
549 #ifdef ENABLE_JACK
550 INITIALIZE_PLAYER(CJACKSoundPlayer)
551 #endif
552 }
553 else if(method=="portaudio")
554 {
555 #ifdef ENABLE_PORTAUDIO
556 INITIALIZE_PLAYER(CPortAudioSoundPlayer)
557 #endif
558 }
559 else if(method=="pulse")
560 {
561 #ifdef ENABLE_PULSE
562 INITIALIZE_PLAYER(CPulseSoundPlayer)
563 #endif
564 }
565 else if(method=="null")
566 {
567 INITIALIZE_PLAYER(CNULLSoundPlayer)
568 }
569 else
570 {
571 Warning("unhandled method type in the registry:AudioOutputMethods[] '"+method+"'");
572 continue;
573 }
574 }
575 catch(exception &e)
576 { // now really give up
577 fprintf(stderr,"Error occurred while initializing audio output method '%s' -- %s\n",method.c_str(),e.what());
578 initializeThrewException=true;
579 }
580 }
581
582 if(soundPlayer)
583 {
584 if(initializeThrewException)
585 Error(_("No audio output method could be initialized -- Playing will be disabled."));
586
587 return soundPlayer;
588 }
589 else /* ??? this should never happen anymore now with CNULLSoundPlayer */
590 throw runtime_error(string(__func__)+" -- "+_("Either no audio output method was enabled at configure-time, or no method was recognized in the registry:AudioOutputMethods[] setting"));
591 }
592
593