summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-10-09 21:51:36 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-10-09 21:51:36 +0300
commita1101ec38cb29f701457215e35f75904326120cb (patch)
treee0b40731f65b9711d64353a78fd51ffa795b1b67 /src
parent4bf163ecfac27c3dd86dff96df6f4647f9afe021 (diff)
Support Ogg Vorbis audio
Playback starts as soon as possible, so one can listen while streaming. stb_vorbis.c needed a tiny tweak to not die on a file without (Ogg? Vorbis?) comments.
Diffstat (limited to 'src')
-rw-r--r--src/audio/player.c153
-rw-r--r--src/audio/stb_vorbis.c4
-rw-r--r--src/gmrequest.c3
-rw-r--r--src/ui/documentwidget.c13
4 files changed, 141 insertions, 32 deletions
diff --git a/src/audio/player.c b/src/audio/player.c
index 0825dabd..226e0662 100644
--- a/src/audio/player.c
+++ b/src/audio/player.c
@@ -48,7 +48,7 @@ struct Impl_ContentSpec {
48 SDL_AudioSpec output; 48 SDL_AudioSpec output;
49 size_t totalInputSize; 49 size_t totalInputSize;
50 uint64_t totalSamples; 50 uint64_t totalSamples;
51 iRanges dataRange; 51 size_t inputStartPos;
52}; 52};
53 53
54iDeclareType(Decoder) 54iDeclareType(Decoder)
@@ -61,11 +61,13 @@ struct Impl_Decoder {
61 iInputBuf * input; 61 iInputBuf * input;
62 size_t inputPos; 62 size_t inputPos;
63 size_t totalInputSize; 63 size_t totalInputSize;
64 unsigned int outputFreq;
64 iSampleBuf output; 65 iSampleBuf output;
65 iMutex outputMutex; 66 iMutex outputMutex;
67 iArray pendingOutput;
66 uint64_t currentSample; 68 uint64_t currentSample;
67 uint64_t totalSamples; /* zero if unknown */ 69 uint64_t totalSamples; /* zero if unknown */
68 iRanges wavData; 70 stb_vorbis * vorbis;
69}; 71};
70 72
71enum iDecoderStatus { 73enum iDecoderStatus {
@@ -78,8 +80,7 @@ static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) {
78 const size_t inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8; 80 const size_t inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8;
79 const size_t vacancy = vacancy_SampleBuf(&d->output); 81 const size_t vacancy = vacancy_SampleBuf(&d->output);
80 const size_t inputBytePos = inputSampleSize * d->inputPos; 82 const size_t inputBytePos = inputSampleSize * d->inputPos;
81 const size_t avail = 83 const size_t avail = (inputRange.end - inputBytePos) / inputSampleSize;
82 iMin(inputRange.end - inputBytePos, d->wavData.end - inputBytePos) / inputSampleSize;
83 if (avail == 0) { 84 if (avail == 0) {
84 return needMoreInput_DecoderStatus; 85 return needMoreInput_DecoderStatus;
85 } 86 }
@@ -88,7 +89,7 @@ static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) {
88 return ok_DecoderStatus; 89 return ok_DecoderStatus;
89 } 90 }
90 void *samples = malloc(inputSampleSize * n); 91 void *samples = malloc(inputSampleSize * n);
91 /* Get a copy of the input for mixing. */ { 92 /* Get a copy of the input for further processing. */ {
92 lock_Mutex(&d->input->mtx); 93 lock_Mutex(&d->input->mtx);
93 iAssert(inputSampleSize * d->inputPos < size_Block(&d->input->data)); 94 iAssert(inputSampleSize * d->inputPos < size_Block(&d->input->data));
94 memcpy(samples, 95 memcpy(samples,
@@ -154,13 +155,81 @@ static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) {
154 return ok_DecoderStatus; 155 return ok_DecoderStatus;
155} 156}
156 157
158static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d, iRanges inputRange) {
159 iUnused(inputRange);
160 const iBlock *input = &d->input->data;
161 if (!d->vorbis) {
162 lock_Mutex(&d->input->mtx);
163 int error;
164 int consumed;
165 d->vorbis = stb_vorbis_open_pushdata(
166 constData_Block(input), size_Block(input), &consumed, &error, NULL);
167 if (!d->vorbis) {
168 return needMoreInput_DecoderStatus;
169 }
170 d->inputPos += consumed;
171 unlock_Mutex(&d->input->mtx);
172 }
173 if (d->totalSamples == 0 && d->input->isComplete) {
174 /* Time to check the stream size. */
175 lock_Mutex(&d->input->mtx);
176 d->totalInputSize = size_Block(input);
177 int error = 0;
178 stb_vorbis *vrb = stb_vorbis_open_memory(constData_Block(input), size_Block(input),
179 &error, NULL);
180 if (vrb) {
181 d->totalSamples = stb_vorbis_stream_length_in_samples(vrb);
182 stb_vorbis_close(vrb);
183 }
184 unlock_Mutex(&d->input->mtx);
185 }
186 enum iDecoderStatus status = ok_DecoderStatus;
187 while (size_Array(&d->pendingOutput) < d->output.count) {
188 /* Try to decode some input. */
189 lock_Mutex(&d->input->mtx);
190 int count = 0;
191 float **samples = NULL;
192 int remaining = d->inputPos < size_Block(input) ? size_Block(input) - d->inputPos : 0;
193 int consumed = stb_vorbis_decode_frame_pushdata(
194 d->vorbis, constData_Block(input) + d->inputPos, remaining, NULL, &samples, &count);
195 d->inputPos += consumed;
196 iAssert(d->inputPos <= size_Block(input));
197 unlock_Mutex(&d->input->mtx);
198 if (count == 0) {
199 if (consumed == 0) {
200 status = needMoreInput_DecoderStatus;
201 break;
202 }
203 else continue;
204 }
205 /* Apply gain. */ {
206 float sample[2];
207 for (size_t i = 0; i < (size_t) count; ++i) {
208 for (size_t chan = 0; chan < d->output.numChannels; chan++) {
209 sample[chan] = samples[chan][i] * d->gain;
210 }
211 pushBack_Array(&d->pendingOutput, sample);
212 }
213 }
214 }
215 /* Write as much as we can. */
216 lock_Mutex(&d->outputMutex);
217 size_t avail = vacancy_SampleBuf(&d->output);
218 size_t n = iMin(avail, size_Array(&d->pendingOutput));
219 write_SampleBuf(&d->output, constData_Array(&d->pendingOutput), n);
220 removeN_Array(&d->pendingOutput, 0, n);
221 unlock_Mutex(&d->outputMutex);
222 d->currentSample += n;
223 return status;
224}
225
157static iThreadResult run_Decoder_(iThread *thread) { 226static iThreadResult run_Decoder_(iThread *thread) {
158 iDecoder *d = userData_Thread(thread); 227 iDecoder *d = userData_Thread(thread);
159 /* Amount of data initially available. */
160 lock_Mutex(&d->input->mtx);
161 size_t inputSize = size_InputBuf(d->input);
162 unlock_Mutex(&d->input->mtx);
163 while (d->type) { 228 while (d->type) {
229 /* Check amount of data available. */
230 lock_Mutex(&d->input->mtx);
231 size_t inputSize = size_InputBuf(d->input);
232 unlock_Mutex(&d->input->mtx);
164 iRanges inputRange = { d->inputPos, inputSize }; 233 iRanges inputRange = { d->inputPos, inputSize };
165 iAssert(inputRange.start <= inputRange.end); 234 iAssert(inputRange.start <= inputRange.end);
166 if (!d->type) break; 235 if (!d->type) break;
@@ -171,6 +240,8 @@ static iThreadResult run_Decoder_(iThread *thread) {
171 case wav_DecoderType: 240 case wav_DecoderType:
172 status = decodeWav_Decoder_(d, inputRange); 241 status = decodeWav_Decoder_(d, inputRange);
173 break; 242 break;
243 case vorbis_DecoderType:
244 status = decodeVorbis_Decoder_(d, inputRange);
174 default: 245 default:
175 break; 246 break;
176 } 247 }
@@ -180,7 +251,6 @@ static iThreadResult run_Decoder_(iThread *thread) {
180 if (size_InputBuf(d->input) == inputSize) { 251 if (size_InputBuf(d->input) == inputSize) {
181 wait_Condition(&d->input->changed, &d->input->mtx); 252 wait_Condition(&d->input->changed, &d->input->mtx);
182 } 253 }
183 inputSize = size_InputBuf(d->input);
184 unlock_Mutex(&d->input->mtx); 254 unlock_Mutex(&d->input->mtx);
185 } 255 }
186 else { 256 else {
@@ -194,18 +264,21 @@ static iThreadResult run_Decoder_(iThread *thread) {
194} 264}
195 265
196void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { 266void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) {
197 d->type = spec->type; 267 d->type = spec->type;
198 d->gain = 0.5f; 268 d->gain = 0.5f;
199 d->input = input; 269 d->input = input;
200 d->inputPos = spec->dataRange.start; 270 d->inputPos = spec->inputStartPos;
201 d->inputFormat = spec->inputFormat; 271 d->inputFormat = spec->inputFormat;
202 d->totalInputSize = spec->totalInputSize; 272 d->totalInputSize = spec->totalInputSize;
273 d->outputFreq = spec->output.freq;
274 init_Array(&d->pendingOutput, spec->output.channels * SDL_AUDIO_BITSIZE(spec->output.format) / 8);
203 init_SampleBuf(&d->output, 275 init_SampleBuf(&d->output,
204 spec->output.format, 276 spec->output.format,
205 spec->output.channels, 277 spec->output.channels,
206 spec->output.samples * 2); 278 spec->output.samples * 2);
207 d->currentSample = 0; 279 d->currentSample = 0;
208 d->totalSamples = spec->totalSamples; 280 d->totalSamples = spec->totalSamples;
281 d->vorbis = NULL;
209 init_Mutex(&d->outputMutex); 282 init_Mutex(&d->outputMutex);
210 d->thread = new_Thread(run_Decoder_); 283 d->thread = new_Thread(run_Decoder_);
211 setUserData_Thread(d->thread, d); 284 setUserData_Thread(d->thread, d);
@@ -220,11 +293,7 @@ void deinit_Decoder(iDecoder *d) {
220 iRelease(d->thread); 293 iRelease(d->thread);
221 deinit_Mutex(&d->outputMutex); 294 deinit_Mutex(&d->outputMutex);
222 deinit_SampleBuf(&d->output); 295 deinit_SampleBuf(&d->output);
223} 296 deinit_Array(&d->pendingOutput);
224
225static void start_Decoder_(iDecoder *d) {
226 if (!d->thread && d->type != none_DecoderType) {
227 }
228} 297}
229 298
230iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec), 299iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec),
@@ -256,7 +325,18 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
256 const size_t dataSize = size_InputBuf(d->data); 325 const size_t dataSize = size_InputBuf(d->data);
257 iBuffer *buf = iClob(new_Buffer()); 326 iBuffer *buf = iClob(new_Buffer());
258 open_Buffer(buf, &d->data->data); 327 open_Buffer(buf, &d->data->data);
259 content.type = wav_DecoderType; /* TODO: from MIME */ 328 if (!cmp_String(&d->mime, "audio/wave") || !cmp_String(&d->mime, "audio/wav") ||
329 !cmp_String(&d->mime, "audio/x-wav") || !cmp_String(&d->mime, "audio/x-pn-wav")) {
330 content.type = wav_DecoderType;
331 }
332 else if (!cmp_String(&d->mime, "audio/vorbis") || !cmp_String(&d->mime, "audio/ogg") ||
333 !cmp_String(&d->mime, "audio/x-vorbis+ogg")) {
334 content.type = vorbis_DecoderType;
335 }
336 else {
337 /* TODO: Could try decoders to see if one works? */
338 content.type = none_DecoderType;
339 }
260 if (content.type == wav_DecoderType && dataSize >= 44) { 340 if (content.type == wav_DecoderType && dataSize >= 44) {
261 /* Read the RIFF/WAVE header. */ 341 /* Read the RIFF/WAVE header. */
262 iStream *is = stream_Buffer(buf); 342 iStream *is = stream_Buffer(buf);
@@ -326,8 +406,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
326 } 406 }
327 } 407 }
328 else if (memcmp(magic, "data", 4) == 0) { 408 else if (memcmp(magic, "data", 4) == 0) {
329 content.dataRange = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; 409 content.inputStartPos = pos_Stream(is);
330 content.totalSamples = (uint64_t) size_Range(&content.dataRange) / blockAlign; 410 content.totalSamples = (uint64_t) size / blockAlign;
331 break; 411 break;
332 } 412 }
333 else { 413 else {
@@ -335,6 +415,26 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
335 } 415 }
336 } 416 }
337 } 417 }
418 else if (content.type == vorbis_DecoderType) {
419 /* Try to decode what we have and see if it looks like Vorbis. */
420 int consumed = 0;
421 int error = 0;
422 stb_vorbis *vrb = stb_vorbis_open_pushdata(
423 constData_Block(&d->data->data), size_Block(&d->data->data), &consumed, &error, NULL);
424 if (!vrb) {
425 return content;
426 }
427 const stb_vorbis_info info = stb_vorbis_get_info(vrb);
428 const int numChannels = info.channels;
429 if (numChannels != 1 && numChannels != 2) {
430 return content;
431 }
432 content.output.freq = info.sample_rate;
433 content.output.channels = numChannels;
434 content.output.format = AUDIO_F32;
435 content.inputFormat = AUDIO_F32; /* actually stb_vorbis provides floats */
436 stb_vorbis_close(vrb);
437 }
338 iAssert(content.inputFormat == content.output.format || 438 iAssert(content.inputFormat == content.output.format ||
339 (content.inputFormat == AUDIO_S24LSB && content.output.format == AUDIO_S16) || 439 (content.inputFormat == AUDIO_S24LSB && content.output.format == AUDIO_S16) ||
340 (content.inputFormat == AUDIO_F64LSB && content.output.format == AUDIO_F32)); 440 (content.inputFormat == AUDIO_F64LSB && content.output.format == AUDIO_F32));
@@ -416,7 +516,10 @@ iBool start_Player(iPlayer *d) {
416 if (isStarted_Player(d)) { 516 if (isStarted_Player(d)) {
417 return iFalse; 517 return iFalse;
418 } 518 }
419 iContentSpec content = contentSpec_Player_(d); 519 iContentSpec content = contentSpec_Player_(d);
520 if (!content.output.freq) {
521 return iFalse;
522 }
420 content.output.callback = writeOutputSamples_Player_; 523 content.output.callback = writeOutputSamples_Player_;
421 content.output.userdata = d; 524 content.output.userdata = d;
422 d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0); 525 d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0);
@@ -456,7 +559,7 @@ float duration_Player(const iPlayer *d) {
456} 559}
457 560
458float streamProgress_Player(const iPlayer *d) { 561float streamProgress_Player(const iPlayer *d) {
459 if (d->decoder->totalInputSize) { 562 if (d->decoder && d->decoder->totalInputSize) {
460 lock_Mutex(&d->data->mtx); 563 lock_Mutex(&d->data->mtx);
461 const double inputSize = size_InputBuf(d->data); 564 const double inputSize = size_InputBuf(d->data);
462 unlock_Mutex(&d->data->mtx); 565 unlock_Mutex(&d->data->mtx);
diff --git a/src/audio/stb_vorbis.c b/src/audio/stb_vorbis.c
index a8cbfa6c..98b8e649 100644
--- a/src/audio/stb_vorbis.c
+++ b/src/audio/stb_vorbis.c
@@ -3642,8 +3642,8 @@ static int start_decoder(vorb *f)
3642 f->vendor[len] = (char)'\0'; 3642 f->vendor[len] = (char)'\0';
3643 //user comments 3643 //user comments
3644 f->comment_list_length = get32_packet(f); 3644 f->comment_list_length = get32_packet(f);
3645 f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); 3645 f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length + 1));
3646 if (f->comment_list == NULL) return error(f, VORBIS_outofmem); 3646 if (f->comment_list == NULL) return error(f, VORBIS_outofmem);
3647 3647
3648 for(i=0; i < f->comment_list_length; ++i) { 3648 for(i=0; i < f->comment_list_length; ++i) {
3649 len = get32_packet(f); 3649 len = get32_packet(f);
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 161c654c..dd2c2720 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -457,6 +457,9 @@ void submit_GmRequest(iGmRequest *d) {
457 else if (endsWithCase_String(path, ".wav")) { 457 else if (endsWithCase_String(path, ".wav")) {
458 setCStr_String(&d->resp.meta, "audio/wave"); 458 setCStr_String(&d->resp.meta, "audio/wave");
459 } 459 }
460 else if (endsWithCase_String(path, ".ogg")) {
461 setCStr_String(&d->resp.meta, "audio/ogg");
462 }
460 else { 463 else {
461 setCStr_String(&d->resp.meta, "application/octet-stream"); 464 setCStr_String(&d->resp.meta, "application/octet-stream");
462 } 465 }
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index bbe5ccba..db7d8c8a 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1680,11 +1680,14 @@ static void draw_PlayerUI(iPlayerUI *d, iPaint *p) {
1680 isPaused_Player(d->player) ? dim : bright, 1680 isPaused_Player(d->player) ? dim : bright,
1681 left_Alignment, 1681 left_Alignment,
1682 iRound(playTime)); 1682 iRound(playTime));
1683 int rightWidth = drawSevenSegmentTime_( 1683 int rightWidth = 0;
1684 init_I2(right_Rect(d->scrubberRect) - 2 * gap_UI, yMid - hgt / 2), 1684 if (totalTime > 0) {
1685 dim, 1685 rightWidth =
1686 right_Alignment, 1686 drawSevenSegmentTime_(init_I2(right_Rect(d->scrubberRect) - 2 * gap_UI, yMid - hgt / 2),
1687 iRound(totalTime)); 1687 dim,
1688 right_Alignment,
1689 iRound(totalTime));
1690 }
1688 /* Scrubber. */ 1691 /* Scrubber. */
1689 const int s1 = left_Rect(d->scrubberRect) + leftWidth + 6 * gap_UI; 1692 const int s1 = left_Rect(d->scrubberRect) + leftWidth + 6 * gap_UI;
1690 const int s2 = right_Rect(d->scrubberRect) - rightWidth - 6 * gap_UI; 1693 const int s2 = right_Rect(d->scrubberRect) - rightWidth - 6 * gap_UI;