diff options
-rw-r--r-- | src/audio/player.c | 215 |
1 files changed, 161 insertions, 54 deletions
diff --git a/src/audio/player.c b/src/audio/player.c index 1dd2ebfc..f1d6a2c8 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -28,6 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
28 | 28 | ||
29 | iDeclareType(InputBuf) | 29 | iDeclareType(InputBuf) |
30 | 30 | ||
31 | #if !defined (AUDIO_S24LSB) | ||
32 | # define AUDIO_S24LSB 0x8018 /* 24-bit integer samples */ | ||
33 | #endif | ||
34 | #if !defined (AUDIO_F64LSB) | ||
35 | # define AUDIO_F64LSB 0x8140 /* 64-bit floating point samples */ | ||
36 | #endif | ||
37 | |||
31 | struct Impl_InputBuf { | 38 | struct Impl_InputBuf { |
32 | iMutex mtx; | 39 | iMutex mtx; |
33 | iCondition changed; | 40 | iCondition changed; |
@@ -59,25 +66,28 @@ iDefineTypeConstruction(InputBuf) | |||
59 | iDeclareType(SampleBuf) | 66 | iDeclareType(SampleBuf) |
60 | 67 | ||
61 | struct Impl_SampleBuf { | 68 | struct Impl_SampleBuf { |
62 | void * data; | 69 | SDL_AudioFormat format; |
63 | uint8_t numBits; | 70 | uint8_t numChannels; |
64 | uint8_t numChannels; | 71 | uint8_t sampleSize; /* as bytes; one sample includes values for all channels */ |
65 | uint8_t sampleSize; /* bytes; one sample includes values for all channels */ | 72 | void * data; |
66 | size_t count; | 73 | size_t count; |
67 | size_t head, tail; | 74 | size_t head, tail; |
75 | iCondition moreNeeded; | ||
68 | }; | 76 | }; |
69 | 77 | ||
70 | void init_SampleBuf(iSampleBuf *d, size_t numChannels, size_t sampleSize, size_t count) { | 78 | void init_SampleBuf(iSampleBuf *d, SDL_AudioFormat format, size_t numChannels, size_t count) { |
79 | d->format = format; | ||
71 | d->numChannels = numChannels; | 80 | d->numChannels = numChannels; |
72 | d->sampleSize = sampleSize; | 81 | d->sampleSize = SDL_AUDIO_BITSIZE(format) / 8 * numChannels; |
73 | d->numBits = sampleSize / numChannels * 8; | ||
74 | d->count = count + 1; /* considered empty if head==tail */ | 82 | d->count = count + 1; /* considered empty if head==tail */ |
75 | d->data = malloc(d->sampleSize * d->count); | 83 | d->data = malloc(d->sampleSize * d->count); |
76 | d->head = 0; | 84 | d->head = 0; |
77 | d->tail = 0; | 85 | d->tail = 0; |
86 | init_Condition(&d->moreNeeded); | ||
78 | } | 87 | } |
79 | 88 | ||
80 | void deinit_SampleBuf(iSampleBuf *d) { | 89 | void deinit_SampleBuf(iSampleBuf *d) { |
90 | deinit_Condition(&d->moreNeeded); | ||
81 | free(d->data); | 91 | free(d->data); |
82 | } | 92 | } |
83 | 93 | ||
@@ -134,8 +144,9 @@ void read_SampleBuf(iSampleBuf *d, const size_t n, void *samples_out) { | |||
134 | iDeclareType(ContentSpec) | 144 | iDeclareType(ContentSpec) |
135 | 145 | ||
136 | struct Impl_ContentSpec { | 146 | struct Impl_ContentSpec { |
137 | SDL_AudioSpec spec; | 147 | SDL_AudioFormat inputFormat; |
138 | iRanges wavData; | 148 | SDL_AudioSpec output; |
149 | iRanges wavData; | ||
139 | }; | 150 | }; |
140 | 151 | ||
141 | iDeclareType(Decoder) | 152 | iDeclareType(Decoder) |
@@ -152,6 +163,7 @@ struct Impl_Decoder { | |||
152 | enum iDecoderType type; | 163 | enum iDecoderType type; |
153 | float gain; | 164 | float gain; |
154 | iThread * thread; | 165 | iThread * thread; |
166 | SDL_AudioFormat inputFormat; | ||
155 | iInputBuf * input; | 167 | iInputBuf * input; |
156 | size_t inputPos; | 168 | size_t inputPos; |
157 | iSampleBuf output; | 169 | iSampleBuf output; |
@@ -159,70 +171,140 @@ struct Impl_Decoder { | |||
159 | iRanges wavData; | 171 | iRanges wavData; |
160 | }; | 172 | }; |
161 | 173 | ||
162 | static void parseWav_Decoder_(iDecoder *d, iRanges inputRange) { | 174 | enum iDecoderParseStatus { |
163 | const size_t sampleSize = d->output.sampleSize; | 175 | ok_DecoderParseStatus, |
164 | const size_t vacancy = vacancy_SampleBuf(&d->output); | 176 | needMoreInput_DecoderParseStatus, |
165 | const size_t avail = iMin(inputRange.end - d->inputPos, d->wavData.end - d->inputPos) / | 177 | }; |
166 | sampleSize; | 178 | |
179 | static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRange) { | ||
180 | const uint8_t numChannels = d->output.numChannels; | ||
181 | const size_t inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8; | ||
182 | const size_t vacancy = vacancy_SampleBuf(&d->output); | ||
183 | const size_t inputBytePos = inputSampleSize * d->inputPos; | ||
184 | const size_t avail = | ||
185 | iMin(inputRange.end - inputBytePos, d->wavData.end - inputBytePos) / inputSampleSize; | ||
186 | if (avail == 0) { | ||
187 | return needMoreInput_DecoderParseStatus; | ||
188 | } | ||
167 | const size_t n = iMin(vacancy, avail); | 189 | const size_t n = iMin(vacancy, avail); |
168 | if (n == 0) return; | 190 | if (n == 0) { |
169 | void *samples = malloc(sampleSize * n); | 191 | return ok_DecoderParseStatus; |
192 | } | ||
193 | void *samples = malloc(inputSampleSize * n); | ||
170 | /* Get a copy of the input for mixing. */ { | 194 | /* Get a copy of the input for mixing. */ { |
171 | lock_Mutex(&d->input->mtx); | 195 | lock_Mutex(&d->input->mtx); |
172 | memcpy( | 196 | iAssert(inputSampleSize * d->inputPos < size_Block(&d->input->data)); |
173 | samples, constData_Block(&d->input->data) + sampleSize * d->inputPos, sampleSize * n); | 197 | memcpy(samples, |
198 | constData_Block(&d->input->data) + inputSampleSize * d->inputPos, | ||
199 | inputSampleSize * n); | ||
174 | d->inputPos += n; | 200 | d->inputPos += n; |
175 | unlock_Mutex(&d->input->mtx); | 201 | unlock_Mutex(&d->input->mtx); |
176 | } | 202 | } |
177 | /* Gain. */ { | 203 | /* Gain. */ { |
178 | const float gain = d->gain; | 204 | const float gain = d->gain; |
179 | if (d->output.numBits == 16) { | 205 | if (d->inputFormat == AUDIO_F64LSB) { |
180 | int16_t *value = samples; | 206 | iAssert(d->output.format == AUDIO_F32); |
181 | for (size_t count = d->output.numChannels * n; count; count--, value++) { | 207 | double *inValue = samples; |
208 | float * outValue = samples; | ||
209 | for (size_t count = numChannels * n; count; count--) { | ||
210 | *outValue++ = gain * *inValue++; | ||
211 | } | ||
212 | } | ||
213 | else if (d->inputFormat == AUDIO_F32) { | ||
214 | float *value = samples; | ||
215 | for (size_t count = numChannels * n; count; count--, value++) { | ||
182 | *value *= gain; | 216 | *value *= gain; |
183 | } | 217 | } |
184 | } | 218 | } |
219 | else if (d->inputFormat == AUDIO_S24LSB) { | ||
220 | iAssert(d->output.format == AUDIO_S16); | ||
221 | const char *inValue = samples; | ||
222 | int16_t * outValue = samples; | ||
223 | for (size_t count = numChannels * n; count; count--, inValue += 3, outValue++) { | ||
224 | memcpy(outValue, inValue, 2); | ||
225 | *outValue *= gain; | ||
226 | } | ||
227 | } | ||
228 | else { | ||
229 | switch (SDL_AUDIO_BITSIZE(d->output.format)) { | ||
230 | case 8: { | ||
231 | uint8_t *value = samples; | ||
232 | for (size_t count = numChannels * n; count; count--, value++) { | ||
233 | *value = (int) (*value - 127) * gain + 127; | ||
234 | } | ||
235 | break; | ||
236 | } | ||
237 | case 16: { | ||
238 | int16_t *value = samples; | ||
239 | for (size_t count = numChannels * n; count; count--, value++) { | ||
240 | *value *= gain; | ||
241 | } | ||
242 | break; | ||
243 | } | ||
244 | case 32: { | ||
245 | int32_t *value = samples; | ||
246 | for (size_t count = numChannels * n; count; count--, value++) { | ||
247 | *value *= gain; | ||
248 | } | ||
249 | break; | ||
250 | } | ||
251 | } | ||
252 | } | ||
185 | } | 253 | } |
186 | iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); | 254 | iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); |
187 | free(samples); | 255 | free(samples); |
256 | return ok_DecoderParseStatus; | ||
188 | } | 257 | } |
189 | 258 | ||
190 | static iThreadResult run_Decoder_(iThread *thread) { | 259 | static iThreadResult run_Decoder_(iThread *thread) { |
191 | iDecoder *d = userData_Thread(thread); | 260 | iDecoder *d = userData_Thread(thread); |
261 | /* Amount of data initially available. */ | ||
262 | lock_Mutex(&d->input->mtx); | ||
263 | size_t inputSize = size_InputBuf(d->input); | ||
264 | unlock_Mutex(&d->input->mtx); | ||
192 | while (d->type) { | 265 | while (d->type) { |
193 | size_t inputSize = 0; | ||
194 | /* Grab more input. */ { | ||
195 | lock_Mutex(&d->input->mtx); | ||
196 | wait_Condition(&d->input->changed, &d->input->mtx); | ||
197 | inputSize = size_Block(&d->input->data); | ||
198 | unlock_Mutex(&d->input->mtx); | ||
199 | } | ||
200 | iRanges inputRange = { d->inputPos, inputSize }; | 266 | iRanges inputRange = { d->inputPos, inputSize }; |
201 | iAssert(inputRange.start <= inputRange.end); | 267 | iAssert(inputRange.start <= inputRange.end); |
202 | if (!d->type) break; | 268 | if (!d->type) break; |
203 | /* Have data to work on and a place to save output? */ | 269 | /* Have data to work on and a place to save output? */ |
204 | if (!isEmpty_Range(&inputRange) && !isFull_SampleBuf(&d->output)) { | 270 | enum iDecoderParseStatus status = ok_DecoderParseStatus; |
271 | if (!isEmpty_Range(&inputRange)) { | ||
205 | switch (d->type) { | 272 | switch (d->type) { |
206 | case wav_DecoderType: | 273 | case wav_DecoderType: |
207 | parseWav_Decoder_(d, inputRange); | 274 | status = parseWav_Decoder_(d, inputRange); |
208 | break; | 275 | break; |
209 | default: | 276 | default: |
210 | break; | 277 | break; |
211 | } | 278 | } |
212 | } | 279 | } |
280 | if (status == needMoreInput_DecoderParseStatus) { | ||
281 | lock_Mutex(&d->input->mtx); | ||
282 | if (size_InputBuf(d->input) == inputSize) { | ||
283 | wait_Condition(&d->input->changed, &d->input->mtx); | ||
284 | } | ||
285 | inputSize = size_InputBuf(d->input); | ||
286 | unlock_Mutex(&d->input->mtx); | ||
287 | } | ||
288 | else { | ||
289 | iGuardMutex( | ||
290 | &d->outputMutex, if (isFull_SampleBuf(&d->output)) { | ||
291 | wait_Condition(&d->output.moreNeeded, &d->outputMutex); | ||
292 | }); | ||
293 | } | ||
213 | } | 294 | } |
214 | return 0; | 295 | return 0; |
215 | } | 296 | } |
216 | 297 | ||
217 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | 298 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { |
218 | d->type = wav_DecoderType; | 299 | d->type = wav_DecoderType; |
219 | d->gain = 0.5f; | 300 | d->gain = 0.5f; |
220 | d->input = input; | 301 | d->input = input; |
221 | d->inputPos = spec->wavData.start; | 302 | d->inputPos = spec->wavData.start; |
303 | d->inputFormat = spec->inputFormat; | ||
222 | init_SampleBuf(&d->output, | 304 | init_SampleBuf(&d->output, |
223 | spec->spec.channels, | 305 | spec->output.format, |
224 | SDL_AUDIO_BITSIZE(spec->spec.format) / 8 * spec->spec.channels, | 306 | spec->output.channels, |
225 | spec->spec.samples * 2); | 307 | spec->output.samples * 2); |
226 | init_Mutex(&d->outputMutex); | 308 | init_Mutex(&d->outputMutex); |
227 | d->thread = new_Thread(run_Decoder_); | 309 | d->thread = new_Thread(run_Decoder_); |
228 | setUserData_Thread(d->thread, d); | 310 | setUserData_Thread(d->thread, d); |
@@ -231,6 +313,7 @@ void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | |||
231 | 313 | ||
232 | void deinit_Decoder(iDecoder *d) { | 314 | void deinit_Decoder(iDecoder *d) { |
233 | d->type = none_DecoderType; | 315 | d->type = none_DecoderType; |
316 | signal_Condition(&d->output.moreNeeded); | ||
234 | signal_Condition(&d->input->changed); | 317 | signal_Condition(&d->input->changed); |
235 | join_Thread(d->thread); | 318 | join_Thread(d->thread); |
236 | iRelease(d->thread); | 319 | iRelease(d->thread); |
@@ -287,30 +370,52 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
287 | readData_Buffer(buf, 4, magic); | 370 | readData_Buffer(buf, 4, magic); |
288 | const size_t size = read32_Stream(is); | 371 | const size_t size = read32_Stream(is); |
289 | if (memcmp(magic, "fmt ", 4) == 0) { | 372 | if (memcmp(magic, "fmt ", 4) == 0) { |
290 | if (size != 16) { | 373 | if (size != 16 && size != 18) { |
291 | return content; | 374 | return content; |
292 | } | 375 | } |
293 | const int16_t mode = read16_Stream(is); /* 1 = PCM */ | 376 | enum iWavFormat { |
377 | pcm_WavFormat = 1, | ||
378 | ieeeFloat_WavFormat = 3, | ||
379 | }; | ||
380 | const int16_t mode = read16_Stream(is); /* 1 = PCM, 3 = IEEE_FLOAT */ | ||
294 | const int16_t numChannels = read16_Stream(is); | 381 | const int16_t numChannels = read16_Stream(is); |
295 | const int32_t freq = read32_Stream(is); | 382 | const int32_t freq = read32_Stream(is); |
296 | const uint32_t bytesPerSecond = readU32_Stream(is); | 383 | const uint32_t bytesPerSecond = readU32_Stream(is); |
297 | const int16_t blockAlign = read16_Stream(is); | 384 | const int16_t blockAlign = read16_Stream(is); |
298 | const int16_t bitsPerSample = read16_Stream(is); | 385 | const int16_t bitsPerSample = read16_Stream(is); |
386 | const uint16_t extSize = (size == 18 ? readU16_Stream(is) : 0); | ||
299 | iUnused(bytesPerSecond); | 387 | iUnused(bytesPerSecond); |
300 | iUnused(blockAlign); /* TODO: Should use this one when reading samples? */ | 388 | if (mode != pcm_WavFormat && mode != ieeeFloat_WavFormat) { /* PCM or float */ |
301 | if (mode != 1) { /* PCM */ | 389 | return content; |
390 | } | ||
391 | if (extSize != 0) { | ||
302 | return content; | 392 | return content; |
303 | } | 393 | } |
304 | if (numChannels != 1 && numChannels != 2) { | 394 | if (numChannels != 1 && numChannels != 2) { |
305 | return content; | 395 | return content; |
306 | } | 396 | } |
307 | if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32) { | 397 | if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && |
398 | bitsPerSample != 32 && bitsPerSample != 64) { | ||
399 | return content; | ||
400 | } | ||
401 | if (bitsPerSample == 24 && blockAlign != 3 * numChannels) { | ||
308 | return content; | 402 | return content; |
309 | } | 403 | } |
310 | content.spec.freq = freq; | 404 | content.output.freq = freq; |
311 | content.spec.channels = numChannels; | 405 | content.output.channels = numChannels; |
312 | content.spec.format = | 406 | if (mode == ieeeFloat_WavFormat) { |
313 | (bitsPerSample == 8 ? AUDIO_S8 : bitsPerSample == 16 ? AUDIO_S16 : AUDIO_S32); | 407 | content.inputFormat = (bitsPerSample == 32 ? AUDIO_F32 : AUDIO_F64LSB); |
408 | content.output.format = AUDIO_F32; | ||
409 | } | ||
410 | else if (bitsPerSample == 24) { | ||
411 | content.inputFormat = AUDIO_S24LSB; | ||
412 | content.output.format = AUDIO_S16; | ||
413 | } | ||
414 | else { | ||
415 | content.inputFormat = content.output.format = | ||
416 | (bitsPerSample == 8 ? AUDIO_U8 | ||
417 | : bitsPerSample == 16 ? AUDIO_S16 : AUDIO_S32); | ||
418 | } | ||
314 | } | 419 | } |
315 | else if (memcmp(magic, "data", 4) == 0) { | 420 | else if (memcmp(magic, "data", 4) == 0) { |
316 | content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; | 421 | content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; |
@@ -321,7 +426,10 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
321 | } | 426 | } |
322 | } | 427 | } |
323 | } | 428 | } |
324 | content.spec.samples = 2048; | 429 | iAssert(content.inputFormat == content.output.format || |
430 | (content.inputFormat == AUDIO_S24LSB && content.output.format == AUDIO_S16) || | ||
431 | (content.inputFormat == AUDIO_F64LSB && content.output.format == AUDIO_F32)); | ||
432 | content.output.samples = 2048; | ||
325 | return content; | 433 | return content; |
326 | } | 434 | } |
327 | 435 | ||
@@ -337,9 +445,8 @@ static void writeOutputSamples_Player_(void *plr, Uint8 *stream, int len) { | |||
337 | else { | 445 | else { |
338 | memset(stream, d->spec.silence, len); | 446 | memset(stream, d->spec.silence, len); |
339 | } | 447 | } |
448 | signal_Condition(&d->decoder->output.moreNeeded); | ||
340 | unlock_Mutex(&d->decoder->outputMutex); | 449 | unlock_Mutex(&d->decoder->outputMutex); |
341 | /* Wake up decoder; there is more room for output. */ | ||
342 | signal_Condition(&d->data->changed); | ||
343 | } | 450 | } |
344 | 451 | ||
345 | void init_Player(iPlayer *d) { | 452 | void init_Player(iPlayer *d) { |
@@ -387,9 +494,9 @@ iBool start_Player(iPlayer *d) { | |||
387 | return iFalse; | 494 | return iFalse; |
388 | } | 495 | } |
389 | iContentSpec content = contentSpec_Player_(d); | 496 | iContentSpec content = contentSpec_Player_(d); |
390 | content.spec.callback = writeOutputSamples_Player_; | 497 | content.output.callback = writeOutputSamples_Player_; |
391 | content.spec.userdata = d; | 498 | content.output.userdata = d; |
392 | d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.spec, &d->spec, 0); | 499 | d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0); |
393 | if (!d->device) { | 500 | if (!d->device) { |
394 | return iFalse; | 501 | return iFalse; |
395 | } | 502 | } |