mplay.mp3

Controllable music player (mp3)
git clone https://git.sinitax.com/sinitax/mplay.mp3
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

commit 59883095b00ba55ce7441a22e8de6971059e0041
parent ac7ab7067e81f8ab3748de26acefc4c8f31ec8ea
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed, 13 Mar 2024 20:34:43 +0100

Let minimp3 handle file mapping and extracting mp3 metadata

Diffstat:
Mmplay.mp3.c | 177++++++++++++++++---------------------------------------------------------------
1 file changed, 35 insertions(+), 142 deletions(-)

diff --git a/mplay.mp3.c b/mplay.mp3.c @@ -1,7 +1,7 @@ #include "mplay.h" #define MINIMP3_IMPLEMENTATION -#include "minimp3.h" +#include "minimp3_ex.h" #include <pulse/sample.h> #include <pulse/introspect.h> @@ -25,38 +25,20 @@ #include <stdio.h> #include <stdlib.h> -struct audiofile { - uint8_t *data; - size_t len; -}; - struct decoder { - mp3dec_t dec; - uint8_t *pos; - ssize_t left; - - mp3d_sample_t *samples; - size_t sample_cnt; - size_t sample_cap; - size_t sample_max; - + mp3dec_ex_t dec; size_t sample_next; - - bool seek; + size_t samples_max; + size_t samples_read; int rate; int channels; + bool seek; bool init; bool pause; }; -static pa_sample_spec pa_spec = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 -}; - static pa_buffer_attr pa_buf = { .fragsize = UINT32_MAX, .maxlength = UINT32_MAX, @@ -74,8 +56,6 @@ static pa_stream *pa_strm; static pa_sink_input_info pa_strm_sink; static bool pa_strm_sink_update; -static struct audiofile audiofile = { 0 }; - static struct decoder decoder = { 0 }; static pthread_t input_worker_thread; @@ -139,99 +119,18 @@ get_line(char *buf, int size) if (istty) term_set_raw(); } -static uint8_t * -map_file(const char *path, size_t *len) -{ - struct stat attr; - uint8_t *buf; - int fd; - - fd = open(path, O_RDONLY); - if (fd < 0) die("open %s:", path); - - if (fstat(fd, &attr)) - die("fstat %s:", path); - - if ((attr.st_mode & S_IFMT) == S_IFDIR) - die("not a file: %s", path); - - buf = mmap(NULL, (size_t) attr.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (!buf) die("mmap %s:", path); - - *len = (size_t) attr.st_size; - - close(fd); - - return buf; -} - -static size_t -decode_next_frame(mp3d_sample_t *samples, size_t *size) -{ - static const size_t max_cnt = MINIMP3_MAX_SAMPLES_PER_FRAME; - mp3dec_frame_info_t info; - size_t cnt; - - if (size) *size = 0; - - if (decoder.sample_next < decoder.sample_cnt) { - cnt = MIN(max_cnt, decoder.sample_cnt - decoder.sample_next); - *size = cnt * sizeof(mp3d_sample_t); - } else { - if (decoder.sample_cnt + max_cnt > decoder.sample_cap) { - decoder.sample_cap = MAX(decoder.sample_cap * 2, - decoder.sample_cnt + max_cnt); - decoder.samples = realloc(decoder.samples, - decoder.sample_cap * sizeof(mp3d_sample_t)); - if (!decoder.samples) die("realloc:"); - } - - cnt = (size_t) mp3dec_decode_frame(&decoder.dec, decoder.pos, - (int) decoder.left, &decoder.samples[decoder.sample_cnt], &info); - cnt *= (size_t) info.channels; - decoder.sample_cnt += cnt; - - if (size) *size = (size_t) info.frame_bytes; - - if (!info.frame_bytes) - return 0; - - if (!decoder.init) { - decoder.rate = info.hz; - decoder.channels = info.channels; - } - - decoder.pos += info.frame_bytes; - decoder.left -= info.frame_bytes; - } - - if (samples != NULL) - memcpy(samples, &decoder.samples[decoder.sample_next], - cnt * sizeof(mp3d_sample_t)); - decoder.sample_next += cnt; - - return cnt; -} - static void -decoder_init(void) +decoder_init(const char *filepath) { - mp3d_sample_t samples[MINIMP3_MAX_SAMPLES_PER_FRAME]; - size_t size; - - mp3dec_init(&decoder.dec); - decoder.pos = audiofile.data; - decoder.left = (ssize_t) audiofile.len; + if (mp3dec_ex_open(&decoder.dec, filepath, MP3D_SEEK_TO_SAMPLE)) + die("mp3dec_ex_open"); - decoder.sample_cnt = 0; decoder.sample_next = 0; - decoder.sample_cap = MINIMP3_MAX_SAMPLES_PER_FRAME; - decoder.samples = malloc(decoder.sample_cap * sizeof(mp3d_sample_t)); - if (!decoder.samples) die("malloc:"); + decoder.samples_max = 0; + decoder.samples_max = decoder.dec.samples / (size_t)decoder.dec.info.channels; - /* decode channel specs */ - decode_next_frame(samples, &size); - if (!size) die("invalid mp3"); + decoder.channels = decoder.dec.info.channels; + decoder.rate = decoder.dec.info.hz; decoder.seek = false; decoder.pause = false; @@ -241,15 +140,7 @@ decoder_init(void) static void decoder_seek(size_t sample_pos) { - size_t size, cnt; - - while (decoder.sample_next < sample_pos) { - cnt = decode_next_frame(NULL, &size); - if (!size) { - mplay_status(MPLAY_INFO_EXIT, "from seek"); - exit(0); - } - } + mp3dec_ex_seek(&decoder.dec, sample_pos * (size_t)decoder.channels); decoder.sample_next = sample_pos; } @@ -263,28 +154,31 @@ pa_stream_drain_callback(pa_stream *stream, int success, void *data) static void pa_stream_write_callback(pa_stream *stream, size_t requested, void *data) { - mp3d_sample_t samples[MINIMP3_MAX_SAMPLES_PER_FRAME]; + mp3dec_frame_info_t info; + mp3d_sample_t *samples; pa_operation *op; ssize_t remaining; - size_t cnt, size; + size_t cnt; remaining = (ssize_t) requested; while (remaining > 0) { - cnt = decode_next_frame(samples, &size); - if (!size) { + cnt = mp3dec_ex_read_frame(&decoder.dec, &samples, &info, requested); + if (!cnt) { op = pa_stream_drain(pa_strm, pa_stream_drain_callback, NULL); pa_operation_unref(op); return; } - if (!cnt) continue; pa_stream_write(stream, samples, cnt * sizeof(mp3d_sample_t), NULL, 0, decoder.seek ? PA_SEEK_RELATIVE_ON_READ: PA_SEEK_RELATIVE); decoder.seek = false; - remaining -= (ssize_t) (cnt * sizeof(mp3d_sample_t)); + decoder.sample_next += cnt / (size_t)decoder.channels; + decoder.samples_read = decoder.sample_next; + decoder.samples_max = MAX(decoder.samples_max, decoder.samples_read); + remaining -= (ssize_t) (cnt * (size_t)decoder.channels * sizeof(mp3d_sample_t)); } } @@ -347,7 +241,7 @@ stream_samples_buffered(void) last_write_index = (size_t) MAX(0, info->write_index); buffered = (last_write_index - last_read_index) - / sizeof(mp3d_sample_t); + / ((size_t) decoder.channels * sizeof(mp3d_sample_t)); last_read_index = (size_t) MAX(0, info->read_index); @@ -371,19 +265,19 @@ user_vol(void) static double user_pos(void) { - size_t sample_pos; + ssize_t sample_pos; - sample_pos = (size_t) MAX(0, - (ssize_t) decoder.sample_next - (ssize_t) stream_samples_buffered()); + sample_pos = (ssize_t) decoder.samples_read; + sample_pos -= (ssize_t) stream_samples_buffered(); + sample_pos = MAX(0, sample_pos); - return (double) sample_pos * 1.F / (double) (decoder.rate * decoder.channels); + return (double) sample_pos * 1.F / (double) decoder.rate; } static double user_end(void) { - return (double) decoder.sample_max * 1.F - / (double) (decoder.rate * decoder.channels); + return (double) decoder.samples_max * 1.F / (double) decoder.rate; } static void @@ -577,8 +471,13 @@ static void pulse_stream_init(void) { pa_channel_map pa_chmap; + pa_sample_spec pa_spec; int ret; + pa_spec.format = PA_SAMPLE_S16LE; + pa_spec.channels = (uint8_t) decoder.channels; + pa_spec.rate = (uint32_t) decoder.rate; + pa_channel_map_init_stereo(&pa_chmap); pa_strm = pa_stream_new(pa_ctx, "mplay.mp3", &pa_spec, &pa_chmap); @@ -622,8 +521,6 @@ cleanup(void) pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); - munmap(audiofile.data, audiofile.len); - if (input_worker_alive) pthread_kill(input_worker_thread, SIGINT); } @@ -657,11 +554,7 @@ main(int argc, const char **argv) istty = isatty(0); - audiofile.data = map_file(file, &audiofile.len); - decoder_init(); - - pa_spec.channels = (uint8_t) decoder.channels; - pa_spec.rate = (uint32_t) decoder.rate; + decoder_init(file); pa_mloop = pa_threaded_mainloop_new(); if (!pa_mloop) die("pa_threaded_mainloop_new");