diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-09 14:19:40 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-09 14:19:40 +0300 |
commit | 4bf163ecfac27c3dd86dff96df6f4647f9afe021 (patch) | |
tree | e360b25b9b420068e0650ed2caa57fed054a733c /src/audio/player.c | |
parent | bb7bc6fac4fec804846d11c7d77e1b553ba2be6a (diff) |
Cleanup
Moved buffers out of player.c. Include MIME type with the data so the correct decoder can be chosen.
Added stb_vorbis: https://github.com/nothings/stb
Diffstat (limited to 'src/audio/player.c')
-rw-r--r-- | src/audio/player.c | 197 |
1 files changed, 49 insertions, 148 deletions
diff --git a/src/audio/player.c b/src/audio/player.c index 07f41f01..0825dabd 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -21,146 +21,38 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "player.h" | 23 | #include "player.h" |
24 | #include "buf.h" | ||
25 | |||
26 | #define STB_VORBIS_HEADER_ONLY | ||
27 | #include "stb_vorbis.c" | ||
24 | 28 | ||
25 | #include <the_Foundation/buffer.h> | 29 | #include <the_Foundation/buffer.h> |
26 | #include <the_Foundation/thread.h> | 30 | #include <the_Foundation/thread.h> |
27 | #include <SDL_audio.h> | 31 | #include <SDL_audio.h> |
28 | 32 | ||
29 | iDeclareType(InputBuf) | ||
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 | |||
38 | struct Impl_InputBuf { | ||
39 | iMutex mtx; | ||
40 | iCondition changed; | ||
41 | iBlock data; | ||
42 | iBool isComplete; | ||
43 | }; | ||
44 | |||
45 | void init_InputBuf(iInputBuf *d) { | ||
46 | init_Mutex(&d->mtx); | ||
47 | init_Condition(&d->changed); | ||
48 | init_Block(&d->data, 0); | ||
49 | d->isComplete = iTrue; | ||
50 | } | ||
51 | |||
52 | void deinit_InputBuf(iInputBuf *d) { | ||
53 | deinit_Block(&d->data); | ||
54 | deinit_Condition(&d->changed); | ||
55 | deinit_Mutex(&d->mtx); | ||
56 | } | ||
57 | |||
58 | size_t size_InputBuf(const iInputBuf *d) { | ||
59 | return size_Block(&d->data); | ||
60 | } | ||
61 | |||
62 | iDefineTypeConstruction(InputBuf) | ||
63 | |||
64 | /*----------------------------------------------------------------------------------------------*/ | ||
65 | |||
66 | iDeclareType(SampleBuf) | ||
67 | |||
68 | struct Impl_SampleBuf { | ||
69 | SDL_AudioFormat format; | ||
70 | uint8_t numChannels; | ||
71 | uint8_t sampleSize; /* as bytes; one sample includes values for all channels */ | ||
72 | void * data; | ||
73 | size_t count; | ||
74 | size_t head, tail; | ||
75 | iCondition moreNeeded; | ||
76 | }; | ||
77 | |||
78 | void init_SampleBuf(iSampleBuf *d, SDL_AudioFormat format, size_t numChannels, size_t count) { | ||
79 | d->format = format; | ||
80 | d->numChannels = numChannels; | ||
81 | d->sampleSize = SDL_AUDIO_BITSIZE(format) / 8 * numChannels; | ||
82 | d->count = count + 1; /* considered empty if head==tail */ | ||
83 | d->data = malloc(d->sampleSize * d->count); | ||
84 | d->head = 0; | ||
85 | d->tail = 0; | ||
86 | init_Condition(&d->moreNeeded); | ||
87 | } | ||
88 | |||
89 | void deinit_SampleBuf(iSampleBuf *d) { | ||
90 | deinit_Condition(&d->moreNeeded); | ||
91 | free(d->data); | ||
92 | } | ||
93 | |||
94 | size_t size_SampleBuf(const iSampleBuf *d) { | ||
95 | return d->head - d->tail; | ||
96 | } | ||
97 | |||
98 | size_t vacancy_SampleBuf(const iSampleBuf *d) { | ||
99 | return d->count - size_SampleBuf(d) - 1; | ||
100 | } | ||
101 | |||
102 | iBool isFull_SampleBuf(const iSampleBuf *d) { | ||
103 | return vacancy_SampleBuf(d) == 0; | ||
104 | } | ||
105 | |||
106 | iLocalDef void *ptr_SampleBuf_(iSampleBuf *d, size_t pos) { | ||
107 | return ((char *) d->data) + (d->sampleSize * pos); | ||
108 | } | ||
109 | |||
110 | void write_SampleBuf(iSampleBuf *d, const void *samples, const size_t n) { | ||
111 | iAssert(n <= vacancy_SampleBuf(d)); | ||
112 | const size_t headPos = d->head % d->count; | ||
113 | const size_t avail = d->count - headPos; | ||
114 | if (n > avail) { | ||
115 | const char *in = samples; | ||
116 | memcpy(ptr_SampleBuf_(d, headPos), in, d->sampleSize * avail); | ||
117 | in += d->sampleSize * avail; | ||
118 | memcpy(ptr_SampleBuf_(d, 0), in, d->sampleSize * (n - avail)); | ||
119 | } | ||
120 | else { | ||
121 | memcpy(ptr_SampleBuf_(d, headPos), samples, d->sampleSize * n); | ||
122 | } | ||
123 | d->head += n; | ||
124 | } | ||
125 | |||
126 | void read_SampleBuf(iSampleBuf *d, const size_t n, void *samples_out) { | ||
127 | iAssert(n <= size_SampleBuf(d)); | ||
128 | const size_t tailPos = d->tail % d->count; | ||
129 | const size_t avail = d->count - tailPos; | ||
130 | if (n > avail) { | ||
131 | char *out = samples_out; | ||
132 | memcpy(out, ptr_SampleBuf_(d, tailPos), d->sampleSize * avail); | ||
133 | out += d->sampleSize * avail; | ||
134 | memcpy(out, ptr_SampleBuf_(d, 0), d->sampleSize * (n - avail)); | ||
135 | } | ||
136 | else { | ||
137 | memcpy(samples_out, ptr_SampleBuf_(d, tailPos), d->sampleSize * n); | ||
138 | } | ||
139 | d->tail += n; | ||
140 | } | ||
141 | |||
142 | /*----------------------------------------------------------------------------------------------*/ | 33 | /*----------------------------------------------------------------------------------------------*/ |
143 | 34 | ||
144 | iDeclareType(ContentSpec) | 35 | iDeclareType(ContentSpec) |
145 | 36 | ||
146 | struct Impl_ContentSpec { | ||
147 | SDL_AudioFormat inputFormat; | ||
148 | SDL_AudioSpec output; | ||
149 | size_t totalInputSize; | ||
150 | uint64_t totalSamples; | ||
151 | iRanges wavData; | ||
152 | }; | ||
153 | |||
154 | iDeclareType(Decoder) | ||
155 | |||
156 | enum iDecoderType { | 37 | enum iDecoderType { |
157 | none_DecoderType, | 38 | none_DecoderType, |
158 | wav_DecoderType, | 39 | wav_DecoderType, |
159 | mpeg_DecoderType, | ||
160 | vorbis_DecoderType, | 40 | vorbis_DecoderType, |
41 | mpeg_DecoderType, | ||
161 | midi_DecoderType, | 42 | midi_DecoderType, |
162 | }; | 43 | }; |
163 | 44 | ||
45 | struct Impl_ContentSpec { | ||
46 | enum iDecoderType type; | ||
47 | SDL_AudioFormat inputFormat; | ||
48 | SDL_AudioSpec output; | ||
49 | size_t totalInputSize; | ||
50 | uint64_t totalSamples; | ||
51 | iRanges dataRange; | ||
52 | }; | ||
53 | |||
54 | iDeclareType(Decoder) | ||
55 | |||
164 | struct Impl_Decoder { | 56 | struct Impl_Decoder { |
165 | enum iDecoderType type; | 57 | enum iDecoderType type; |
166 | float gain; | 58 | float gain; |
@@ -176,12 +68,12 @@ struct Impl_Decoder { | |||
176 | iRanges wavData; | 68 | iRanges wavData; |
177 | }; | 69 | }; |
178 | 70 | ||
179 | enum iDecoderParseStatus { | 71 | enum iDecoderStatus { |
180 | ok_DecoderParseStatus, | 72 | ok_DecoderStatus, |
181 | needMoreInput_DecoderParseStatus, | 73 | needMoreInput_DecoderStatus, |
182 | }; | 74 | }; |
183 | 75 | ||
184 | static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRange) { | 76 | static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) { |
185 | const uint8_t numChannels = d->output.numChannels; | 77 | const uint8_t numChannels = d->output.numChannels; |
186 | const size_t inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8; | 78 | const size_t inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8; |
187 | const size_t vacancy = vacancy_SampleBuf(&d->output); | 79 | const size_t vacancy = vacancy_SampleBuf(&d->output); |
@@ -189,11 +81,11 @@ static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRang | |||
189 | const size_t avail = | 81 | const size_t avail = |
190 | iMin(inputRange.end - inputBytePos, d->wavData.end - inputBytePos) / inputSampleSize; | 82 | iMin(inputRange.end - inputBytePos, d->wavData.end - inputBytePos) / inputSampleSize; |
191 | if (avail == 0) { | 83 | if (avail == 0) { |
192 | return needMoreInput_DecoderParseStatus; | 84 | return needMoreInput_DecoderStatus; |
193 | } | 85 | } |
194 | const size_t n = iMin(vacancy, avail); | 86 | const size_t n = iMin(vacancy, avail); |
195 | if (n == 0) { | 87 | if (n == 0) { |
196 | return ok_DecoderParseStatus; | 88 | return ok_DecoderStatus; |
197 | } | 89 | } |
198 | void *samples = malloc(inputSampleSize * n); | 90 | void *samples = malloc(inputSampleSize * n); |
199 | /* Get a copy of the input for mixing. */ { | 91 | /* Get a copy of the input for mixing. */ { |
@@ -259,7 +151,7 @@ static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRang | |||
259 | iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); | 151 | iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); |
260 | d->currentSample += n; | 152 | d->currentSample += n; |
261 | free(samples); | 153 | free(samples); |
262 | return ok_DecoderParseStatus; | 154 | return ok_DecoderStatus; |
263 | } | 155 | } |
264 | 156 | ||
265 | static iThreadResult run_Decoder_(iThread *thread) { | 157 | static iThreadResult run_Decoder_(iThread *thread) { |
@@ -273,17 +165,17 @@ static iThreadResult run_Decoder_(iThread *thread) { | |||
273 | iAssert(inputRange.start <= inputRange.end); | 165 | iAssert(inputRange.start <= inputRange.end); |
274 | if (!d->type) break; | 166 | if (!d->type) break; |
275 | /* Have data to work on and a place to save output? */ | 167 | /* Have data to work on and a place to save output? */ |
276 | enum iDecoderParseStatus status = ok_DecoderParseStatus; | 168 | enum iDecoderStatus status = ok_DecoderStatus; |
277 | if (!isEmpty_Range(&inputRange)) { | 169 | if (!isEmpty_Range(&inputRange)) { |
278 | switch (d->type) { | 170 | switch (d->type) { |
279 | case wav_DecoderType: | 171 | case wav_DecoderType: |
280 | status = parseWav_Decoder_(d, inputRange); | 172 | status = decodeWav_Decoder_(d, inputRange); |
281 | break; | 173 | break; |
282 | default: | 174 | default: |
283 | break; | 175 | break; |
284 | } | 176 | } |
285 | } | 177 | } |
286 | if (status == needMoreInput_DecoderParseStatus) { | 178 | if (status == needMoreInput_DecoderStatus) { |
287 | lock_Mutex(&d->input->mtx); | 179 | lock_Mutex(&d->input->mtx); |
288 | if (size_InputBuf(d->input) == inputSize) { | 180 | if (size_InputBuf(d->input) == inputSize) { |
289 | wait_Condition(&d->input->changed, &d->input->mtx); | 181 | wait_Condition(&d->input->changed, &d->input->mtx); |
@@ -302,10 +194,10 @@ static iThreadResult run_Decoder_(iThread *thread) { | |||
302 | } | 194 | } |
303 | 195 | ||
304 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | 196 | void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { |
305 | d->type = wav_DecoderType; | 197 | d->type = spec->type; |
306 | d->gain = 0.5f; | 198 | d->gain = 0.5f; |
307 | d->input = input; | 199 | d->input = input; |
308 | d->inputPos = spec->wavData.start; | 200 | d->inputPos = spec->dataRange.start; |
309 | d->inputFormat = spec->inputFormat; | 201 | d->inputFormat = spec->inputFormat; |
310 | d->totalInputSize = spec->totalInputSize; | 202 | d->totalInputSize = spec->totalInputSize; |
311 | init_SampleBuf(&d->output, | 203 | init_SampleBuf(&d->output, |
@@ -330,16 +222,22 @@ void deinit_Decoder(iDecoder *d) { | |||
330 | deinit_SampleBuf(&d->output); | 222 | deinit_SampleBuf(&d->output); |
331 | } | 223 | } |
332 | 224 | ||
225 | static void start_Decoder_(iDecoder *d) { | ||
226 | if (!d->thread && d->type != none_DecoderType) { | ||
227 | } | ||
228 | } | ||
229 | |||
333 | iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec), | 230 | iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec), |
334 | input, spec) | 231 | input, spec) |
335 | 232 | ||
336 | /*----------------------------------------------------------------------------------------------*/ | 233 | /*----------------------------------------------------------------------------------------------*/ |
337 | 234 | ||
338 | struct Impl_Player { | 235 | struct Impl_Player { |
339 | SDL_AudioSpec spec; | 236 | SDL_AudioSpec spec; |
340 | SDL_AudioDeviceID device; | 237 | SDL_AudioDeviceID device; |
341 | iInputBuf *data; | 238 | iString mime; |
342 | iDecoder *decoder; | 239 | iInputBuf * data; |
240 | iDecoder * decoder; | ||
343 | }; | 241 | }; |
344 | 242 | ||
345 | iDefineTypeConstruction(Player) | 243 | iDefineTypeConstruction(Player) |
@@ -358,8 +256,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
358 | const size_t dataSize = size_InputBuf(d->data); | 256 | const size_t dataSize = size_InputBuf(d->data); |
359 | iBuffer *buf = iClob(new_Buffer()); | 257 | iBuffer *buf = iClob(new_Buffer()); |
360 | open_Buffer(buf, &d->data->data); | 258 | open_Buffer(buf, &d->data->data); |
361 | enum iDecoderType decType = wav_DecoderType; /* TODO: from MIME */ | 259 | content.type = wav_DecoderType; /* TODO: from MIME */ |
362 | if (decType == wav_DecoderType && dataSize >= 44) { | 260 | if (content.type == wav_DecoderType && dataSize >= 44) { |
363 | /* Read the RIFF/WAVE header. */ | 261 | /* Read the RIFF/WAVE header. */ |
364 | iStream *is = stream_Buffer(buf); | 262 | iStream *is = stream_Buffer(buf); |
365 | char magic[4]; | 263 | char magic[4]; |
@@ -428,8 +326,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
428 | } | 326 | } |
429 | } | 327 | } |
430 | else if (memcmp(magic, "data", 4) == 0) { | 328 | else if (memcmp(magic, "data", 4) == 0) { |
431 | content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; | 329 | content.dataRange = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; |
432 | content.totalSamples = (uint64_t) size_Range(&content.wavData) / blockAlign; | 330 | content.totalSamples = (uint64_t) size_Range(&content.dataRange) / blockAlign; |
433 | break; | 331 | break; |
434 | } | 332 | } |
435 | else { | 333 | else { |
@@ -462,6 +360,7 @@ static void writeOutputSamples_Player_(void *plr, Uint8 *stream, int len) { | |||
462 | 360 | ||
463 | void init_Player(iPlayer *d) { | 361 | void init_Player(iPlayer *d) { |
464 | iZap(d->spec); | 362 | iZap(d->spec); |
363 | init_String(&d->mime); | ||
465 | d->device = 0; | 364 | d->device = 0; |
466 | d->decoder = NULL; | 365 | d->decoder = NULL; |
467 | d->data = new_InputBuf(); | 366 | d->data = new_InputBuf(); |
@@ -470,6 +369,7 @@ void init_Player(iPlayer *d) { | |||
470 | void deinit_Player(iPlayer *d) { | 369 | void deinit_Player(iPlayer *d) { |
471 | stop_Player(d); | 370 | stop_Player(d); |
472 | delete_InputBuf(d->data); | 371 | delete_InputBuf(d->data); |
372 | deinit_String(&d->mime); | ||
473 | } | 373 | } |
474 | 374 | ||
475 | iBool isStarted_Player(const iPlayer *d) { | 375 | iBool isStarted_Player(const iPlayer *d) { |
@@ -481,13 +381,14 @@ iBool isPaused_Player(const iPlayer *d) { | |||
481 | return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED; | 381 | return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED; |
482 | } | 382 | } |
483 | 383 | ||
484 | void setFormatHint_Player(iPlayer *d, const char *hint) { | 384 | void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock *data, |
485 | } | 385 | enum iPlayerUpdate update) { |
486 | |||
487 | void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) { | ||
488 | /* TODO: Add MIME as argument */ | 386 | /* TODO: Add MIME as argument */ |
489 | iInputBuf *input = d->data; | 387 | iInputBuf *input = d->data; |
490 | lock_Mutex(&input->mtx); | 388 | lock_Mutex(&input->mtx); |
389 | if (mimeType) { | ||
390 | set_String(&d->mime, mimeType); | ||
391 | } | ||
491 | switch (update) { | 392 | switch (update) { |
492 | case replace_PlayerUpdate: | 393 | case replace_PlayerUpdate: |
493 | set_Block(&input->data, data); | 394 | set_Block(&input->data, data); |
@@ -515,7 +416,7 @@ iBool start_Player(iPlayer *d) { | |||
515 | if (isStarted_Player(d)) { | 416 | if (isStarted_Player(d)) { |
516 | return iFalse; | 417 | return iFalse; |
517 | } | 418 | } |
518 | iContentSpec content = contentSpec_Player_(d); | 419 | iContentSpec content = contentSpec_Player_(d); |
519 | content.output.callback = writeOutputSamples_Player_; | 420 | content.output.callback = writeOutputSamples_Player_; |
520 | content.output.userdata = d; | 421 | content.output.userdata = d; |
521 | d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0); | 422 | d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0); |