1 /********************************************************************
2
3 Wav streamer rev3.
4 Code by N.A. Moseley
5 Copyright 2006-2007
6
7 Note: only stereo files using PCM or IEEE floats are supported!
8 supports 16,24,32 bit PCM and 32 bit float.
9 stereo only!!!
10 License: GPLv2
11
12 rev3: first working version
13 rev4: converted to unicode filenames & wxwidgets FileStream
14 ********************************************************************/
15
16 #include "wav_streamer.h"
17
18 #define TEMPBUFFERSIZE 65536
19
WavStreamer()20 WavStreamer::WavStreamer() : mutex(wxMUTEX_DEFAULT), wavefile(NULL), sample_index(0), temp_buffer(NULL)
21 {
22 }
23
~WavStreamer()24 WavStreamer::~WavStreamer()
25 {
26 if (wavefile!=NULL)
27 delete wavefile; // destructor automatically closes the file.
28 if (temp_buffer != NULL)
29 delete[] static_cast<char*>(temp_buffer);
30 }
31
OpenFile(const wxString & filename)32 int WavStreamer::OpenFile(const wxString &filename)
33 {
34 wxMutexLocker locker(mutex); // lock the mutex, unlocks automatically upon exit.
35
36 // try to open the file
37 wavefile = new wxFileInputStream(filename);
38 if (wavefile == NULL)
39 return -2;
40
41 // check for RIFF file & size
42 size_t bytes = wavefile->Read(chunk_type, 4).LastRead();
43 if ((bytes != 4) || (strncmp(chunk_type, "RIFF", 4)!=0))
44 {
45 delete wavefile;
46 wavefile = NULL;
47 return -1;
48 }
49 bytes = wavefile->Read(&chunk_size, 4).LastRead();
50 //bytes = fread(&chunk_size, 1, 4, wavefile);
51 if (bytes != 4)
52 {
53 delete wavefile;
54 wavefile = NULL;
55 return -1;
56 }
57 bytes = wavefile->Read(&chunk_type, 4).LastRead();
58 //bytes = fread(&chunk_type, 1, 4, wavefile); // not a chunk, but some other 4-letter type = 'WAVE'.
59 if ((bytes != 4) && (strncmp(chunk_type, "WAVE", 4)!=0))
60 {
61 delete wavefile;
62 wavefile = NULL;
63 return -1;
64 }
65
66 riff_size = chunk_size + 8; // size of RIFF chunk + 4-byte ID + 4-byte chunksize.
67 if (!FindChunk("fmt "))
68 {
69 // error, format chunk not found!
70 delete wavefile;
71 wavefile = NULL;
72 return -1;
73 }
74
75 // now, read the format chunk
76 bytes = wavefile->Read(&myformat, sizeof(WavFormatChunk)).LastRead();
77 //bytes = fread(&myformat, 1, sizeof(WavFormatChunk), wavefile);
78 if (myformat.wFormatTag == -2) // WAVE_FORMAT_EXTENSIBLE case..
79 {
80 myformat.wFormatTag = 1; // treat as PCM data, which should be the same when we have only 2 channels.
81 }
82
83 if (((myformat.wFormatTag != 3) && (myformat.wFormatTag != 1)) || (myformat.wChannels != 2))
84 {
85 delete wavefile;
86 wavefile = NULL;
87 return -1; // wrong format!
88 }
89 // formats smaller than 16 bits are not supported!
90 if (myformat.wBitsPerSample < 16)
91 {
92 delete wavefile;
93 wavefile = NULL;
94 return -1;
95 }
96
97 // if there are additional bytes in the format chunk, skip them.
98 // important in the WAVE_FORMAT_EXTENSIBLE case!
99 if (sizeof(WavFormatChunk) != chunk_size)
100 {
101 wavefile->SeekI(chunk_size - sizeof(WavFormatChunk), wxFromCurrent);
102 //fseek(wavefile, , SEEK_CUR);
103 }
104 // now, search for the audio data and set the file offset pointers
105 if (!FindChunk("data"))
106 {
107 delete wavefile;
108 wavefile = NULL;
109 return -1;
110 }
111 playback_start = wavefile->TellI();
112 playback_offset = playback_start;
113 playback_end = playback_start + chunk_size;
114
115 // allocate the correct temporary buffer.
116 if (temp_buffer!=NULL) delete[] static_cast<char*>(temp_buffer);
117 temp_buffer = static_cast<void*>(new char[myformat.wBitsPerSample*TEMPBUFFERSIZE*2/8]);
118
119 // don't forget to actually read the data
120 ReadRawData(TEMPBUFFERSIZE);
121 sample_index = 0;
122
123 my_filename = filename;
124 return 0;
125 }
126
FindChunk(const char ID[4])127 bool WavStreamer::FindChunk(const char ID[4])
128 {
129 chunk_size = 0;
130 // iterate until we get a format chunk..
131 while (strncmp(chunk_type, ID, 4)!=0)
132 {
133 // skip the size of the chunk (except for the first RIFF chunk!)
134 wavefile->SeekI(chunk_size, wxFromCurrent);
135
136 // read type of next chunk
137 size_t bytes = wavefile->Read(chunk_type, 4).LastRead();
138 if (bytes<=0) return false;
139 bytes = wavefile->Read(&chunk_size, 4).LastRead();
140 if (bytes<=0) return false;
141 }
142 return true;
143 }
144
ReadRawData(int RequestedSamples)145 int WavStreamer::ReadRawData(int RequestedSamples)
146 {
147 int bytes_per_sample;
148 switch(myformat.wBitsPerSample)
149 {
150 case 16:
151 bytes_per_sample = 2;
152 break;
153 case 24:
154 bytes_per_sample = 3;
155 break;
156 case 32:
157 bytes_per_sample = 4;
158 break;
159 default:
160 return -1; // error, wrong format!
161 }
162 int bytes_to_read = RequestedSamples * 2 * bytes_per_sample; // RequestedSamples is in 'stereo', so 2 real samples.
163 int offset = 0;
164 while(bytes_to_read>0)
165 {
166 if (bytes_to_read > (playback_end - playback_offset)) // check for wrap-around
167 {
168 int read_amount = (playback_end - playback_offset);
169 wavefile->Read(static_cast<char*>(temp_buffer) + offset, read_amount);
170 //fread(static_cast<char*>(temp_buffer) + offset, 1, read_amount, wavefile); //FIXME: handle errors
171 wavefile->SeekI(playback_start);
172 //fseek(wavefile, playback_start, SEEK_SET);
173 offset += read_amount;
174 playback_offset = playback_start;
175 bytes_to_read -= read_amount;
176 }
177 else
178 {
179 wavefile->Read(static_cast<char*>(temp_buffer) + offset, bytes_to_read);
180 //fread(static_cast<char*>(temp_buffer) + offset, 1, bytes_to_read, wavefile); //FIXME: handle errors
181 playback_offset += bytes_to_read;
182 bytes_to_read = 0;
183 }
184 }
185 sample_index = 0;
186 return RequestedSamples;
187 }
188
FillBuffer(float * stereo_buffer,int stereo_samples)189 void WavStreamer::FillBuffer(float *stereo_buffer, int stereo_samples)
190 {
191 wxMutexLocker locker(mutex); // lock the mutex, unlocks automatically upon exit.
192 if (wavefile==NULL)
193 {
194 // this is compatible with the IEEE 756 floating-point format!
195 memset(stereo_buffer, 0, sizeof(float)*2*stereo_samples);
196 return;
197 }
198 else
199 {
200 int mono_samples = stereo_samples * 2;
201 if (myformat.wFormatTag == 1) // PCM
202 {
203 switch(myformat.wBitsPerSample)
204 {
205 case 16:
206 {
207 short *ptr = static_cast<short*>(temp_buffer);
208 for(int i=0; i<mono_samples; i++)
209 {
210 if (sample_index > (TEMPBUFFERSIZE*2-1)) ReadRawData(TEMPBUFFERSIZE);
211 stereo_buffer[i] = static_cast<float>(ptr[sample_index]) / 32768.0f;
212 sample_index++;
213 }
214 }
215 break;
216 case 24:
217 {
218 signed char *ptr = static_cast<signed char*>(temp_buffer);
219 long v = 0;
220 int index = sample_index * 3;
221 for(int i=0; i<mono_samples; i++)
222 {
223 if (sample_index > (TEMPBUFFERSIZE*2-1))
224 {
225 ReadRawData(TEMPBUFFERSIZE);
226 index = 0;
227 }
228 v = static_cast<int>(ptr[index]) << 8;
229 v |= static_cast<int>(ptr[index+1]) << 16;
230 v |= static_cast<int>(ptr[index+2]) << 24;
231 index += 3;
232 stereo_buffer[i] = static_cast<float>(v) / 2147483648.0f;
233 sample_index++;
234 }
235 }
236 break;
237 case 32:
238 {
239 int *ptr = static_cast<int*>(temp_buffer);
240 for(int i=0; i<mono_samples; i++)
241 {
242 if (sample_index > (TEMPBUFFERSIZE*2-1)) ReadRawData(TEMPBUFFERSIZE);
243 stereo_buffer[i] = static_cast<float>(ptr[sample_index]) / 2147483648.0f;
244 sample_index++;
245 }
246 }
247 break;
248 }
249 }
250 else if (myformat.wFormatTag == 3)
251 {
252 // uncompressed float data
253 float *ptr = static_cast<float*>(temp_buffer);
254 for(int i=0; i<mono_samples; i++)
255 {
256 if (sample_index > (TEMPBUFFERSIZE*2-1)) ReadRawData(TEMPBUFFERSIZE);
257 stereo_buffer[i] = ptr[sample_index]; // FIXME: scaling??
258 sample_index++;
259 }
260 }
261 }
262 }
263
GetFormat(WavFormatChunk & output) const264 bool WavStreamer::GetFormat(WavFormatChunk &output) const
265 {
266 if (wavefile == NULL) return false;
267 output = myformat;
268 return true;
269 }
270