summaryrefslogtreecommitdiff
path: root/src/audio/player.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-10-09 14:19:40 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-10-09 14:19:40 +0300
commit4bf163ecfac27c3dd86dff96df6f4647f9afe021 (patch)
treee360b25b9b420068e0650ed2caa57fed054a733c /src/audio/player.c
parentbb7bc6fac4fec804846d11c7d77e1b553ba2be6a (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.c197
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
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, 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
29iDeclareType(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
38struct Impl_InputBuf {
39 iMutex mtx;
40 iCondition changed;
41 iBlock data;
42 iBool isComplete;
43};
44
45void 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
52void deinit_InputBuf(iInputBuf *d) {
53 deinit_Block(&d->data);
54 deinit_Condition(&d->changed);
55 deinit_Mutex(&d->mtx);
56}
57
58size_t size_InputBuf(const iInputBuf *d) {
59 return size_Block(&d->data);
60}
61
62iDefineTypeConstruction(InputBuf)
63
64/*----------------------------------------------------------------------------------------------*/
65
66iDeclareType(SampleBuf)
67
68struct 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
78void 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
89void deinit_SampleBuf(iSampleBuf *d) {
90 deinit_Condition(&d->moreNeeded);
91 free(d->data);
92}
93
94size_t size_SampleBuf(const iSampleBuf *d) {
95 return d->head - d->tail;
96}
97
98size_t vacancy_SampleBuf(const iSampleBuf *d) {
99 return d->count - size_SampleBuf(d) - 1;
100}
101
102iBool isFull_SampleBuf(const iSampleBuf *d) {
103 return vacancy_SampleBuf(d) == 0;
104}
105
106iLocalDef void *ptr_SampleBuf_(iSampleBuf *d, size_t pos) {
107 return ((char *) d->data) + (d->sampleSize * pos);
108}
109
110void 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
126void 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
144iDeclareType(ContentSpec) 35iDeclareType(ContentSpec)
145 36
146struct Impl_ContentSpec {
147 SDL_AudioFormat inputFormat;
148 SDL_AudioSpec output;
149 size_t totalInputSize;
150 uint64_t totalSamples;
151 iRanges wavData;
152};
153
154iDeclareType(Decoder)
155
156enum iDecoderType { 37enum 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
45struct 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
54iDeclareType(Decoder)
55
164struct Impl_Decoder { 56struct 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
179enum iDecoderParseStatus { 71enum iDecoderStatus {
180 ok_DecoderParseStatus, 72 ok_DecoderStatus,
181 needMoreInput_DecoderParseStatus, 73 needMoreInput_DecoderStatus,
182}; 74};
183 75
184static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRange) { 76static 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
265static iThreadResult run_Decoder_(iThread *thread) { 157static 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
304void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { 196void 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
225static void start_Decoder_(iDecoder *d) {
226 if (!d->thread && d->type != none_DecoderType) {
227 }
228}
229
333iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec), 230iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec),
334 input, spec) 231 input, spec)
335 232
336/*----------------------------------------------------------------------------------------------*/ 233/*----------------------------------------------------------------------------------------------*/
337 234
338struct Impl_Player { 235struct 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
345iDefineTypeConstruction(Player) 243iDefineTypeConstruction(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
463void init_Player(iPlayer *d) { 361void 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) {
470void deinit_Player(iPlayer *d) { 369void 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
475iBool isStarted_Player(const iPlayer *d) { 375iBool 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
484void setFormatHint_Player(iPlayer *d, const char *hint) { 384void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock *data,
485} 385 enum iPlayerUpdate update) {
486
487void 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);