diff options
Diffstat (limited to 'src/3rdparty/cmixer/cmixer.c')
-rw-r--r-- | src/3rdparty/cmixer/cmixer.c | 738 |
1 files changed, 0 insertions, 738 deletions
diff --git a/src/3rdparty/cmixer/cmixer.c b/src/3rdparty/cmixer/cmixer.c deleted file mode 100644 index b3f8a44..0000000 --- a/src/3rdparty/cmixer/cmixer.c +++ /dev/null @@ -1,738 +0,0 @@ -/* -** Copyright (c) 2017 rxi -** -** Permission is hereby granted, free of charge, to any person obtaining a copy -** of this software and associated documentation files (the "Software"), to -** deal in the Software without restriction, including without limitation the -** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -** sell copies of the Software, and to permit persons to whom the Software is -** furnished to do so, subject to the following conditions: -** -** The above copyright notice and this permission notice shall be included in -** all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -** IN THE SOFTWARE. -**/ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "cmixer.h" - -#define UNUSED(x) ((void) (x)) -#define CLAMP(x, a, b) ((x) < (a) ? (a) : (x) > (b) ? (b) : (x)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -#define FX_BITS (12) -#define FX_UNIT (1 << FX_BITS) -#define FX_MASK (FX_UNIT - 1) -#define FX_FROM_FLOAT(f) ((f) * FX_UNIT) -#define FX_LERP(a, b, p) ((a) + ((((b) - (a)) * (p)) >> FX_BITS)) - -#define BUFFER_SIZE (512) -#define BUFFER_MASK (BUFFER_SIZE - 1) - -#define CM_USE_STB_VORBIS - -struct cm_Source { - cm_Source *next; /* Next source in list */ - cm_Int16 buffer[BUFFER_SIZE]; /* Internal buffer with raw stereo PCM */ - cm_EventHandler handler; /* Event handler */ - void *udata; /* Stream's udata (from cm_SourceInfo) */ - int samplerate; /* Stream's native samplerate */ - int length; /* Stream's length in frames */ - int end; /* End index for the current play-through */ - int state; /* Current state (playing|paused|stopped) */ - cm_Int64 position; /* Current playhead position (fixed point) */ - int lgain, rgain; /* Left and right gain (fixed point) */ - int rate; /* Playback rate (fixed point) */ - int nextfill; /* Next frame idx where the buffer needs to be filled */ - int loop; /* Whether the source will loop when `end` is reached */ - int rewind; /* Whether the source will rewind before playing */ - int active; /* Whether the source is part of `sources` list */ - double gain; /* Gain set by `cm_set_gain()` */ - double pan; /* Pan set by `cm_set_pan()` */ -}; - - -static struct { - const char *lasterror; /* Last error message */ - cm_EventHandler lock; /* Event handler for lock/unlock events */ - cm_Source *sources; /* Linked list of active (playing) sources */ - cm_Int32 buffer[BUFFER_SIZE]; /* Internal master buffer */ - int samplerate; /* Master samplerate */ - int gain; /* Master gain (fixed point) */ -} cmixer; - - -static void dummy_handler(cm_Event *e) { - UNUSED(e); -} - - -static void lock(void) { - cm_Event e; - e.type = CM_EVENT_LOCK; - cmixer.lock(&e); -} - - -static void unlock(void) { - cm_Event e; - e.type = CM_EVENT_UNLOCK; - cmixer.lock(&e); -} - - -const char* cm_get_error(void) { - const char *res = cmixer.lasterror; - cmixer.lasterror = NULL; - return res; -} - - -static const char* error(const char *msg) { - cmixer.lasterror = msg; - return msg; -} - - -void cm_init(int samplerate) { - cmixer.samplerate = samplerate; - cmixer.lock = dummy_handler; - cmixer.sources = NULL; - cmixer.gain = FX_UNIT; -} - - -void cm_set_lock(cm_EventHandler lock) { - cmixer.lock = lock; -} - - -void cm_set_master_gain(double gain) { - cmixer.gain = FX_FROM_FLOAT(gain); -} - - -static void rewind_source(cm_Source *src) { - cm_Event e; - e.type = CM_EVENT_REWIND; - e.udata = src->udata; - src->handler(&e); - src->position = 0; - src->rewind = 0; - src->end = src->length; - src->nextfill = 0; -} - - -static void fill_source_buffer(cm_Source *src, int offset, int length) { - cm_Event e; - e.type = CM_EVENT_SAMPLES; - e.udata = src->udata; - e.buffer = src->buffer + offset; - e.length = length; - src->handler(&e); -} - - -static void process_source(cm_Source *src, int len) { - int i, n, a, b, p; - int frame, count; - cm_Int32 *dst = cmixer.buffer; - - /* Do rewind if flag is set */ - if (src->rewind) { - rewind_source(src); - } - - /* Don't process if not playing */ - if (src->state != CM_STATE_PLAYING) { - return; - } - - /* Process audio */ - while (len > 0) { - /* Get current position frame */ - frame = src->position >> FX_BITS; - - /* Fill buffer if required */ - if (frame + 3 >= src->nextfill) { - fill_source_buffer(src, (src->nextfill*2) & BUFFER_MASK, BUFFER_SIZE/2); - src->nextfill += BUFFER_SIZE / 4; - } - - /* Handle reaching the end of the playthrough */ - if (frame >= src->end) { - /* As streams continiously fill the raw buffer in a loop we simply - ** increment the end idx by one length and continue reading from it for - ** another play-through */ - src->end = frame + src->length; - /* Set state and stop processing if we're not set to loop */ - if (!src->loop) { - src->state = CM_STATE_STOPPED; - break; - } - } - - /* Work out how many frames we should process in the loop */ - n = MIN(src->nextfill - 2, src->end) - frame; - count = (n << FX_BITS) / src->rate; - count = MAX(count, 1); - count = MIN(count, len / 2); - len -= count * 2; - - /* Add audio to master buffer */ - if (src->rate == FX_UNIT) { - /* Add audio to buffer -- basic */ - n = frame * 2; - for (i = 0; i < count; i++) { - dst[0] += (src->buffer[(n ) & BUFFER_MASK] * src->lgain) >> FX_BITS; - dst[1] += (src->buffer[(n + 1) & BUFFER_MASK] * src->rgain) >> FX_BITS; - n += 2; - dst += 2; - } - src->position += count * FX_UNIT; - - } else { - /* Add audio to buffer -- interpolated */ - for (i = 0; i < count; i++) { - n = (src->position >> FX_BITS) * 2; - p = src->position & FX_MASK; - a = src->buffer[(n ) & BUFFER_MASK]; - b = src->buffer[(n + 2) & BUFFER_MASK]; - dst[0] += (FX_LERP(a, b, p) * src->lgain) >> FX_BITS; - n++; - a = src->buffer[(n ) & BUFFER_MASK]; - b = src->buffer[(n + 2) & BUFFER_MASK]; - dst[1] += (FX_LERP(a, b, p) * src->rgain) >> FX_BITS; - src->position += src->rate; - dst += 2; - } - } - - } -} - - -void cm_process(cm_Int16 *dst, int len) { - int i; - cm_Source **s; - - /* Process in chunks of BUFFER_SIZE if `len` is larger than BUFFER_SIZE */ - while (len > BUFFER_SIZE) { - cm_process(dst, BUFFER_SIZE); - dst += BUFFER_SIZE; - len -= BUFFER_SIZE; - } - - /* Zeroset internal buffer */ - memset(cmixer.buffer, 0, len * sizeof(cmixer.buffer[0])); - - /* Process active sources */ - lock(); - s = &cmixer.sources; - while (*s) { - process_source(*s, len); - /* Remove source from list if it is no longer playing */ - if ((*s)->state != CM_STATE_PLAYING) { - (*s)->active = 0; - *s = (*s)->next; - } else { - s = &(*s)->next; - } - } - unlock(); - - /* Copy internal buffer to destination and clip */ - for (i = 0; i < len; i++) { - int x = (cmixer.buffer[i] * cmixer.gain) >> FX_BITS; - dst[i] = CLAMP(x, -32768, 32767); - } -} - - -cm_Source* cm_new_source(const cm_SourceInfo *info) { - cm_Source *src = (cm_Source *)calloc(1, sizeof(*src)); - if (!src) { - error("allocation failed"); - return NULL; - } - src->handler = info->handler; - src->length = info->length; - src->samplerate = info->samplerate; - src->udata = info->udata; - cm_set_gain(src, 1); - cm_set_pan(src, 0); - cm_set_pitch(src, 1); - cm_set_loop(src, 0); - cm_stop(src); - return src; -} - - -static const char* wav_init(cm_SourceInfo *info, void *data, int len, int ownsdata); - -#ifdef CM_USE_STB_VORBIS -static const char* ogg_init(cm_SourceInfo *info, void *data, int len, int ownsdata); -#endif - - -static int check_header(void *data, int size, char *str, int offset) { - int len = strlen(str); - return (size >= offset + len) && !memcmp((char*) data + offset, str, len); -} - - -static cm_Source* new_source_from_mem(void *data, int size, int ownsdata) { - const char *err; - cm_SourceInfo info; - - if (check_header(data, size, "WAVE", 8)) { - err = wav_init(&info, data, size, ownsdata); - if (err) { - return NULL; - } - return cm_new_source(&info); - } - -#ifdef CM_USE_STB_VORBIS - if (check_header(data, size, "OggS", 0)) { - err = ogg_init(&info, data, size, ownsdata); - if (err) { - return NULL; - } - return cm_new_source(&info); - } -#endif - - error("unknown format or invalid data"); - return NULL; -} - - -static void* load_file(const char *filename, int *size) { - FILE *fp; - void *data; - int n; - - fp = fopen(filename, "rb"); - if (!fp) { - return NULL; - } - - /* Get size */ - fseek(fp, 0, SEEK_END); - *size = ftell(fp); - rewind(fp); - - /* Malloc, read and return data */ - data = malloc(*size); - if (!data) { - fclose(fp); - return NULL; - } - n = fread(data, 1, *size, fp); - fclose(fp); - if (n != *size) { - free(data); - return NULL; - } - - return data; -} - - -cm_Source* cm_new_source_from_file(const char *filename) { - int size; - cm_Source *src; - void *data; - - /* Load file into memory */ - data = load_file(filename, &size); - if (!data) { - error("could not load file"); - return NULL; - } - - /* Try to load and return */ - src = new_source_from_mem(data, size, 1); - if (!src) { - free(data); - return NULL; - } - - return src; -} - - -cm_Source* cm_new_source_from_mem(void *data, int size) { - return new_source_from_mem(data, size, 0); -} - - -void cm_destroy_source(cm_Source *src) { - cm_Event e; - lock(); - if (src->active) { - cm_Source **s = &cmixer.sources; - while (*s) { - if (*s == src) { - *s = src->next; - break; - } - } - } - unlock(); - e.type = CM_EVENT_DESTROY; - e.udata = src->udata; - src->handler(&e); - free(src); -} - - -double cm_get_length(cm_Source *src) { - return src->length / (double) src->samplerate; -} - - -double cm_get_position(cm_Source *src) { - return ((src->position >> FX_BITS) % src->length) / (double) src->samplerate; -} - - -int cm_get_state(cm_Source *src) { - return src->state; -} - - -static void recalc_source_gains(cm_Source *src) { - double l, r; - double pan = src->pan; - l = src->gain * (pan <= 0. ? 1. : 1. - pan); - r = src->gain * (pan >= 0. ? 1. : 1. + pan); - src->lgain = FX_FROM_FLOAT(l); - src->rgain = FX_FROM_FLOAT(r); -} - - -void cm_set_gain(cm_Source *src, double gain) { - src->gain = gain; - recalc_source_gains(src); -} - - -void cm_set_pan(cm_Source *src, double pan) { - src->pan = CLAMP(pan, -1.0, 1.0); - recalc_source_gains(src); -} - - -void cm_set_pitch(cm_Source *src, double pitch) { - double rate; - if (pitch > 0.) { - rate = src->samplerate / (double) cmixer.samplerate * pitch; - } else { - rate = 0.001; - } - src->rate = FX_FROM_FLOAT(rate); -} - - -void cm_set_loop(cm_Source *src, int loop) { - src->loop = loop; -} - - -void cm_play(cm_Source *src) { - lock(); - src->state = CM_STATE_PLAYING; - if (!src->active) { - src->active = 1; - src->next = cmixer.sources; - cmixer.sources = src; - } - unlock(); -} - - -void cm_pause(cm_Source *src) { - src->state = CM_STATE_PAUSED; -} - - -void cm_stop(cm_Source *src) { - src->state = CM_STATE_STOPPED; - src->rewind = 1; -} - - -/*============================================================================ -** Wav stream -**============================================================================*/ - -typedef struct { - void *data; - int bitdepth; - int samplerate; - int channels; - int length; -} Wav; - -typedef struct { - Wav wav; - void *data; - int idx; -} WavStream; - - -static char* find_subchunk(char *data, int len, char *id, int *size) { - /* TODO : Error handling on malformed wav file */ - int idlen = strlen(id); - char *p = data + 12; -next: - *size = *((cm_UInt32*) (p + 4)); - if (memcmp(p, id, idlen)) { - p += 8 + *size; - if (p > data + len) return NULL; - goto next; - } - return p + 8; -} - - -static const char* read_wav(Wav *w, void *data, int len) { - int bitdepth, channels, samplerate, format; - int sz; - char *p = (char*)data; - memset(w, 0, sizeof(*w)); - - /* Check header */ - if (memcmp(p, "RIFF", 4) || memcmp(p + 8, "WAVE", 4)) { - return error("bad wav header"); - } - /* Find fmt subchunk */ - p = find_subchunk((char*)data, len, "fmt", &sz); - if (!p) { - return error("no fmt subchunk"); - } - - /* Load fmt info */ - format = *((cm_UInt16*) (p)); - channels = *((cm_UInt16*) (p + 2)); - samplerate = *((cm_UInt32*) (p + 4)); - bitdepth = *((cm_UInt16*) (p + 14)); - if (format != 1) { - return error("unsupported format"); - } - if (channels == 0 || samplerate == 0 || bitdepth == 0) { - return error("bad format"); - } - - /* Find data subchunk */ - p = find_subchunk((char*)data, len, "data", &sz); - if (!p) { - return error("no data subchunk"); - } - - /* Init struct */ - w->data = (void*) p; - w->samplerate = samplerate; - w->channels = channels; - w->length = (sz / (bitdepth / 8)) / channels; - w->bitdepth = bitdepth; - /* Done */ - return NULL; -} - - -#define WAV_PROCESS_LOOP(X) \ - while (n--) { \ - X \ - dst += 2; \ - s->idx++; \ - } - -static void wav_handler(cm_Event *e) { - int x, n; - cm_Int16 *dst; - WavStream *s = (WavStream *)e->udata; - int len; - - switch (e->type) { - - case CM_EVENT_DESTROY: - free(s->data); - free(s); - break; - - case CM_EVENT_SAMPLES: - dst = e->buffer; - len = e->length / 2; -fill: - n = MIN(len, s->wav.length - s->idx); - len -= n; - if (s->wav.bitdepth == 16 && s->wav.channels == 1) { - WAV_PROCESS_LOOP({ - dst[0] = dst[1] = ((cm_Int16*) s->wav.data)[s->idx]; - }); - } else if (s->wav.bitdepth == 16 && s->wav.channels == 2) { - WAV_PROCESS_LOOP({ - x = s->idx * 2; - dst[0] = ((cm_Int16*) s->wav.data)[x ]; - dst[1] = ((cm_Int16*) s->wav.data)[x + 1]; - }); - } else if (s->wav.bitdepth == 8 && s->wav.channels == 1) { - WAV_PROCESS_LOOP({ - dst[0] = dst[1] = (((cm_UInt8*) s->wav.data)[s->idx] - 128) << 8; - }); - } else if (s->wav.bitdepth == 8 && s->wav.channels == 2) { - WAV_PROCESS_LOOP({ - x = s->idx * 2; - dst[0] = (((cm_UInt8*) s->wav.data)[x ] - 128) << 8; - dst[1] = (((cm_UInt8*) s->wav.data)[x + 1] - 128) << 8; - }); - } - /* Loop back and continue filling buffer if we didn't fill the buffer */ - if (len > 0) { - s->idx = 0; - goto fill; - } - break; - - case CM_EVENT_REWIND: - s->idx = 0; - break; - } -} - - -static const char* wav_init(cm_SourceInfo *info, void *data, int len, int ownsdata) { - WavStream *stream; - Wav wav; - - const char *err = read_wav(&wav, data, len); - if (err != NULL) { - return err; - } - - if (wav.channels > 2 || (wav.bitdepth != 16 && wav.bitdepth != 8)) { - return error("unsupported wav format"); - } - - stream = (WavStream *)calloc(1, sizeof(*stream)); - if (!stream) { - return error("allocation failed"); - } - stream->wav = wav; - - if (ownsdata) { - stream->data = data; - } - stream->idx = 0; - - info->udata = stream; - info->handler = wav_handler; - info->samplerate = wav.samplerate; - info->length = wav.length; - - /* Return NULL (no error) for success */ - return NULL; -} - - -/*============================================================================ -** Ogg stream -**============================================================================*/ - -#ifdef CM_USE_STB_VORBIS - -#define STB_VORBIS_HEADER_ONLY -#include "cmixer_stb_vorbis.h" - -typedef struct { - stb_vorbis *ogg; - void *data; -} OggStream; - - -static void ogg_handler(cm_Event *e) { - int n, len; - OggStream *s = (OggStream *)e->udata; - cm_Int16 *buf; - - switch (e->type) { - - case CM_EVENT_DESTROY: - stb_vorbis_close(s->ogg); - free(s->data); - free(s); - break; - - case CM_EVENT_SAMPLES: - len = e->length; - buf = e->buffer; -fill: - n = stb_vorbis_get_samples_short_interleaved(s->ogg, 2, buf, len); - n *= 2; - /* rewind and fill remaining buffer if we reached the end of the ogg - ** before filling it */ - if (len != n) { - stb_vorbis_seek_start(s->ogg); - buf += n; - len -= n; - goto fill; - } - break; - - case CM_EVENT_REWIND: - stb_vorbis_seek_start(s->ogg); - break; - } -} - - -static const char* ogg_init(cm_SourceInfo *info, void *data, int len, int ownsdata) { - OggStream *stream; - stb_vorbis *ogg; - stb_vorbis_info ogginfo; - int err; - - ogg = stb_vorbis_open_memory((unsigned char *)data, len, &err, NULL); - if (!ogg) { - return error("invalid ogg data"); - } - - stream = (OggStream *)calloc(1, sizeof(*stream)); - if (!stream) { - stb_vorbis_close(ogg); - return error("allocation failed"); - } - - stream->ogg = ogg; - if (ownsdata) { - stream->data = data; - } - - ogginfo = stb_vorbis_get_info(ogg); - - info->udata = stream; - info->handler = ogg_handler; - info->samplerate = ogginfo.sample_rate; - info->length = stb_vorbis_stream_length_in_samples(ogg); - - /* Return NULL (no error) for success */ - return NULL; -} - - -#endif |