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