mplay.flac

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

mplay.flac.c (15933B)


      1#include "mplay.h"
      2
      3#include <FLAC/format.h>
      4#include <FLAC/stream_decoder.h>
      5#include <pulse/sample.h>
      6#include <pulse/introspect.h>
      7#include <pulse/operation.h>
      8#include <pulse/stream.h>
      9#include <pulse/thread-mainloop.h>
     10#include <pulse/volume.h>
     11#include <pulse/pulseaudio.h>
     12#include <pulse/def.h>
     13
     14#include <sys/mman.h>
     15#include <sys/stat.h>
     16#include <pthread.h>
     17#include <fcntl.h>
     18#include <termios.h>
     19#include <unistd.h>
     20
     21#include <stdarg.h>
     22#include <stdint.h>
     23#include <stdbool.h>
     24#include <string.h>
     25#include <stdio.h>
     26#include <stdlib.h>
     27
     28struct decoder {
     29	FLAC__StreamDecoder *flac;
     30	uint8_t *samples;
     31	size_t sample_cnt;
     32	size_t sample_cap;
     33	size_t sample_size;
     34	size_t sample_max;
     35
     36	size_t sample_next;
     37
     38	bool seek;
     39
     40	uint32_t rate;
     41	uint8_t channels;
     42	pa_sample_format_t format;
     43
     44	bool init;
     45	bool pause;
     46	bool done;
     47	bool end;
     48};
     49
     50static pa_buffer_attr pa_buf = {
     51	.fragsize = UINT32_MAX,
     52	.maxlength = UINT32_MAX,
     53	.tlength = UINT32_MAX,
     54	.prebuf = UINT32_MAX,
     55	.minreq = UINT32_MAX,
     56};
     57
     58static pa_stream_flags_t pa_stream_flags = PA_STREAM_START_CORKED
     59	| PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY;
     60
     61static pa_threaded_mainloop *pa_mloop;
     62static pa_context *pa_ctx;
     63static pa_stream *pa_strm;
     64static pa_sink_input_info pa_strm_sink;
     65static bool pa_strm_sink_update;
     66
     67static struct decoder decoder;
     68
     69static pthread_t input_worker_thread;
     70static bool input_worker_alive = false;
     71
     72static struct termios term_old;
     73static struct termios term_new;
     74static bool term_set = false;
     75
     76static bool headless = false;
     77static bool verbose = false;
     78static bool istty = false;
     79
     80static void
     81__attribute__((noreturn))
     82__attribute__((format(printf, 1, 2)))
     83die(const char *fmt, ...)
     84{
     85	va_list ap;
     86
     87	fputs("mplay.flac: ", stderr);
     88	va_start(ap, fmt);
     89	vfprintf(stderr, fmt, ap);
     90	va_end(ap);
     91	if (*fmt && fmt[strlen(fmt)-1] == ':') {
     92		perror(NULL);
     93	} else {
     94		fputc('\n', stderr);
     95	}
     96
     97	exit(1);
     98}
     99
    100static void
    101term_set_canonical(void)
    102{
    103	term_new.c_lflag |= ICANON;
    104	term_new.c_lflag |= ECHO;
    105	if (tcsetattr(0, TCSANOW, &term_new))
    106		die("tcsetattr:");
    107}
    108
    109static void
    110term_set_raw(void)
    111{
    112	term_new.c_lflag &= ~(0U | ICANON);
    113	term_new.c_lflag &= ~(0U | ECHO);
    114	if (tcsetattr(0, TCSANOW, &term_new))
    115		die("tcsetattr:");
    116}
    117
    118static void
    119get_line(char *buf, int size)
    120{
    121	char *c;
    122
    123	if (istty) putc('>', stderr);
    124	if (istty) term_set_canonical();
    125	fgets(buf, size, stdin);
    126	if ((c = strchr(buf, '\n'))) *c = '\0';
    127	if (istty) term_set_raw();
    128}
    129
    130static FLAC__StreamDecoderWriteStatus
    131decoder_write_callback(const FLAC__StreamDecoder *flac,
    132	const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *user)
    133{
    134	uint32_t si, ch;
    135
    136	assert(decoder.sample_next == decoder.sample_cnt);
    137
    138	if (decoder.sample_cnt + frame->header.blocksize > decoder.sample_cap) {
    139		decoder.sample_cap = MAX(decoder.sample_cap * 2,
    140			decoder.sample_cnt + frame->header.blocksize);
    141		decoder.samples = realloc(decoder.samples, decoder.sample_cap
    142				* decoder.channels * decoder.sample_size);
    143		if (!decoder.samples) die("realloc:");
    144	}
    145
    146	if (decoder.format == PA_SAMPLE_S16LE) {
    147		uint16_t *sample = (uint16_t *)decoder.samples
    148			+ decoder.sample_cnt * decoder.channels;
    149		for (si = 0; si < frame->header.blocksize; si++) {
    150			for (ch = 0; ch < frame->header.channels; ch++) {
    151				*sample++ = (uint16_t)htole32((uint32_t)buffer[ch][si]);
    152			}
    153		}
    154	} else if (decoder.format == PA_SAMPLE_S32LE) {
    155		uint32_t *sample = (uint32_t *)decoder.samples
    156			+ decoder.sample_cnt * decoder.channels;
    157		for (si = 0; si < frame->header.blocksize; si++) {
    158			for (ch = 0; ch < frame->header.channels; ch++) {
    159				*sample++ = htole32((uint32_t)buffer[ch][si]);
    160			}
    161		}
    162	}
    163
    164	decoder.sample_cnt += frame->header.blocksize;
    165	decoder.sample_max = MAX(decoder.sample_max, decoder.sample_cnt);
    166
    167	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
    168}
    169
    170static void
    171decoder_metadata_callback(const FLAC__StreamDecoder *flac,
    172	const FLAC__StreamMetadata *metadata, void *user)
    173{
    174	if (!decoder.init && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
    175		decoder.sample_cnt = 0;
    176		decoder.rate = metadata->data.stream_info.sample_rate;
    177		decoder.channels = (uint8_t) metadata->data.stream_info.channels;
    178		decoder.sample_size = metadata->data.stream_info.bits_per_sample / 8;
    179		decoder.sample_max = metadata->data.stream_info.total_samples;
    180		if (decoder.sample_size == 2) {
    181			decoder.format = PA_SAMPLE_S16LE;
    182		} else if (decoder.sample_size == 4) {
    183			decoder.format = PA_SAMPLE_S32LE;
    184		} else {
    185			die("unsupported sample size");
    186		}
    187		decoder.init = true;
    188	}
    189}
    190
    191static void
    192decoder_error_callback(const FLAC__StreamDecoder *flac,
    193	FLAC__StreamDecoderErrorStatus status, void *user)
    194{
    195	die("flac decoding error: %s", FLAC__StreamDecoderErrorStatusString[status]);
    196}
    197
    198static void
    199decoder_flac_decode_next(void)
    200{
    201	bool ok;
    202
    203	ok = FLAC__stream_decoder_process_single(decoder.flac);
    204	FLAC__StreamDecoderState state =
    205		FLAC__stream_decoder_get_state(decoder.flac);
    206	if (!ok) {
    207		die("decode process failed: %s",
    208			FLAC__StreamDecoderStateString[state]);
    209	}
    210	decoder.done = state == FLAC__STREAM_DECODER_END_OF_STREAM;
    211}
    212
    213static uint8_t *
    214decoder_process_frame(size_t *cnt)
    215{
    216	size_t _cnt;
    217
    218	if (decoder.sample_next < decoder.sample_cnt) {
    219		_cnt = decoder.sample_cnt - decoder.sample_next;
    220	} else {
    221		if (decoder.done) {
    222			decoder.end = true;
    223			_cnt = 0;
    224		} else {
    225			decoder_flac_decode_next();
    226			_cnt = decoder.sample_cnt - decoder.sample_next;
    227		}
    228	}
    229
    230	if (cnt) *cnt = _cnt;
    231
    232	return decoder.samples + decoder.sample_next \
    233		* decoder.channels * decoder.sample_size;
    234}
    235
    236static void
    237decoder_init(const char *file)
    238{
    239	FLAC__StreamDecoderInitStatus init_status;
    240
    241	decoder.sample_cnt = 0;
    242	decoder.sample_next = 0;
    243	decoder.sample_cap = 0;
    244	decoder.sample_size = 0;
    245	decoder.samples = NULL;
    246
    247	decoder.seek = false;
    248	decoder.pause = false;
    249	decoder.done = false;
    250	decoder.end = false;
    251
    252	decoder.flac = FLAC__stream_decoder_new();
    253	init_status = FLAC__stream_decoder_init_file(decoder.flac, file,
    254		decoder_write_callback, decoder_metadata_callback,
    255		decoder_error_callback, NULL);
    256	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
    257		die("flac decoder init failed: %s",
    258			FLAC__StreamDecoderInitStatusString[init_status]);
    259	}
    260
    261	/* decode channel specs */
    262	decoder.init = false;
    263	decoder_process_frame(NULL);
    264	assert(decoder.init);
    265}
    266
    267static void
    268decoder_seek(size_t sample_pos)
    269{
    270	size_t cnt;
    271
    272	while (decoder.sample_next < sample_pos) {
    273		decoder_process_frame(&cnt);
    274		if (decoder.end) {
    275			mplay_status(MPLAY_INFO_EXIT, "from seek");
    276			exit(0);
    277		}
    278		decoder.sample_next += cnt;
    279	}
    280	decoder.sample_next = sample_pos;
    281}
    282
    283
    284static void
    285pa_stream_drain_callback(pa_stream *stream, int success, void *data)
    286{
    287	mplay_status(MPLAY_INFO_EXIT, NULL);
    288	exit(0);
    289}
    290
    291static void
    292pa_stream_write_callback(pa_stream *stream, size_t requested, void *data)
    293{
    294	uint8_t *samples;
    295	pa_operation *op;
    296	ssize_t remaining;
    297	size_t cnt, size;
    298
    299	remaining = (ssize_t) requested;
    300	while (remaining > 0) {
    301		samples = decoder_process_frame(&cnt);
    302		if (decoder.end) {
    303			op = pa_stream_drain(pa_strm,
    304				pa_stream_drain_callback, NULL);
    305			pa_operation_unref(op);
    306			return;
    307		}
    308		if (!cnt) continue;
    309
    310		cnt = MIN(cnt, (size_t) remaining);
    311		size = cnt * decoder.channels * decoder.sample_size;
    312		pa_stream_write(stream, samples, size, NULL, 0,
    313			decoder.seek ? PA_SEEK_RELATIVE_ON_READ : PA_SEEK_RELATIVE);
    314		decoder.sample_next += cnt;
    315
    316		decoder.seek = false;
    317		remaining -= (ssize_t) size;
    318	}
    319}
    320
    321static void
    322pa_stream_underflow_callback(struct pa_stream *stream, void *data)
    323{
    324	fprintf(stderr, "pulseaudio underflow!\n");
    325}
    326
    327static void
    328pa_stream_overflow_callback(struct pa_stream *stream, void *data)
    329{
    330	fprintf(stderr, "pulseaudio overflow!\n");
    331}
    332
    333static void
    334update_sink_input_info_callback(struct pa_context *ctx,
    335	const pa_sink_input_info *info, int eol, void *data)
    336{
    337	if (eol) return;
    338	memcpy(&pa_strm_sink, info, sizeof(pa_sink_input_info));
    339	pa_strm_sink_update = true;
    340	pa_threaded_mainloop_signal(pa_mloop, true);
    341}
    342
    343static void
    344update_sink_input_info(void)
    345{
    346	pa_operation *op;
    347
    348	pa_strm_sink_update = false;
    349	op = pa_context_get_sink_input_info(pa_ctx,
    350		pa_stream_get_index(pa_strm),
    351		update_sink_input_info_callback, NULL);
    352	if (!op) die("pa_context_get_sink_input_info failed");
    353
    354	while (!pa_strm_sink_update) {
    355		pa_threaded_mainloop_unlock(pa_mloop);
    356		pa_threaded_mainloop_wait(pa_mloop);
    357		pa_threaded_mainloop_lock(pa_mloop);
    358	}
    359
    360	pa_threaded_mainloop_accept(pa_mloop);
    361
    362	pa_operation_unref(op);
    363}
    364
    365static size_t
    366stream_samples_buffered(void)
    367{
    368	static size_t last_read_index = 0, last_write_index = 0;
    369	const pa_timing_info *info;
    370	size_t buffered;
    371
    372	info = pa_stream_get_timing_info(pa_strm);
    373	if (!info || info->write_index_corrupt || info->read_index_corrupt)
    374		return 0;
    375
    376	if (info->read_index != last_read_index)
    377		last_write_index = (size_t) MAX(0, info->write_index);
    378
    379	buffered = (last_write_index - last_read_index)
    380		/ (decoder.sample_size * decoder.channels);
    381
    382	last_read_index = (size_t) MAX(0, info->read_index);
    383
    384	return buffered;
    385}
    386
    387static double
    388user_vol(void)
    389{
    390	pa_volume_t vol;
    391
    392	update_sink_input_info();
    393	if (!pa_strm_sink.has_volume)
    394		return -1;
    395
    396	vol = pa_cvolume_avg(&pa_strm_sink.volume);
    397
    398	return (double) vol * 100.F / (double) PA_VOLUME_NORM;
    399}
    400
    401static double
    402user_pos(void)
    403{
    404	size_t sample_pos;
    405
    406	sample_pos = (size_t) MAX(0,
    407		(ssize_t) decoder.sample_next - (ssize_t) stream_samples_buffered());
    408
    409	return (double) sample_pos * 1.F / (double) decoder.rate;
    410}
    411
    412static double
    413user_end(void)
    414{
    415	return (double) decoder.sample_max * 1.F / (double) decoder.rate;
    416}
    417
    418static void
    419cmd_setvol(double vol, double delta)
    420{
    421	pa_operation *op;
    422
    423	pa_threaded_mainloop_lock(pa_mloop);
    424
    425	if (delta != 0) {
    426		vol = user_vol() + delta;
    427	}
    428
    429	update_sink_input_info();
    430	pa_cvolume_set(&pa_strm_sink.volume, 2,
    431		(pa_volume_t) (vol * PA_VOLUME_NORM / 100.F));
    432	if (pa_cvolume_avg(&pa_strm_sink.volume) > 2 * PA_VOLUME_NORM)
    433		pa_cvolume_set(&pa_strm_sink.volume, 2, 2 * PA_VOLUME_NORM);
    434
    435	op = pa_context_set_sink_input_volume(pa_ctx,
    436		pa_stream_get_index(pa_strm),
    437		&pa_strm_sink.volume, NULL, NULL);
    438	pa_operation_unref(op);
    439
    440	mplay_status(MPLAY_INFO_VOLUME, "%02.2f", (double) pa_cvolume_avg(&pa_strm_sink.volume)
    441		* 100.f / (double) PA_VOLUME_NORM);
    442	
    443	pa_threaded_mainloop_unlock(pa_mloop);
    444}
    445
    446static void
    447cmd_seek(double time_sec, double delta)
    448{
    449	size_t sample_pos;
    450	pa_operation *op;
    451
    452	pa_threaded_mainloop_lock(pa_mloop);
    453
    454	if (delta != 0) {
    455		time_sec = user_pos() + delta;
    456	}
    457
    458	if (time_sec < 0) time_sec = 0;
    459	sample_pos = (size_t) (time_sec * decoder.rate);
    460	decoder_seek(sample_pos);
    461
    462	op = pa_stream_flush(pa_strm, NULL, NULL);
    463	pa_operation_unref(op);
    464	decoder.seek = true;
    465
    466	mplay_status(MPLAY_INFO_SEEK, "%02.2f", user_pos());
    467
    468	pa_threaded_mainloop_unlock(pa_mloop);
    469}
    470
    471static void
    472cmd_playpause(void)
    473{
    474	pa_operation *op;
    475
    476	pa_threaded_mainloop_lock(pa_mloop);
    477
    478	decoder.pause ^= 1;
    479	op = pa_stream_cork(pa_strm, decoder.pause, NULL, NULL);
    480	pa_operation_unref(op);
    481
    482	mplay_status(MPLAY_INFO_PAUSE, "%i", decoder.pause);
    483
    484	pa_threaded_mainloop_unlock(pa_mloop);
    485}
    486
    487static void
    488cmd_status(void)
    489{
    490	pa_threaded_mainloop_lock(pa_mloop);
    491
    492	update_sink_input_info();
    493
    494	mplay_status(MPLAY_INFO_STATUS,
    495		"volume=%02.2f,pos=%02.2f,end=%02.2f,pause=%i",
    496		user_vol(), user_pos(), user_end(), decoder.pause);
    497
    498	pa_threaded_mainloop_unlock(pa_mloop);
    499}
    500
    501static bool
    502key_input(void)
    503{
    504	char linebuf[16], *end;
    505	float fval;
    506	int key;
    507
    508	while ((key = mplay_getkey()) == MPLAY_KEY_INV);
    509
    510	if (key == MPLAY_KEY_EOF) return false;
    511
    512	switch (mplay_action(key)) {
    513	case MPLAY_ACTION_VOLUME:
    514		get_line(linebuf, sizeof(linebuf));
    515		fval = strtof(linebuf, &end);
    516		if (!end || *end) {
    517			mplay_status(MPLAY_INFO_VOLUME, "fail:bad float");
    518			break;
    519		}
    520		cmd_setvol(fval, 0);
    521		break;
    522	case MPLAY_ACTION_VOLUME_UP:
    523		cmd_setvol(0, 5);
    524		break;
    525	case MPLAY_ACTION_VOLUME_DOWN:
    526		cmd_setvol(0, -5);
    527		break;
    528	case MPLAY_ACTION_SEEK:
    529		get_line(linebuf, sizeof(linebuf));
    530		fval = strtof(linebuf, &end);
    531		if (!end || *end) {
    532			mplay_status(MPLAY_INFO_SEEK, "fail:bad float");
    533			break;
    534		}
    535		cmd_seek(fval, 0);
    536		break;
    537	case MPLAY_ACTION_SEEK_BWD:
    538		cmd_seek(0, -5);
    539		break;
    540	case MPLAY_ACTION_SEEK_FWD:
    541		cmd_seek(0, 5);
    542		break;
    543	case MPLAY_ACTION_PAUSE:
    544		cmd_playpause();
    545		break;
    546	case MPLAY_ACTION_STATUS:
    547		cmd_status();
    548		break;
    549	default:
    550		mplay_status(MPLAY_INFO_INPUT, "fail");
    551		break;
    552	}
    553
    554	return true;
    555}
    556
    557static void *
    558input_worker(void *arg)
    559{
    560	input_worker_alive = true;
    561
    562	setvbuf(stdin, NULL, _IONBF, 0);
    563	setvbuf(stdout, NULL, _IONBF, 0);
    564	setvbuf(stderr, NULL, _IONBF, 0);
    565
    566	if (istty) {
    567		if (tcgetattr(0, &term_old))
    568			die("tcgetattr:");
    569		term_new = term_old;
    570		term_new.c_iflag |= BRKINT;
    571		term_new.c_iflag &= ~(0U | IGNBRK);
    572		term_set = true;
    573
    574		term_set_raw();
    575	}
    576
    577	mplay_status(MPLAY_INFO_READY, NULL);
    578
    579	while (key_input());
    580
    581	return NULL;
    582}
    583
    584static void
    585pulse_context_init(void)
    586{
    587	pa_mainloop_api *pa_mloop_api;
    588	int ret;
    589
    590	pa_mloop_api = pa_threaded_mainloop_get_api(pa_mloop);
    591	if (!pa_mloop_api) die("pa_threaded_mainloop_get_api");
    592
    593	pa_ctx = pa_context_new(pa_mloop_api, "mplay.flac");
    594	if (!pa_ctx) die("pa_context_new");
    595
    596	ret = pa_context_connect(pa_ctx, NULL, 0, NULL);
    597	if (ret) die("pa_context_connect: %s",
    598		pa_strerror(pa_context_errno(pa_ctx)));
    599
    600	while (pa_context_get_state(pa_ctx) != PA_CONTEXT_READY) {
    601		pa_threaded_mainloop_unlock(pa_mloop);
    602		pa_threaded_mainloop_wait(pa_mloop);
    603		pa_threaded_mainloop_lock(pa_mloop);
    604	}
    605}
    606
    607static void
    608pulse_stream_init(void)
    609{
    610	struct pa_sample_spec pa_spec;
    611	pa_channel_map pa_chmap;
    612	int ret;
    613
    614	pa_channel_map_init_stereo(&pa_chmap);
    615
    616	pa_spec.channels = decoder.channels;
    617	pa_spec.rate = decoder.rate;
    618	pa_spec.format = decoder.format;
    619
    620	pa_strm = pa_stream_new(pa_ctx, "mplay.flac", &pa_spec, &pa_chmap);
    621	if (!pa_strm) die("pa_stream_new: %s",
    622		pa_strerror(pa_context_errno(pa_ctx)));
    623
    624	pa_stream_set_write_callback(pa_strm, pa_stream_write_callback, NULL);
    625
    626	if (verbose) {
    627		pa_stream_set_underflow_callback(pa_strm,
    628			pa_stream_underflow_callback, NULL);
    629		pa_stream_set_overflow_callback(pa_strm,
    630			pa_stream_overflow_callback, NULL);
    631	}
    632
    633	ret = pa_stream_connect_playback(pa_strm, NULL,
    634		&pa_buf, pa_stream_flags, NULL, NULL);
    635	if (ret) die("pa_stream_connect_playback failed");
    636
    637	while (pa_stream_get_state(pa_strm) != PA_STREAM_READY) {
    638		pa_threaded_mainloop_unlock(pa_mloop);
    639		pa_threaded_mainloop_wait(pa_mloop);
    640		pa_threaded_mainloop_lock(pa_mloop);
    641	}
    642}
    643
    644static void
    645sigint_handler(int sig)
    646{
    647	exit(0);
    648}
    649
    650static void
    651cleanup(void)
    652{
    653	if (term_set) tcsetattr(0, TCSANOW, &term_old);
    654
    655	pa_stream_disconnect(pa_strm);
    656	pa_stream_unref(pa_strm);
    657
    658	pa_context_disconnect(pa_ctx);
    659	pa_context_unref(pa_ctx);
    660
    661	if (input_worker_alive)
    662		pthread_kill(input_worker_thread, SIGINT);
    663}
    664
    665static void
    666usage(void)
    667{
    668	fprintf(stderr, "Usage: mplay.flac [-v] [" MPLAY_FLAG_HEADLESS "] FILE\n");
    669	exit(1);
    670}
    671
    672int
    673main(int argc, const char **argv)
    674{
    675	pthread_mutex_t lock;
    676	const char **arg;
    677	const char *file;
    678
    679	file = NULL;
    680	for (arg = argv + 1; *arg; arg++) {
    681		if (!strcmp(*arg, MPLAY_FLAG_HEADLESS)) {
    682			headless = true;
    683		} else if (!strcmp(*arg, "-v")) {
    684			verbose = true;
    685		} else {
    686			if (file) usage();
    687			file = *arg;
    688		}
    689	}
    690	if (!file) usage();
    691
    692	istty = isatty(0);
    693
    694	decoder_init(file);
    695
    696	pa_mloop = pa_threaded_mainloop_new();
    697	if (!pa_mloop) die("pa_threaded_mainloop_new");
    698
    699	pa_threaded_mainloop_start(pa_mloop);
    700
    701	pa_threaded_mainloop_lock(pa_mloop);
    702	pulse_context_init();
    703	pulse_stream_init();
    704	pa_stream_cork(pa_strm, 0, NULL, NULL);
    705	pa_threaded_mainloop_unlock(pa_mloop);
    706
    707	atexit(cleanup);
    708	signal(SIGINT, sigint_handler);
    709
    710	if (!headless) {
    711		if (pthread_create(&input_worker_thread, NULL, input_worker, NULL))
    712			die("pthread_create");
    713		pthread_join(input_worker_thread, NULL);
    714	} else {
    715		pthread_mutex_init(&lock, NULL);
    716		pthread_mutex_lock(&lock);
    717		pthread_mutex_lock(&lock);
    718	}
    719
    720	return 0;
    721}