map

minimalist audio player
git clone https://git.porkepik.fr/map
Log | Files | Refs | README | LICENSE

commit cd18d494d2f9ecf39183bf8774acbf96384125d1
parent 021ebddb542a45331491fb1c18a7f37b5c73a8d8
Author: Thomas Philippe <dev@porkepik.fr>
Date:   Wed,  9 Sep 2020 16:39:27 +0200

switch to libsndfile

Diffstat:
MMakefile | 4++--
MREADME | 2+-
Aalsa.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mflac.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mmap.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
5 files changed, 243 insertions(+), 59 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ .POSIX: PREFIX = /usr/local CFLAGS = -O2 -std=c99 -D_DEFAULT_SOURCE -Wall -LDLIBS = -lsndio -OBJ = flac.o map.o mp3.o sndio.o term.o +LDLIBS = -lasound -lsndfile +OBJ = alsa.o flac.o map.o mp3.o term.o BIN = map $(BIN): $(OBJ) diff --git a/README b/README @@ -1,7 +1,7 @@ map - minimalist audio player ============================= -Requires sndio backend. +ALSA only. Supports FLAC (dr_flac) and MP3 (dr_mp3). controls diff --git a/alsa.c b/alsa.c @@ -0,0 +1,115 @@ +#include <alsa/asoundlib.h> + +static snd_pcm_t *hdl; +static snd_mixer_t *mixerhdl; +static snd_mixer_selem_id_t *sid; +static snd_mixer_elem_t *elem; +static int cur_bits; +static int cur_rate; +static int cur_chan; + +int +alsa_init(void) +{ + if (snd_mixer_open(&mixerhdl, 0) < 0) + return -1; + + if (snd_mixer_attach(mixerhdl, "default") < 0) + goto err0; + + if (snd_mixer_selem_register(mixerhdl, NULL, NULL) < 0) + goto err0; + + if (snd_mixer_load(mixerhdl) < 0) + goto err0; + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, "Master"); + + if (!(elem = snd_mixer_find_selem(mixerhdl, sid))) + goto err0; + + return 0; +err0: + snd_mixer_close(mixerhdl); + return -1; +} + +int +alsa_vol(long vol) +{ + long min, max; + + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + if (vol) + snd_mixer_selem_set_playback_volume_all(elem, vol * max / 100); + else + snd_mixer_selem_get_playback_volume(elem, 0, &vol); + + return vol * 100 / max; +} + +void +alsa_close(void) +{ + if (hdl) + snd_pcm_close(hdl); + hdl = NULL; + + snd_mixer_close(mixerhdl); +} + +int +alsa_open(int bits, int rate, int channels) +{ + snd_pcm_format_t format; + + if (bits == cur_bits && rate == cur_rate && channels == cur_chan) + return 0; + + alsa_close(); + alsa_init(); + + /*switch (bits) { + case 16: + format = SND_PCM_FORMAT_S16; + break; + case 24: + format = SND_PCM_FORMAT_S24; + break; + case 32: + format = SND_PCM_FORMAT_S32; + break; + default: + return -1; + }*/ + + format = SND_PCM_FORMAT_FLOAT; + if (snd_pcm_open(&hdl, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) + return -1; + + if (snd_pcm_set_params(hdl, format, SND_PCM_ACCESS_RW_INTERLEAVED, + channels, rate, 0, 50000) < 0) { + snd_pcm_close(hdl); + hdl = NULL; + return -1; + } + + cur_bits = bits; + cur_rate = rate; + cur_chan = channels; + + return 0; +} + +int +alsa_play(void *buf, size_t nbytes) +{ + snd_pcm_sframes_t frames; + + frames = snd_pcm_writei(hdl, buf, nbytes / cur_chan); + if (frames < 0) + frames = snd_pcm_recover(hdl, frames, 1); + return frames; +} diff --git a/flac.c b/flac.c @@ -1,38 +1,83 @@ -#define DR_FLAC_IMPLEMENTATION -#include "ext/dr_flac.h" +#include <sys/select.h> +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <sndfile.h> -static drflac *flac; +static SNDFILE *sf; +static SF_INFO sfinfo; int flac_open(const char *filename, int *bits, int *rate, int *channels) { - if (!(flac = drflac_open_file(filename, NULL))) + sf = sf_open(filename, SFM_READ, &sfinfo); + if (!sf) { + warnx("sf_open_fd: failed"); return -1; + } - *bits = 32; - *rate = flac->sampleRate; - *channels = flac->channels; + switch (sfinfo.format & 0xff) { + case SF_FORMAT_PCM_16: + *bits = 16; + break; + case SF_FORMAT_PCM_24: + *bits = 24; + break; + case SF_FORMAT_PCM_32: + *bits = 32; + break; + default: + warnx("sfinfo: unsupported format"); + goto err0; + } - return flac->totalPCMFrameCount / flac->sampleRate; + *rate = sfinfo.samplerate; + *channels = sfinfo.channels; + + return sfinfo.frames / sfinfo.samplerate; +err0: + sf_close(sf); + sf = NULL; + return -1; +} + +const char * +flac_artist(void) +{ + return sf_get_string(sf, SF_STR_ARTIST); } -long -flac_decode(void *buf, int nbytes) +const char * +flac_album(void) { - return drflac_read_pcm_frames_s32(flac, nbytes / flac->channels, buf); + return sf_get_string(sf, SF_STR_ALBUM); } void flac_seek(int sec) { - long frame_index = flac->currentPCMFrame + sec * (int) flac->sampleRate; - if (frame_index < 0) - frame_index = 0; - drflac_seek_to_pcm_frame(flac, frame_index); + int offset = sfinfo.samplerate * sec; + sf_seek(sf, offset, SEEK_CUR); } -void +int +flac_decode(void *buf, int nitems) +{ + return sf_read_float(sf, buf, nitems); +} + +int flac_close(void) { - drflac_close(flac); + int r = 0; + + if (sf) { + r = sf_close(sf); + if (r != 0) { + warnx("sf_close: failed"); + r = -1; + } + } + sf = NULL; + return r; } diff --git a/map.c b/map.c @@ -1,8 +1,7 @@ /* TODO * - add cli args to set seek_sec and default_vol - * - fix samples playing when quitting while paused - * - add directory name - * - better error checking + * - fix seeking + * - fix gapless playback */ #include <dirent.h> @@ -20,7 +19,9 @@ struct track { char *filename; - char *name; + char *title; + const char *artist; + const char *album; char *format; int length; int current; @@ -40,15 +41,17 @@ void clear_term(void); void set_cursor(int, int); char poll_key_event(void); -/* sndio.c */ -int sndio_init(int); -int sndio_vol(int); -int sndio_open(int, int, int); -int sndio_play(void *, int); -void sndio_close(void); +/* alsa.c */ +int alsa_init(void); +int alsa_vol(int); +int alsa_open(int, int, int); +int alsa_play(void *, int); +void alsa_close(void); /* flac.c */ int flac_open(const char *, int *, int *, int *); +const char * flac_artist(void); +const char * flac_album(void); long flac_decode(void *, int); void flac_seek(int); void flac_close(void); @@ -81,17 +84,17 @@ print_playlist(struct track **tracks, int start_index, int cursorid) { int i; - set_cursor(rows - 3, 1); + set_cursor(rows - 4, 1); write_term("\x1b[1J"); - for (i = start_index; i < start_index + rows - 5; i++) { + for (i = start_index; i < start_index + rows - 6; i++) { if (i == ntracks) break; set_cursor(i - start_index + 1, 7); if (tracks[i]->current) - write_term("\x1b[1K\x1b[1m%s\x1b[0m", tracks[i]->name); + write_term("\x1b[1K\x1b[1m%s\x1b[0m", tracks[i]->title); else - write_term("\x1b[1K%s", tracks[i]->name); + write_term("\x1b[1K%s", tracks[i]->title); } print_cursor(cursorid, cursorid); } @@ -99,8 +102,18 @@ print_playlist(struct track **tracks, int start_index, int cursorid) static void print_info(struct track track) { + if (track.artist) { + set_cursor(rows - 4, 1); + write_term(" Artist: %s\x1b[K", track.artist); + } + + if (track.album) { + set_cursor(rows - 3, 1); + write_term(" Album: %s\x1b[K", track.album); + } + set_cursor(rows - 2, 1); - write_term(" Playing: %s\x1b[K", track.name); + write_term(" Playing: %s\x1b[K", track.title); set_cursor(rows - 1, 32); write_term("Format: %s", track.format); @@ -109,7 +122,7 @@ print_info(struct track track) static void print_timer(int timersec, int track_sec) { - set_cursor(rows - 1, 13); + set_cursor(rows - 1, 12); write_term("\x1b[1K %d:%02d", timersec / 60, timersec % 60); write_term(" / %d:%02d ", track_sec / 60, track_sec % 60); } @@ -165,12 +178,12 @@ init_track(const char *path, const char *filename) tmp->format = "FLAC"; } - tmp->name = malloc(strlen(strrchr(tmp->filename, '/'))); - if (!tmp->name) + tmp->title = malloc(strlen(strrchr(tmp->filename, '/'))); + if (!tmp->title) errx(1, "malloc failure"); - strcpy(tmp->name, strrchr(tmp->filename, '/') + 1); - if ((extension = strrchr(tmp->name, '.') - tmp->name)) - tmp->name[extension] = '\0'; + strcpy(tmp->title, strrchr(tmp->filename, '/') + 1); + if ((extension = strrchr(tmp->title, '.') - tmp->title)) + tmp->title[extension] = '\0'; return tmp; } @@ -232,8 +245,17 @@ opentrack(struct track *track) if (track->length < 0) errx(1, "failed opening: %s", track->filename); - if ((bufsize = sndio_open(bits, rate, channels)) < 0) - errx(1, "sndio_open error"); + if (strcmp(track->format, "FLAC") == 0) { + track->artist = flac_artist(); + track->album = flac_album(); + } + + if (alsa_open(bits, rate, channels) < 0) + errx(1, "alsa_open error"); + + bufsize = 0.05 * rate; + if (bufsize % 2 != 0) + bufsize++; return bufsize; } @@ -244,7 +266,7 @@ play(void) int trackid = 0, cursorid = 1, toptrack = 0, timersec; int gapless = 0; long done, bufsize, tmpbuf_len; - long *buf, *newbuf; + float *buf, *newbuf; time_t start, pause_t; struct track curtrack; @@ -314,17 +336,17 @@ next: break; /* volume */ case 'o': - volume += -5; + volume += -2; if (volume < 0) volume = 0; - sndio_vol(volume); + alsa_vol(volume); print_volume(volume); break; case 'p': - volume += 5; + volume += 2; if (volume > 100) volume = 100; - sndio_vol(volume); + alsa_vol(volume); print_volume(volume); break; /* skip */ @@ -355,8 +377,8 @@ next: if (!gapless) { done = curtrack.decode(buf, bufsize); - if (trackid+1 < ntracks && done < bufsize / 2) { - tmpbuf_len = done * 2; + if (trackid+1 < ntracks && done < bufsize) { + tmpbuf_len = done; gapless = 1; curtrack.close(); tracks[trackid]->current = 0; @@ -364,15 +386,15 @@ next: goto next; } } else { - newbuf = malloc((bufsize - tmpbuf_len) * sizeof(long)); + newbuf = malloc((bufsize - tmpbuf_len) * sizeof(float)); done = curtrack.decode(newbuf, bufsize - tmpbuf_len); - memcpy(buf + tmpbuf_len, newbuf, done * 2 * sizeof(long)); + memcpy(buf + tmpbuf_len, newbuf, done * sizeof(float)); free(newbuf); gapless = 0; } - if (done < 0 || sndio_play(buf, done) < 0) { - sndio_close(); + if (done < 0 || (done = alsa_play(buf, done)) < 0) { + alsa_close(); curtrack.close(); errx(1, "Error during decoding."); } @@ -391,7 +413,7 @@ endtrack: int main(int argc, char **argv) { - int i; + int i, old_vol; volume = DEFAULT_VOL; check_args(argc, argv); @@ -402,16 +424,18 @@ main(int argc, char **argv) qsort(tracks, ntracks, sizeof(*tracks), trackcmp); init_term(); atexit(end_term); - if (sndio_init(volume) < 0) - errx(1, "Could not initialize sndio.\n"); + alsa_init(); + old_vol = alsa_vol(0); + alsa_vol(DEFAULT_VOL); play(); - sndio_close(); + alsa_vol(old_vol + 1); + alsa_close(); clear_term(); for (i = 0; i < ntracks; i++) { free(tracks[i]->filename); - free(tracks[i]->name); + free(tracks[i]->title); free(tracks[i]); } free(tracks);