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 #ifndef __ASoundPlayer_H__ 22 #define __ASoundPlayer_H__ 23 24 #include "../../config/common.h" 25 26 class ASoundPlayer; 27 28 #include <set> 29 30 #include <vector> 31 32 #ifdef HAVE_FFTW 33 #include <map> 34 #include <fftw3.h> 35 #include <CMutex.h> 36 #include <TAutoBuffer.h> 37 38 typedef double fftw_real; 39 40 #endif 41 42 43 /* 44 - This class should be derived from to create the actual player for a given 45 operating system platform. 46 47 - This class is not thread-safe, methods from two independent threads should 48 not call common methods for an ASoundPlayer object without a locking mechanism 49 50 - if deinitialize is overridden, then it should not raise an exception if 51 the player is not initialized. 52 53 - overriding initialize and deinitialize methods should invoke the overridden method 54 - this class's initialize() method should be called AFTER the derived class 55 has finished its initialization 56 57 - the derived class's destructor needs to call deinitialize because if this base 58 class did it, some stuff could be freed in the derived class before the base 59 class's destructor ran 60 61 - To play sounds, call createChannel to get a CSoundPlayerChannel object that 62 you can use to play, stop, seek ... 63 */ 64 65 #include "CSound_defs.h" 66 class CSoundPlayerChannel; 67 68 #include "DSP/LevelDetector.h" 69 #include "../misc/TMemoryPipe.h" 70 71 #define MAX_OUTPUT_DEVICES 16 72 class ASoundPlayer 73 { 74 public: 75 struct RDeviceParams 76 { 77 unsigned channelCount; 78 unsigned sampleRate; 79 80 RDeviceParams() : channelCount(1),sampleRate(44100) { } 81 } devices[MAX_OUTPUT_DEVICES]; 82 83 virtual ~ASoundPlayer(); 84 85 static ASoundPlayer *createInitializedSoundPlayer(); 86 87 virtual void initialize(); 88 virtual void deinitialize(); 89 virtual bool isInitialized() const=0; 90 91 void stopAll(); 92 93 /* 94 * These need to be implemented to know if the output device(s) 95 * support full duplex mode or not. If not, then when ReZound 96 * is about to record this method should call killAll() and 97 * deinitialize the device(s). When doneRecording() is called 98 * then the device(s) should be reinitialized, but the playing 99 * state of each channel does not have to be restored. 100 * 101 * It is the responsbility of the caller of aboutToRecord() to 102 * make sure that doneRecording() is called after recording has 103 * ended or an error has occurred. Otherwise, the audio output 104 * would never be restored. 105 * 106 * If these are called while the sound player is not initialized 107 * the results are undefined. 108 */ 109 virtual void aboutToRecord()=0; 110 virtual void doneRecording()=0; 111 112 CSoundPlayerChannel *newSoundPlayerChannel(CSound *sound); 113 114 115 // gets the max RMS level since the last call to this method for the same channel (hence two unrelated sub-systems could not currently use this method at the same time since they would interfere with each other's last-time-this-method-was-called) 116 const sample_t getRMSLevel(unsigned channel) const; 117 118 // gets the maximum peak level since the last call to this method for the same channel (hence two unrelated sub-systems could not currently use this method at the same time since they would interfere with each other's last-time-this-method-was-called) 119 const sample_t getPeakLevel(unsigned channel) const; 120 121 // gets a sampling of the data that has been played back since the last time this method was called 122 // At most bufferSizeInSamples samples (not frames) of data get copied into the buffer (this is in case the number of channels (not supposed to) increases in the ASoundPlayer object) 123 // The number of channels in each frame is devices[0].channelCount // ??? only device zero 124 // The data will be sample frames where each frame contains nChannels of data 125 // The reason for returning all channels in the same buffer is to ensure that the phase difference can be accurately determined (because asking for data at two different times (once for the left channel and again for the right) might return data represented from two different points in playback time) 126 // The number of sample FRAMES is returned 127 const size_t getSamplingForStereoPhaseMeters(sample_t *buffer,size_t bufferSizeInSamples) const; 128 129 // Returns the frequency analysis of the most recent buffer played (if ReZound was configured and found fftw installed) 130 // It returns a result that is not useful for careful analysis because for 131 // the CPU's sake it only does analysis on part of the buffer and doesn't 132 // make any effort to track results from buffers that were processed between 133 // calls to this function. 134 // The result is a vector of float with values 0 to 1 135 // The frequency band of each element is: 136 // f(i)=20*2^(i/2) 137 // which gives each octave at i=0,2,4,6,8... and an band within the octave at 1,3,5,7... 138 // So the elements would measure 20Hz, ~30Hz, 40Hz, ~60Hz, 80Hz, ~120Hz, 160Hz, ~240Hz, 320Hz, ~480Hz, 640Hz, ... 139 // The number of elements will be according to the sample rate of the output device since the 140 // higher the sample rate the higher the frequency that can be represented in the data. The elements 141 // will represent as many bands as will fit from 20Hz up to half the sample rate, not going over. 142 // the labels on the bands corrispond to its being the center frequency in the band (except perhaps the first and last band) 143 const vector<float> getFrequencyAnalysis() const; 144 const size_t getFrequency(size_t index) const; // returns the frequency of the value at the index within the vector return from getFrequencyAnalysis 145 const size_t getFrequencyAnalysisOctaveStride() const; // return the number of bands per octave returned by getFrequencyAnalysis 146 147 protected: 148 149 ASoundPlayer(); 150 151 // bufferSize is in sample frames (NOT BYTES) 152 void mixSoundPlayerChannels(const unsigned nChannels,sample_t * const buffer,const size_t bufferSize); 153 154 private: 155 156 friend class CSoundPlayerChannel; 157 158 CMutex m; // protects soundPlayerChannels 159 set<CSoundPlayerChannel *> soundPlayerChannels; // ??? might as well be a vector 160 void addSoundPlayerChannel(CSoundPlayerChannel *soundPlayerChannel); 161 void removeSoundPlayerChannel(CSoundPlayerChannel *soundPlayerChannel); 162 163 164 CDSPRMSLevelDetector RMSLevelDetectors[MAX_CHANNELS]; 165 166 mutable sample_t maxRMSLevels[MAX_CHANNELS]; 167 mutable bool resetMaxRMSLevels[MAX_CHANNELS]; // a bool that is flagged if the next buffer processed should start with a new max or max with the current one (since it hasn't been obtained from the get method yet) 168 169 mutable sample_t peakLevels[MAX_CHANNELS]; 170 mutable bool resetPeakLevels[MAX_CHANNELS]; // a bool that is flagged if the next buffer processed should start with a new max or max with the current one (since it hasn't been obtained from the get method yet) 171 172 #ifdef HAVE_FFTW 173 #define ASP_ANALYSIS_BUFFER_SIZE 8192 174 mutable CMutex frequencyAnalysisBufferMutex; 175 mutable bool frequencyAnalysisBufferPrepared; 176 mutable fftw_real frequencyAnalysisBuffer[ASP_ANALYSIS_BUFFER_SIZE]; 177 size_t frequencyAnalysisBufferLength; // the amount of data that mixSoundPlayerChannels copied into the buffer 178 mutable map<size_t,TAutoBuffer<fftw_real> *> hammingWindows; // create and save Hamming windows for any length needed 179 fftw_plan analyzerPlan; 180 fftw_real data[ASP_ANALYSIS_BUFFER_SIZE]; 181 mutable vector<size_t> bandLowerIndexes; // mutable because calculateAnalyzerBandIndexRanges is called from getFrequencyAnalysis 182 mutable vector<size_t> bandUpperIndexes; 183 184 void calculateAnalyzerBandIndexRanges() const; // const because it is called from getFrequencyAnalysis 185 static TAutoBuffer<fftw_real> *createHammingWindow(size_t windowSize); 186 #endif 187 188 mutable TMemoryPipe<sample_t> samplingForStereoPhaseMeters; 189 }; 190 191 192 193 #endif 194