summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-10-14 22:19:21 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-10-14 22:19:21 +0300
commitb073e4c6aad0a4c35ea89c4592d5b48a39bb61f5 (patch)
tree4c35506cd40ba0af0ef3c1d526435b8abd1b9f70 /src/audio
parent625e3046cc636db5b0325b96b4c22610252a5ab4 (diff)
Use libmpg123 to decode MPEG audio
mpg123 is configured as an optional dependency. Works for full files currently.
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/player.c111
1 files changed, 100 insertions, 11 deletions
diff --git a/src/audio/player.c b/src/audio/player.c
index b2a6ae15..ef238378 100644
--- a/src/audio/player.c
+++ b/src/audio/player.c
@@ -30,6 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30#include <the_Foundation/thread.h> 30#include <the_Foundation/thread.h>
31#include <SDL_audio.h> 31#include <SDL_audio.h>
32 32
33#if defined (LAGRANGE_ENABLE_MPG123)
34# include <mpg123.h>
35#endif
36
33/*----------------------------------------------------------------------------------------------*/ 37/*----------------------------------------------------------------------------------------------*/
34 38
35iDeclareType(ContentSpec) 39iDeclareType(ContentSpec)
@@ -68,6 +72,9 @@ struct Impl_Decoder {
68 uint64_t currentSample; 72 uint64_t currentSample;
69 uint64_t totalSamples; /* zero if unknown */ 73 uint64_t totalSamples; /* zero if unknown */
70 stb_vorbis * vorbis; 74 stb_vorbis * vorbis;
75#if defined (LAGRANGE_ENABLE_MPG123)
76 mpg123_handle * mpeg;
77#endif
71}; 78};
72 79
73enum iDecoderStatus { 80enum iDecoderStatus {
@@ -155,8 +162,18 @@ static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) {
155 return ok_DecoderStatus; 162 return ok_DecoderStatus;
156} 163}
157 164
158static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d, iRanges inputRange) { 165static void writePending_Decoder_(iDecoder *d) {
159 iUnused(inputRange); 166 /* Write as much as we can. */
167 lock_Mutex(&d->outputMutex);
168 size_t avail = vacancy_SampleBuf(&d->output);
169 size_t n = iMin(avail, size_Array(&d->pendingOutput));
170 write_SampleBuf(&d->output, constData_Array(&d->pendingOutput), n);
171 removeN_Array(&d->pendingOutput, 0, n);
172 unlock_Mutex(&d->outputMutex);
173 d->currentSample += n;
174}
175
176static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d) {
160 const iBlock *input = &d->input->data; 177 const iBlock *input = &d->input->data;
161 if (!d->vorbis) { 178 if (!d->vorbis) {
162 lock_Mutex(&d->input->mtx); 179 lock_Mutex(&d->input->mtx);
@@ -213,14 +230,47 @@ static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d, iRanges inputRange
213 } 230 }
214 } 231 }
215 } 232 }
216 /* Write as much as we can. */ 233 writePending_Decoder_(d);
217 lock_Mutex(&d->outputMutex); 234 return status;
218 size_t avail = vacancy_SampleBuf(&d->output); 235}
219 size_t n = iMin(avail, size_Array(&d->pendingOutput)); 236
220 write_SampleBuf(&d->output, constData_Array(&d->pendingOutput), n); 237enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) {
221 removeN_Array(&d->pendingOutput, 0, n); 238 enum iDecoderStatus status = ok_DecoderStatus;
222 unlock_Mutex(&d->outputMutex); 239#if defined (LAGRANGE_ENABLE_MPG123)
223 d->currentSample += n; 240 const iBlock *input = &d->input->data;
241 if (!d->mpeg) {
242 d->mpeg = mpg123_new(NULL, NULL);
243 mpg123_format_none(d->mpeg);
244 mpg123_format(d->mpeg, d->outputFreq, d->output.numChannels, MPG123_ENC_SIGNED_16);
245 mpg123_open_feed(d->mpeg);
246 lock_Mutex(&d->input->mtx);
247 mpg123_feed(d->mpeg, constData_Block(input), size_Block(input));
248 unlock_Mutex(&d->input->mtx);
249 long r; int ch, enc;
250 mpg123_getformat(d->mpeg, &r, &ch, &enc);
251 iAssert(r == d->outputFreq);
252 iAssert(ch == d->output.numChannels);
253 iAssert(enc == MPG123_ENC_SIGNED_16);
254 }
255 while (size_Array(&d->pendingOutput) < d->output.count) {
256 int16_t buffer[512];
257 size_t bytesWritten = 0;
258 const int rc = mpg123_read(d->mpeg, buffer, sizeof(buffer), &bytesWritten);
259 const float gain = d->gain;
260 for (size_t i = 0; i < bytesWritten / 2; i++) {
261 buffer[i] *= gain;
262 }
263 pushBackN_Array(&d->pendingOutput, buffer, bytesWritten / 2 / d->output.numChannels);
264 if (rc == MPG123_NEED_MORE) {
265 status = needMoreInput_DecoderStatus;
266 break;
267 }
268 else if (rc == MPG123_DONE) {
269 break;
270 }
271 }
272 writePending_Decoder_(d);
273#endif
224 return status; 274 return status;
225} 275}
226 276
@@ -242,7 +292,11 @@ static iThreadResult run_Decoder_(iThread *thread) {
242 status = decodeWav_Decoder_(d, inputRange); 292 status = decodeWav_Decoder_(d, inputRange);
243 break; 293 break;
244 case vorbis_DecoderType: 294 case vorbis_DecoderType:
245 status = decodeVorbis_Decoder_(d, inputRange); 295 status = decodeVorbis_Decoder_(d);
296 break;
297 case mpeg_DecoderType:
298 status = decodeMpeg_Decoder_(d);
299 break;
246 default: 300 default:
247 break; 301 break;
248 } 302 }
@@ -280,6 +334,9 @@ void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) {
280 d->currentSample = 0; 334 d->currentSample = 0;
281 d->totalSamples = spec->totalSamples; 335 d->totalSamples = spec->totalSamples;
282 d->vorbis = NULL; 336 d->vorbis = NULL;
337#if defined (LAGRANGE_ENABLE_MPG123)
338 d->mpeg = NULL;
339#endif
283 init_Mutex(&d->outputMutex); 340 init_Mutex(&d->outputMutex);
284 d->thread = new_Thread(run_Decoder_); 341 d->thread = new_Thread(run_Decoder_);
285 setUserData_Thread(d->thread, d); 342 setUserData_Thread(d->thread, d);
@@ -295,6 +352,15 @@ void deinit_Decoder(iDecoder *d) {
295 deinit_Mutex(&d->outputMutex); 352 deinit_Mutex(&d->outputMutex);
296 deinit_SampleBuf(&d->output); 353 deinit_SampleBuf(&d->output);
297 deinit_Array(&d->pendingOutput); 354 deinit_Array(&d->pendingOutput);
355 if (d->vorbis) {
356 stb_vorbis_close(d->vorbis);
357 }
358#if defined (LAGRANGE_ENABLE_MPG123)
359 if (d->mpeg) {
360 mpg123_close(d->mpeg);
361 mpg123_delete(d->mpeg);
362 }
363#endif
298} 364}
299 365
300iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec), 366iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec),
@@ -336,6 +402,11 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
336 !cmp_String(&d->mime, "audio/x-vorbis+ogg")) { 402 !cmp_String(&d->mime, "audio/x-vorbis+ogg")) {
337 content.type = vorbis_DecoderType; 403 content.type = vorbis_DecoderType;
338 } 404 }
405#if defined (LAGRANGE_ENABLE_MPG123)
406 else if (!cmp_String(&d->mime, "audio/mpeg") || !cmp_String(&d->mime, "audio/mp3")) {
407 content.type = mpeg_DecoderType;
408 }
409#endif
339 else { 410 else {
340 /* TODO: Could try decoders to see if one works? */ 411 /* TODO: Could try decoders to see if one works? */
341 content.type = none_DecoderType; 412 content.type = none_DecoderType;
@@ -438,6 +509,24 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
438 content.inputFormat = AUDIO_F32; /* actually stb_vorbis provides floats */ 509 content.inputFormat = AUDIO_F32; /* actually stb_vorbis provides floats */
439 stb_vorbis_close(vrb); 510 stb_vorbis_close(vrb);
440 } 511 }
512 else if (content.type == mpeg_DecoderType) {
513#if defined (LAGRANGE_ENABLE_MPG123)
514 mpg123_handle *mh = mpg123_new(NULL, NULL);
515 mpg123_open_feed(mh);
516 mpg123_feed(mh, constData_Block(&d->data->data), size_Block(&d->data->data));
517 long rate = 0;
518 int channels = 0;
519 int encoding = 0;
520 if (mpg123_getformat(mh, &rate, &channels, &encoding) == MPG123_OK) {
521 content.output.freq = rate;
522 content.output.channels = channels;
523 content.inputFormat = AUDIO_S16;
524 content.output.format = AUDIO_S16;
525 }
526 mpg123_close(mh);
527 mpg123_delete(mh);
528#endif
529 }
441 iAssert(content.inputFormat == content.output.format || 530 iAssert(content.inputFormat == content.output.format ||
442 (content.inputFormat == AUDIO_S24LSB && content.output.format == AUDIO_S16) || 531 (content.inputFormat == AUDIO_S24LSB && content.output.format == AUDIO_S16) ||
443 (content.inputFormat == AUDIO_F64LSB && content.output.format == AUDIO_F32)); 532 (content.inputFormat == AUDIO_F64LSB && content.output.format == AUDIO_F32));