diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2017-05-31 22:23:15 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2017-05-31 22:26:49 -0700 |
commit | 095ce46f0ee7cad07d7bafb2d771cac235574d1c (patch) | |
tree | 87bc71a08014dd0f92c81b8c5b34847754827ef9 /src | |
parent | 10338bf9283a86471aaf1a4702e8f02e6c9c4f52 (diff) |
playit: add public playit api
The schism stuff isn't really appropriate for external use, its headers
want to include its config.h. So this just keeps all the schism interfacing
private to libplayit where the schism code resides, and the public api is
all new with no dependency on the schism code for a clear separation.
While at it I've added a seekable flag which renders the song in-memory
for easy and accurate seeking within the rendered output.
Note this commit obsoletes the examples in examples/* which were really
only intended as examples of how to do rudimentary playback with the
stripped down schism code.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/playit.c | 209 | ||||
-rw-r--r-- | src/playit.h | 13 |
3 files changed, 226 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ba32c52..7da07f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,9 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 no-dist-gzip lib_LIBRARIES = libplayit.a -include_HEADERS = \ +include_HEADERS = playit.h + +noinst_HEADERS = \ include/cmixer.h \ include/disko.h \ include/dmoz.h \ @@ -91,6 +93,7 @@ endif ## aaaaaaaaahhhhhhhhhhhhhhhhhhh!!!!!!!1 libplayit_a_SOURCES = \ + playit.c \ fmt/compression.c \ fmt/it.c \ util/util.c \ diff --git a/src/playit.c b/src/playit.c new file mode 100644 index 0000000..da3d642 --- /dev/null +++ b/src/playit.c @@ -0,0 +1,209 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "playit.h" +#include "sndfile.h" +#include "fmt.h" + +#define BYTES_PER_FRAME 4 /* simply fixed at 16-bit+stereo: 4 bytes per frame. */ + +typedef struct playit_prerendered_t { + void *buf; + unsigned num_frames; +} playit_prerendered_t; + +typedef struct playit_t { + song_t *song; + unsigned flags; + playit_prerendered_t prerendered; + unsigned current_frame; +} playit_t; + +static song_t * song_open_file(const char *file) +{ + slurp_t *s; + song_t *song; + + assert(file); + + s = slurp(file, NULL, 0); + if (!s) { + fprintf(stderr, "Failed to open \"%s\"\n", file); + goto _fail; + } + + song = csf_allocate(); + if (!song) { + fprintf(stderr, "Failed to allocate song_t\n"); + goto _fail_slurp; + } + + if (fmt_it_load_song(song, s, 0) != LOAD_SUCCESS) + goto _fail_song; + + unslurp(s); + + return song; + +_fail_song: + csf_free(song); +_fail_slurp: + unslurp(s); +_fail: + return NULL; +} + + +/* prerender the opened song into playit->prerendered.buf */ +static int playit_prerender(playit_t *playit) +{ + unsigned alloc_size = 8192, rendered_size = 0; + void *buf = NULL, *new; + + assert(playit); + assert(playit->song); + + while (!(playit->song->flags & SONG_ENDREACHED)) { + unsigned ret; + + alloc_size *= 2; + new = realloc(buf, alloc_size); + if (!new) { + fprintf(stderr, "Failed to realloc %u bytes\n", alloc_size); + goto _fail; + } else + buf = new; + + ret = csf_read(playit->song, buf + rendered_size, alloc_size - rendered_size); + rendered_size += ret * BYTES_PER_FRAME; + } + + playit->prerendered.buf = realloc(buf, rendered_size); + if (!playit->prerendered.buf) { + fprintf(stderr, "Failed to realloc to rendered %u bytes\n", rendered_size); + goto _fail; + } + + playit->prerendered.num_frames = rendered_size / BYTES_PER_FRAME; + + + return 1; + +_fail: + free(buf); + + return 0; +} + + +playit_t * playit_open_file(const char *file, unsigned flags) +{ + playit_t *playit; + song_t *song; + + assert(file); + + playit = calloc(1, sizeof(playit_t)); + if (!playit) { + fprintf(stderr, "Failed to allocate playit_t\n"); + goto _fail; + } + + playit->song = song = song_open_file(file); + if (!playit->song) + goto _fail_playit; + + /* TODO: it seems kind of silly that we're dicking directly with the song_t members, + * there should really be a schism api for doing this properly. + */ + csf_set_current_order(song, 0); + song->repeat_count = -1; /* -1 to stop vs. looping */ + song->buffer_count = 0; + song->stop_at_order = -1; + song->stop_at_row = -1; + song->flags &= ~(SONG_PAUSED | SONG_PATTERNLOOP | SONG_ENDREACHED); + csf_reset_playmarks(song); + csf_set_resampling_mode(song, SRCMODE_LINEAR); + csf_set_wave_config(song, 44100, 16, 2); + + /* If seekable render the whole song into an in-memory buffer for + * precise seekability. This is useful for demo development where + * jumping around the demo within a tool like GNU Rocket is desirable. + * Presumably you'd turn this off for the release and mix realtime, + * unless you need the CPU time and have RAM to spare. + */ + if ((flags & PLAYIT_FLAG_SEEKABLE) && !playit_prerender(playit)) + goto _fail_playit; + + playit->flags = flags; + + return playit; + +_fail_song: + csf_free(playit->song); +_fail_playit: + free(playit); +_fail: + return NULL; +} + + +/* set song frame to an arbitrary offset, returns new offset */ +unsigned playit_seek(playit_t *playit, unsigned frame) +{ + assert(playit); + assert((playit->flags & PLAYIT_FLAG_SEEKABLE)); + + if (frame > playit->prerendered.num_frames) + frame = playit->prerendered.num_frames; + + return playit->current_frame = frame; +} + + +/* Populate buf with up to len bytes of rendered song data starting from the current frame offset. */ +/* The number of frames written into buf is returned (not a byte count!). */ +/* When the end of the song is reached, 0 is returned. */ +int playit_update(playit_t *playit, void *buf, int len, unsigned *res_frame) +{ + int ret = 0; + + assert(playit); + assert(buf); + + if (!(playit->flags & PLAYIT_FLAG_SEEKABLE)) { + if (!(playit->song->flags & SONG_ENDREACHED)) + ret = csf_read(playit->song, buf, len); + } else { + unsigned frames_to_copy = len / BYTES_PER_FRAME; + playit_prerendered_t *pre = &playit->prerendered; + + frames_to_copy = MIN(frames_to_copy, pre->num_frames - playit->current_frame); + + if (frames_to_copy) { + void *src = pre->buf + playit->current_frame * BYTES_PER_FRAME; + + memcpy(buf, src, frames_to_copy * BYTES_PER_FRAME); + } + + ret = frames_to_copy; + } + + playit->current_frame += ret; + + if (res_frame) + *res_frame = playit->current_frame; + + return ret; +} + + +void playit_destroy(playit_t *playit) +{ + assert(playit); + assert(playit->song); + + csf_free(playit->song); + free(playit); +} diff --git a/src/playit.h b/src/playit.h new file mode 100644 index 0000000..6fd9ddf --- /dev/null +++ b/src/playit.h @@ -0,0 +1,13 @@ +#ifndef _PLAYIT_H +#define _PLAYIT_H + +#define PLAYIT_FLAG_SEEKABLE 1 + +typedef struct playit_t playit_t; + +playit_t * playit_open_file(const char *file, unsigned flags); +unsigned playit_seek(playit_t *playit, unsigned frame); +int playit_update(playit_t *playit, void *buf, int len, unsigned *res_frame); +void playit_destroy(playit_t *playit); + +#endif |