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