diff --git a/run-fb.sh b/run-fb.sh deleted file mode 100755 index 68679ef..0000000 --- a/run-fb.sh +++ /dev/null @@ -1,2 +0,0 @@ -export LIBGL_ALWAYS_SOFTWARE=1 -python3 app.py \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..9741899 --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +exec bin/player music/pink\ floyd/dark\ side\ of\ the\ moon/01\ Speak\ to\ Me.flac music/pink\ floyd/dark\ side\ of\ the\ moon/02\ Breathe\ \(In\ the\ Air\).flac \ No newline at end of file diff --git a/src/main.c b/src/main.c index 80596c7..01cf7ca 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,4 @@ -// player.c -#include +#include #include #include #include @@ -14,166 +13,165 @@ #define OUT_RATE 48000 #define OUT_CHANNELS 2 #define OUT_FORMAT AV_SAMPLE_FMT_S16 -#define OUT_LAYOUT AV_CH_LAYOUT_STEREO +#define PRELOAD_SEC 10 -int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr, "usage: %s \n", argv[0]); - return 1; - } +typedef struct { + AVFormatContext *fmt; + AVCodecContext *dec; + SwrContext *swr; + AVPacket *pkt; + AVFrame *frm; + int stream; + int64_t duration_samples; + int64_t played_samples; + int eof; +} Decoder; - const char *filename = argv[1]; +/* ---------- Decoder helpers ---------- */ - /* ---------- Open input ---------- */ +static int decoder_open(Decoder *d, const char *path) { + memset(d, 0, sizeof(*d)); - AVFormatContext *fmt = NULL; - if (avformat_open_input(&fmt, filename, NULL, NULL) < 0) { - fprintf(stderr, "failed to open input\n"); - return 1; - } + if (avformat_open_input(&d->fmt, path, NULL, NULL) < 0) + return -1; - if (avformat_find_stream_info(fmt, NULL) < 0) { - fprintf(stderr, "failed to read stream info\n"); - return 1; - } + avformat_find_stream_info(d->fmt, NULL); - /* ---------- Find audio stream ---------- */ - - int stream_index = -1; - for (unsigned i = 0; i < fmt->nb_streams; i++) { - if (fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - stream_index = i; + for (unsigned i = 0; i < d->fmt->nb_streams; i++) { + if (d->fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + d->stream = i; break; } } - if (stream_index < 0) { - fprintf(stderr, "no audio stream found\n"); - return 1; - } + AVStream *s = d->fmt->streams[d->stream]; + const AVCodec *codec = avcodec_find_decoder(s->codecpar->codec_id); - AVStream *stream = fmt->streams[stream_index]; + d->dec = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(d->dec, s->codecpar); + avcodec_open2(d->dec, codec, NULL); - /* ---------- Decoder ---------- */ + d->swr = swr_alloc(); + AVChannelLayout out = AV_CHANNEL_LAYOUT_STEREO; - const AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id); - if (!codec) { - fprintf(stderr, "decoder not found\n"); - return 1; - } + av_opt_set_chlayout(d->swr, "out_chlayout", &out, 0); + av_opt_set_int(d->swr, "out_sample_rate", OUT_RATE, 0); + av_opt_set_sample_fmt(d->swr, "out_sample_fmt", OUT_FORMAT, 0); - AVCodecContext *dec = avcodec_alloc_context3(codec); - avcodec_parameters_to_context(dec, stream->codecpar); + av_opt_set_chlayout(d->swr, "in_chlayout", &d->dec->ch_layout, 0); + av_opt_set_int(d->swr, "in_sample_rate", d->dec->sample_rate, 0); + av_opt_set_sample_fmt(d->swr, "in_sample_fmt", d->dec->sample_fmt, 0); - if (avcodec_open2(dec, codec, NULL) < 0) { - fprintf(stderr, "failed to open decoder\n"); - return 1; - } + swr_init(d->swr); - /* ---------- Resampler ---------- */ + if (s->duration != AV_NOPTS_VALUE) + d->duration_samples = + av_rescale_q(s->duration, s->time_base, (AVRational){1, OUT_RATE}); - SwrContext *swr = swr_alloc(); - if (!swr) { - fprintf(stderr, "failed to alloc swr\n"); - return 1; - } - - AVChannelLayout out_ch_layout; - av_channel_layout_default(&out_ch_layout, OUT_CHANNELS); - - av_opt_set_chlayout(swr, "out_chlayout", &out_ch_layout, 0); - av_opt_set_int(swr, "out_sample_rate", OUT_RATE, 0); - av_opt_set_sample_fmt(swr, "out_sample_fmt", OUT_FORMAT, 0); - - av_opt_set_chlayout(swr, "in_chlayout", &dec->ch_layout, 0); - av_opt_set_int(swr, "in_sample_rate", dec->sample_rate, 0); - av_opt_set_sample_fmt(swr, "in_sample_fmt", dec->sample_fmt, 0); - - if (swr_init(swr) < 0) { - fprintf(stderr, "failed to init swr\n"); - return 1; - } - - if (!swr || swr_init(swr) < 0) { - fprintf(stderr, "failed to init resampler\n"); - return 1; - } - - /* ---------- ALSA ---------- */ - - snd_pcm_t *pcm; - if (snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { - fprintf(stderr, "failed to open ALSA device\n"); - return 1; - } - - snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, - OUT_CHANNELS, OUT_RATE, 1, - 500000 // 0.5s latency - ); - - /* ---------- Buffers ---------- */ - - AVPacket *pkt = av_packet_alloc(); - AVFrame *frm = av_frame_alloc(); - - uint8_t *outbuf = NULL; - int out_linesize = 0; - int max_samples = 0; - - /* ---------- Decode loop ---------- */ - - while (av_read_frame(fmt, pkt) >= 0) { - if (pkt->stream_index != stream_index) { - av_packet_unref(pkt); - continue; - } - - avcodec_send_packet(dec, pkt); - - while (avcodec_receive_frame(dec, frm) == 0) { - int needed = - av_rescale_rnd(swr_get_delay(swr, dec->sample_rate) + frm->nb_samples, - OUT_RATE, dec->sample_rate, AV_ROUND_UP); - - if (needed > max_samples) { - av_freep(&outbuf); - av_samples_alloc(&outbuf, &out_linesize, OUT_CHANNELS, needed, - OUT_FORMAT, 1); - max_samples = needed; - } - - int samples = swr_convert(swr, &outbuf, needed, - (const uint8_t **)frm->data, frm->nb_samples); - - int written = snd_pcm_writei(pcm, outbuf, samples); - if (written < 0) - snd_pcm_recover(pcm, written, 1); - } - - av_packet_unref(pkt); - } - - /* ---------- Flush ---------- */ - - avcodec_send_packet(dec, NULL); - while (avcodec_receive_frame(dec, frm) == 0) { - int samples = swr_convert(swr, &outbuf, max_samples, - (const uint8_t **)frm->data, frm->nb_samples); - snd_pcm_writei(pcm, outbuf, samples); - } - - snd_pcm_drain(pcm); - - /* ---------- Cleanup ---------- */ - - av_freep(&outbuf); - av_frame_free(&frm); - av_packet_free(&pkt); - swr_free(&swr); - avcodec_free_context(&dec); - avformat_close_input(&fmt); - snd_pcm_close(pcm); + d->pkt = av_packet_alloc(); + d->frm = av_frame_alloc(); return 0; } + +static void decoder_close(Decoder *d) { + if (!d) + return; + av_packet_free(&d->pkt); + av_frame_free(&d->frm); + swr_free(&d->swr); + avcodec_free_context(&d->dec); + avformat_close_input(&d->fmt); +} + +/* ---------- Decode PCM ---------- */ + +static int decode_pcm(Decoder *d, uint8_t **out, int max_samples) { + while (1) { + if (!d->eof) { + if (av_read_frame(d->fmt, d->pkt) < 0) { + avcodec_send_packet(d->dec, NULL); + d->eof = 1; + } else if (d->pkt->stream_index == d->stream) { + avcodec_send_packet(d->dec, d->pkt); + } + av_packet_unref(d->pkt); + } + + if (avcodec_receive_frame(d->dec, d->frm) == 0) { + int samples = + swr_convert(d->swr, out, max_samples, (const uint8_t **)d->frm->data, + d->frm->nb_samples); + d->played_samples += samples; + return samples; + } + + if (d->eof) + return 0; + } +} + +/* ---------- Main ---------- */ + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "usage: %s track1 track2 ...\n", argv[0]); + return 1; + } + + snd_pcm_t *pcm; + snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0); + snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, + OUT_CHANNELS, OUT_RATE, 1, 500000); + + Decoder cur = {0}, next = {0}; + int track = 1; + int next_loaded = 0; + + decoder_open(&cur, argv[track++]); + + uint8_t *buf = NULL; + int linesize = 0; + int max_samples = OUT_RATE * 2; + + av_samples_alloc(&buf, &linesize, OUT_CHANNELS, max_samples, OUT_FORMAT, 1); + + while (1) { + int samples = decode_pcm(&cur, &buf, max_samples); + + if (samples > 0) { + snd_pcm_writei(pcm, buf, samples); + } + + if (!next_loaded && cur.duration_samples && + cur.duration_samples - cur.played_samples < PRELOAD_SEC * OUT_RATE && + track < argc) { + decoder_open(&next, argv[track++]); + next_loaded = 1; + } + + if (samples < max_samples && cur.eof) { + if (next_loaded) { + decoder_close(&cur); + cur = next; + memset(&next, 0, sizeof(next)); + next_loaded = 0; + int samples_needed = max_samples - samples; + samples = decode_pcm(&cur, &buf, samples_needed); + printf("yo read %d samples\n", samples); + if (samples > 0) { + snd_pcm_writei(pcm, buf, samples); + } + continue; + } + break; + } + } + + snd_pcm_drain(pcm); + snd_pcm_close(pcm); + + decoder_close(&cur); + av_freep(&buf); + return 0; +}