diff options
Diffstat (limited to 'src/audio/player.c')
-rw-r--r-- | src/audio/player.c | 87 |
1 files changed, 55 insertions, 32 deletions
diff --git a/src/audio/player.c b/src/audio/player.c index 1dd3c38a..1dd2ebfc 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -60,17 +60,21 @@ iDeclareType(SampleBuf) | |||
60 | 60 | ||
61 | struct Impl_SampleBuf { | 61 | struct Impl_SampleBuf { |
62 | void * data; | 62 | void * data; |
63 | size_t sampleSize; | 63 | uint8_t numBits; |
64 | uint8_t numChannels; | ||
65 | uint8_t sampleSize; /* bytes; one sample includes values for all channels */ | ||
64 | size_t count; | 66 | size_t count; |
65 | size_t head, tail; | 67 | size_t head, tail; |
66 | }; | 68 | }; |
67 | 69 | ||
68 | void init_SampleBuf(iSampleBuf *d, size_t sampleSize, size_t count) { | 70 | void init_SampleBuf(iSampleBuf *d, size_t numChannels, size_t sampleSize, size_t count) { |
69 | d->sampleSize = sampleSize; | 71 | d->numChannels = numChannels; |
70 | d->count = count + 1; /* considered empty if head==tail */ | 72 | d->sampleSize = sampleSize; |
71 | d->data = malloc(d->sampleSize * d->count); | 73 | d->numBits = sampleSize / numChannels * 8; |
72 | d->head = 0; | 74 | d->count = count + 1; /* considered empty if head==tail */ |
73 | d->tail = 0; | 75 | d->data = malloc(d->sampleSize * d->count); |
76 | d->head = 0; | ||
77 | d->tail = 0; | ||
74 | } | 78 | } |
75 | 79 | ||
76 | void deinit_SampleBuf(iSampleBuf *d) { | 80 | void deinit_SampleBuf(iSampleBuf *d) { |
@@ -146,24 +150,41 @@ enum iDecoderType { | |||
146 | 150 | ||
147 | struct Impl_Decoder { | 151 | struct Impl_Decoder { |
148 | enum iDecoderType type; | 152 | enum iDecoderType type; |
153 | float gain; | ||
149 | iThread * thread; | 154 | iThread * thread; |
150 | iInputBuf * input; | 155 | iInputBuf * input; |
151 | size_t inputPos; | 156 | size_t inputPos; |
152 | iSampleBuf output; | 157 | iSampleBuf output; |
153 | iMutex mtx; /* for output */ | 158 | iMutex outputMutex; |
154 | iRanges wavData; | 159 | iRanges wavData; |
155 | }; | 160 | }; |
156 | 161 | ||
157 | static void parseWav_Decoder_(iDecoder *d, iRanges inputRange) { | 162 | static void parseWav_Decoder_(iDecoder *d, iRanges inputRange) { |
158 | lock_Mutex(&d->mtx); | 163 | const size_t sampleSize = d->output.sampleSize; |
159 | const size_t vacancy = vacancy_SampleBuf(&d->output); | 164 | const size_t vacancy = vacancy_SampleBuf(&d->output); |
160 | const size_t avail = iMin(inputRange.end - d->inputPos, d->wavData.end - d->inputPos) / | 165 | const size_t avail = iMin(inputRange.end - d->inputPos, d->wavData.end - d->inputPos) / |
161 | d->output.sampleSize; | 166 | sampleSize; |
162 | const size_t n = iMin(vacancy, avail); | 167 | const size_t n = iMin(vacancy, avail); |
163 | iGuardMutex(&d->input->mtx, /* lock so input array isn't reallocated during the copy */ | 168 | if (n == 0) return; |
164 | write_SampleBuf(&d->output, constData_Block(&d->input->data) + d->inputPos, n)); | 169 | void *samples = malloc(sampleSize * n); |
165 | d->inputPos += n; | 170 | /* Get a copy of the input for mixing. */ { |
166 | unlock_Mutex(&d->mtx); | 171 | lock_Mutex(&d->input->mtx); |
172 | memcpy( | ||
173 | samples, constData_Block(&d->input->data) + sampleSize * d->inputPos, sampleSize * n); | ||
174 | d->inputPos += n; | ||
175 | unlock_Mutex(&d->input->mtx); | ||
176 | } | ||
177 | /* Gain. */ { | ||
178 | const float gain = d->gain; | ||
179 | if (d->output.numBits == 16) { | ||
180 | int16_t *value = samples; | ||
181 | for (size_t count = d->output.numChannels * n; count; count--, value++) { | ||
182 | *value *= gain; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); | ||
187 | free(samples); | ||
167 | } | 188 | } |
168 | 189 | ||
169 | static iThreadResult run_Decoder_(iThread *thread) { | 190 | static iThreadResult run_Decoder_(iThread *thread) { |
@@ -195,11 +216,14 @@ static iThreadResult run_Decoder_(iThread *thread) { | |||
195 | 216 | ||
196 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | 217 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { |
197 | d->type = wav_DecoderType; | 218 | d->type = wav_DecoderType; |
219 | d->gain = 0.5f; | ||
198 | d->input = input; | 220 | d->input = input; |
199 | d->inputPos = spec->wavData.start; | 221 | d->inputPos = spec->wavData.start; |
200 | init_SampleBuf(&d->output, SDL_AUDIO_BITSIZE(spec->spec.format) / 8 * | 222 | init_SampleBuf(&d->output, |
201 | spec->spec.channels, spec->spec.samples * 2); | 223 | spec->spec.channels, |
202 | init_Mutex(&d->mtx); | 224 | SDL_AUDIO_BITSIZE(spec->spec.format) / 8 * spec->spec.channels, |
225 | spec->spec.samples * 2); | ||
226 | init_Mutex(&d->outputMutex); | ||
203 | d->thread = new_Thread(run_Decoder_); | 227 | d->thread = new_Thread(run_Decoder_); |
204 | setUserData_Thread(d->thread, d); | 228 | setUserData_Thread(d->thread, d); |
205 | start_Thread(d->thread); | 229 | start_Thread(d->thread); |
@@ -210,7 +234,7 @@ void deinit_Decoder(iDecoder *d) { | |||
210 | signal_Condition(&d->input->changed); | 234 | signal_Condition(&d->input->changed); |
211 | join_Thread(d->thread); | 235 | join_Thread(d->thread); |
212 | iRelease(d->thread); | 236 | iRelease(d->thread); |
213 | deinit_Mutex(&d->mtx); | 237 | deinit_Mutex(&d->outputMutex); |
214 | deinit_SampleBuf(&d->output); | 238 | deinit_SampleBuf(&d->output); |
215 | } | 239 | } |
216 | 240 | ||
@@ -289,8 +313,7 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
289 | (bitsPerSample == 8 ? AUDIO_S8 : bitsPerSample == 16 ? AUDIO_S16 : AUDIO_S32); | 313 | (bitsPerSample == 8 ? AUDIO_S8 : bitsPerSample == 16 ? AUDIO_S16 : AUDIO_S32); |
290 | } | 314 | } |
291 | else if (memcmp(magic, "data", 4) == 0) { | 315 | else if (memcmp(magic, "data", 4) == 0) { |
292 | size_t len = read32_Stream(is); /* data size */ | 316 | content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; |
293 | content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + len }; | ||
294 | break; | 317 | break; |
295 | } | 318 | } |
296 | else { | 319 | else { |
@@ -307,14 +330,14 @@ static void writeOutputSamples_Player_(void *plr, Uint8 *stream, int len) { | |||
307 | iAssert(d->decoder); | 330 | iAssert(d->decoder); |
308 | const size_t sampleSize = sampleSize_Player_(d); | 331 | const size_t sampleSize = sampleSize_Player_(d); |
309 | const size_t count = len / sampleSize; | 332 | const size_t count = len / sampleSize; |
310 | lock_Mutex(&d->decoder->mtx); | 333 | lock_Mutex(&d->decoder->outputMutex); |
311 | if (size_SampleBuf(&d->decoder->output) >= count) { | 334 | if (size_SampleBuf(&d->decoder->output) >= count) { |
312 | read_SampleBuf(&d->decoder->output, count, stream); | 335 | read_SampleBuf(&d->decoder->output, count, stream); |
313 | } | 336 | } |
314 | else { | 337 | else { |
315 | memset(stream, 0, len); /* TODO: If unsigned samples, don't use zero! */ | 338 | memset(stream, d->spec.silence, len); |
316 | } | 339 | } |
317 | unlock_Mutex(&d->decoder->mtx); | 340 | unlock_Mutex(&d->decoder->outputMutex); |
318 | /* Wake up decoder; there is more room for output. */ | 341 | /* Wake up decoder; there is more room for output. */ |
319 | signal_Condition(&d->data->changed); | 342 | signal_Condition(&d->data->changed); |
320 | } | 343 | } |
@@ -340,23 +363,23 @@ void setFormatHint_Player(iPlayer *d, const char *hint) { | |||
340 | } | 363 | } |
341 | 364 | ||
342 | void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) { | 365 | void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) { |
343 | iInputBuf *inp = d->data; | 366 | iInputBuf *input = d->data; |
344 | lock_Mutex(&inp->mtx); | 367 | lock_Mutex(&input->mtx); |
345 | switch (update) { | 368 | switch (update) { |
346 | case replace_PlayerUpdate: | 369 | case replace_PlayerUpdate: |
347 | set_Block(&inp->data, data); | 370 | set_Block(&input->data, data); |
348 | inp->isComplete = iFalse; | 371 | input->isComplete = iFalse; |
349 | break; | 372 | break; |
350 | case append_PlayerUpdate: | 373 | case append_PlayerUpdate: |
351 | append_Block(&inp->data, data); | 374 | append_Block(&input->data, data); |
352 | inp->isComplete = iFalse; | 375 | input->isComplete = iFalse; |
353 | break; | 376 | break; |
354 | case complete_PlayerUpdate: | 377 | case complete_PlayerUpdate: |
355 | inp->isComplete = iTrue; | 378 | input->isComplete = iTrue; |
356 | break; | 379 | break; |
357 | } | 380 | } |
358 | signal_Condition(&inp->changed); | 381 | signal_Condition(&input->changed); |
359 | unlock_Mutex(&inp->mtx); | 382 | unlock_Mutex(&input->mtx); |
360 | } | 383 | } |
361 | 384 | ||
362 | iBool start_Player(iPlayer *d) { | 385 | iBool start_Player(iPlayer *d) { |