844 lines
21 KiB
C
Raw Normal View History

/* Copyright (C) 2010-2017 The RetroArch team
2017-02-22 23:54:28 +00:00
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (audio_mixer.c).
* ---------------------------------------------------------------------------------------
2017-02-22 23:54:28 +00:00
*
* 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.
2017-02-22 23:54:28 +00:00
*/
#include <audio/audio_mixer.h>
2017-02-22 23:54:28 +00:00
#include <audio/audio_resampler.h>
2017-02-22 23:54:28 +00:00
#include <formats/rwav.h>
#include <memalign.h>
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
#include <rthreads/rthreads.h>
#endif
2017-02-22 23:54:28 +00:00
#include <stdlib.h>
#include <string.h>
#include <math.h>
2017-02-23 05:56:31 +01:00
#ifdef HAVE_CONFIG_H
#include "../../config.h"
2017-02-23 05:56:31 +01:00
#endif
#ifdef HAVE_STB_VORBIS
2017-02-22 23:54:28 +00:00
#define STB_VORBIS_NO_PUSHDATA_API
#define STB_VORBIS_NO_STDIO
#define STB_VORBIS_NO_CRT
#include "../../deps/stb/stb_vorbis.h"
2017-02-23 05:56:31 +01:00
#endif
2017-02-22 23:54:28 +00:00
#ifdef HAVE_IBXM
#include "../../deps/ibxm/ibxm.h"
#endif
2017-02-22 23:54:28 +00:00
#define AUDIO_MIXER_MAX_VOICES 8
#define AUDIO_MIXER_TEMP_OGG_BUFFER 8192
2017-05-16 23:12:03 +02:00
struct audio_mixer_sound
{
enum audio_mixer_type type;
union
{
struct
{
/* wav */
unsigned frames;
const float* pcm;
} wav;
#ifdef HAVE_STB_VORBIS
struct
{
/* ogg */
unsigned size;
const void* data;
} ogg;
#endif
#ifdef HAVE_IBXM
struct
{
/* mod/s3m/xm */
unsigned size;
const void* data;
} mod;
#endif
2017-05-16 23:12:03 +02:00
} types;
};
2017-05-08 18:11:09 +02:00
struct audio_mixer_voice
2017-02-22 23:54:28 +00:00
{
bool repeat;
2017-05-08 21:24:14 +02:00
unsigned type;
2017-02-22 23:54:28 +00:00
float volume;
2017-05-08 18:11:09 +02:00
audio_mixer_sound_t *sound;
2017-02-22 23:54:28 +00:00
audio_mixer_stop_cb_t stop_cb;
union
{
struct
{
/* wav */
unsigned position;
} wav;
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-02-22 23:54:28 +00:00
struct
{
/* ogg */
unsigned position;
unsigned samples;
unsigned buf_samples;
2017-05-08 21:24:14 +02:00
float* buffer;
2017-02-22 23:54:28 +00:00
float ratio;
2017-05-08 21:24:14 +02:00
stb_vorbis *stream;
void *resampler_data;
const retro_resampler_t *resampler;
2017-02-22 23:54:28 +00:00
} ogg;
2017-02-23 05:56:31 +01:00
#endif
#ifdef HAVE_IBXM
struct
{
/* mod/s3m/xm */
unsigned position;
unsigned samples;
unsigned buf_samples;
int* buffer;
struct replay* stream;
} mod;
#endif
2017-02-22 23:54:28 +00:00
} types;
};
2017-05-08 21:24:14 +02:00
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES];
static unsigned s_rate = 0;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
static slock_t* s_locker = NULL;
2017-05-21 11:57:10 +01:00
#endif
2017-02-22 23:54:28 +00:00
2017-05-08 20:37:12 +02:00
static bool wav2float(const rwav_t* wav, float** pcm, size_t samples_out)
2017-02-22 23:54:28 +00:00
{
size_t i;
/* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes */
2017-05-08 20:37:12 +02:00
float *f = (float*)memalign_alloc(16,
((samples_out + 15) & ~15) * sizeof(float));
2017-02-22 23:54:28 +00:00
2017-02-23 04:03:53 +01:00
if (!f)
2017-02-22 23:54:28 +00:00
return false;
*pcm = f;
2017-05-08 18:37:31 +02:00
if (wav->bitspersample == 8)
2017-02-22 23:54:28 +00:00
{
2017-05-08 18:37:31 +02:00
float sample = 0.0f;
const uint8_t *u8 = (const uint8_t*)wav->samples;
if (wav->numchannels == 1)
2017-02-22 23:54:28 +00:00
{
for (i = wav->numsamples; i != 0; i--)
{
sample = (float)*u8++ / 255.0f;
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
*f++ = sample;
2017-02-22 23:54:28 +00:00
}
}
2017-05-08 18:37:31 +02:00
else if (wav->numchannels == 2)
2017-02-22 23:54:28 +00:00
{
for (i = wav->numsamples; i != 0; i--)
{
2017-05-08 18:37:31 +02:00
sample = (float)*u8++ / 255.0f;
2017-02-22 23:54:28 +00:00
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
2017-05-08 18:37:31 +02:00
sample = (float)*u8++ / 255.0f;
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
2017-02-22 23:54:28 +00:00
}
}
}
2017-05-08 18:37:31 +02:00
else
2017-02-22 23:54:28 +00:00
{
2017-05-08 18:37:31 +02:00
/* TODO/FIXME note to leiradel - can we use audio/conversion/s16_to_float
* functions here? */
float sample = 0.0f;
const int16_t *s16 = (const int16_t*)wav->samples;
if (wav->numchannels == 1)
2017-02-22 23:54:28 +00:00
{
for (i = wav->numsamples; i != 0; i--)
{
2017-05-08 18:37:31 +02:00
sample = (float)((int)*s16++ + 32768) / 65535.0f;
2017-02-22 23:54:28 +00:00
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
*f++ = sample;
2017-02-22 23:54:28 +00:00
}
}
2017-05-08 18:37:31 +02:00
else if (wav->numchannels == 2)
2017-02-22 23:54:28 +00:00
{
for (i = wav->numsamples; i != 0; i--)
{
sample = (float)((int)*s16++ + 32768) / 65535.0f;
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
2017-02-22 23:54:28 +00:00
sample = (float)((int)*s16++ + 32768) / 65535.0f;
sample = sample * 2.0f - 1.0f;
2017-02-23 04:00:40 +01:00
*f++ = sample;
2017-02-22 23:54:28 +00:00
}
}
}
2017-05-08 18:37:31 +02:00
2017-02-22 23:54:28 +00:00
return true;
}
2017-02-23 04:00:40 +01:00
static bool one_shot_resample(const float* in, size_t samples_in,
unsigned rate, float** out, size_t* samples_out)
2017-02-22 23:54:28 +00:00
{
2017-03-05 22:21:55 +01:00
struct resampler_data info;
2017-02-23 04:00:40 +01:00
void* data = NULL;
2017-02-22 23:54:28 +00:00
const retro_resampler_t* resampler = NULL;
2017-02-23 04:00:40 +01:00
float ratio = (double)s_rate / (double)rate;
2017-02-22 23:54:28 +00:00
if (!retro_resampler_realloc(&data, &resampler, NULL, ratio))
return false;
/*
* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
* add four more samples in the formula below just as safeguard, because
* resampler->process sometimes reports more output samples than the
* formula below calculates. Ideally, audio resamplers should have a
* function to return the number of samples they will output given a
* count of input samples.
*/
*samples_out = samples_in * ratio + 4;
2017-02-23 04:00:40 +01:00
*out = (float*)memalign_alloc(16,
((*samples_out + 15) & ~15) * sizeof(float));
2017-02-22 23:54:28 +00:00
if (*out == NULL)
return false;
2017-02-23 04:00:40 +01:00
info.data_in = in;
info.data_out = *out;
info.input_frames = samples_in / 2;
2017-03-05 22:21:55 +01:00
info.output_frames = 0;
2017-02-23 04:00:40 +01:00
info.ratio = ratio;
2017-02-22 23:54:28 +00:00
resampler->process(data, &info);
resampler->free(data);
return true;
}
void audio_mixer_init(unsigned rate)
{
unsigned i;
s_rate = rate;
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
s_voices[i].type = AUDIO_MIXER_TYPE_NONE;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
s_locker = slock_new();
2017-05-21 11:57:10 +01:00
#endif
2017-02-22 23:54:28 +00:00
}
void audio_mixer_done(void)
{
unsigned i;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
/* Dont call audio mixer functions after this point */
slock_free(s_locker);
s_locker = NULL;
2017-05-21 11:57:10 +01:00
#endif
2017-02-22 23:54:28 +00:00
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
s_voices[i].type = AUDIO_MIXER_TYPE_NONE;
}
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size)
2017-02-22 23:54:28 +00:00
{
/* WAV data */
rwav_t wav;
/* WAV samples converted to float */
2017-02-23 04:00:40 +01:00
float* pcm = NULL;
size_t samples = 0;
2017-02-22 23:54:28 +00:00
/* Result */
2017-02-23 04:00:40 +01:00
audio_mixer_sound_t* sound = NULL;
2017-05-13 07:53:42 +02:00
enum rwav_state rwav_ret = rwav_load(&wav, buffer, size);
2017-05-08 18:37:31 +02:00
if (rwav_ret != RWAV_ITERATE_DONE)
return NULL;
2017-02-22 23:54:28 +00:00
2017-05-08 20:37:12 +02:00
samples = wav.numsamples * 2;
2017-05-08 20:37:12 +02:00
if (!wav2float(&wav, &pcm, samples))
2017-02-22 23:54:28 +00:00
return NULL;
if (wav.samplerate != s_rate)
{
2017-05-08 18:37:31 +02:00
float* resampled = NULL;
2017-02-23 04:00:40 +01:00
if (!one_shot_resample(pcm, samples,
wav.samplerate, &resampled, &samples))
2017-02-22 23:54:28 +00:00
return NULL;
memalign_free((void*)pcm);
pcm = resampled;
}
2017-05-13 07:56:44 +02:00
sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
2017-02-22 23:54:28 +00:00
2017-02-23 04:03:53 +01:00
if (!sound)
2017-02-22 23:54:28 +00:00
{
memalign_free((void*)pcm);
return NULL;
}
2017-02-23 04:03:53 +01:00
sound->type = AUDIO_MIXER_TYPE_WAV;
sound->types.wav.frames = (unsigned)(samples / 2);
2017-02-23 04:03:53 +01:00
sound->types.wav.pcm = pcm;
2017-02-22 23:54:28 +00:00
rwav_free(&wav);
2017-05-08 18:37:31 +02:00
2017-02-22 23:54:28 +00:00
return sound;
}
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
2017-02-22 23:54:28 +00:00
{
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-05-13 07:53:42 +02:00
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
2017-02-22 23:54:28 +00:00
2017-02-23 04:03:53 +01:00
if (!sound)
2017-02-22 23:54:28 +00:00
return NULL;
2017-05-15 01:28:34 +02:00
2017-04-21 20:53:07 +02:00
sound->type = AUDIO_MIXER_TYPE_OGG;
2017-02-22 23:54:28 +00:00
sound->types.ogg.size = size;
sound->types.ogg.data = buffer;
return sound;
2017-02-23 05:56:31 +01:00
#else
return NULL;
#endif
2017-02-22 23:54:28 +00:00
}
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size)
{
#ifdef HAVE_IBXM
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
if (!sound)
return NULL;
sound->type = AUDIO_MIXER_TYPE_MOD;
sound->types.mod.size = size;
sound->types.mod.data = buffer;
return sound;
#else
return NULL;
#endif
}
2017-02-22 23:54:28 +00:00
void audio_mixer_destroy(audio_mixer_sound_t* sound)
{
void *handle = NULL;
2017-05-08 18:11:09 +02:00
if (!sound)
return;
2017-02-23 05:56:31 +01:00
switch (sound->type)
{
case AUDIO_MIXER_TYPE_WAV:
handle = (void*)sound->types.wav.pcm;
if (handle)
memalign_free(handle);
2017-02-23 05:56:31 +01:00
break;
case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
handle = (void*)sound->types.ogg.data;
if (handle)
free(handle);
#endif
break;
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
handle = (void*)sound->types.mod.data;
if (handle)
free(handle);
2017-02-23 05:56:31 +01:00
#endif
break;
2017-05-08 20:27:13 +02:00
case AUDIO_MIXER_TYPE_NONE:
break;
2017-02-23 05:56:31 +01:00
}
2017-02-22 23:54:28 +00:00
free(sound);
}
2017-02-23 04:00:40 +01:00
static bool audio_mixer_play_wav(audio_mixer_sound_t* sound,
audio_mixer_voice_t* voice, bool repeat, float volume,
audio_mixer_stop_cb_t stop_cb)
2017-02-22 23:54:28 +00:00
{
voice->types.wav.position = 0;
return true;
}
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-02-23 04:00:40 +01:00
static bool audio_mixer_play_ogg(
audio_mixer_sound_t* sound,
audio_mixer_voice_t* voice,
bool repeat, float volume,
audio_mixer_stop_cb_t stop_cb)
2017-02-22 23:54:28 +00:00
{
stb_vorbis_info info;
2017-05-08 20:27:13 +02:00
int res = 0;
float ratio = 0.0f;
unsigned samples = 0;
void *ogg_buffer = NULL;
void *resampler_data = NULL;
const retro_resampler_t* resamp = NULL;
stb_vorbis *stb_vorbis = stb_vorbis_open_memory(
2017-02-23 04:00:40 +01:00
(const unsigned char*)sound->types.ogg.data,
sound->types.ogg.size, &res, NULL);
2017-02-22 23:54:28 +00:00
2017-05-08 20:27:13 +02:00
if (!stb_vorbis)
2017-02-22 23:54:28 +00:00
return false;
2017-05-08 20:27:13 +02:00
info = stb_vorbis_get_info(stb_vorbis);
2017-02-22 23:54:28 +00:00
if (info.sample_rate != s_rate)
{
2017-05-08 20:27:13 +02:00
ratio = (double)s_rate / (double)info.sample_rate;
2017-02-22 23:54:28 +00:00
2017-05-08 20:27:13 +02:00
if (!retro_resampler_realloc(&resampler_data,
&resamp, NULL, ratio))
goto error;
2017-02-22 23:54:28 +00:00
}
2017-05-08 20:27:13 +02:00
samples = (unsigned)(AUDIO_MIXER_TEMP_OGG_BUFFER * ratio);
ogg_buffer = (float*)memalign_alloc(16,
2017-02-23 04:00:40 +01:00
((samples + 15) & ~15) * sizeof(float));
2017-02-22 23:54:28 +00:00
2017-05-08 20:27:13 +02:00
if (!ogg_buffer)
2017-02-22 23:54:28 +00:00
{
2017-05-08 20:27:13 +02:00
resamp->free(resampler_data);
goto error;
2017-02-22 23:54:28 +00:00
}
2017-05-08 20:27:13 +02:00
voice->types.ogg.resampler = resamp;
voice->types.ogg.resampler_data = resampler_data;
2017-05-14 03:29:26 +02:00
voice->types.ogg.buffer = (float*)ogg_buffer;
2017-05-08 20:27:13 +02:00
voice->types.ogg.buf_samples = samples;
voice->types.ogg.ratio = ratio;
voice->types.ogg.stream = stb_vorbis;
voice->types.ogg.position = 0;
voice->types.ogg.samples = 0;
2017-02-22 23:54:28 +00:00
return true;
2017-05-08 20:27:13 +02:00
error:
stb_vorbis_close(stb_vorbis);
return false;
2017-02-22 23:54:28 +00:00
}
2017-02-23 05:56:31 +01:00
#endif
2017-02-22 23:54:28 +00:00
#ifdef HAVE_IBXM
static bool audio_mixer_play_mod(
audio_mixer_sound_t* sound,
audio_mixer_voice_t* voice,
bool repeat, float volume,
audio_mixer_stop_cb_t stop_cb)
{
int buf_samples = 0;
int samples = 0;
void *mod_buffer = NULL;
struct module* module = NULL;
struct replay* replay = NULL;
struct data data;
char message[64];
data.buffer = (char*)sound->types.ogg.data;
data.length = sound->types.ogg.size;
module = module_load(&data, message);
if (module==NULL) {
printf("audio_mixer_play_mod module_load() failed with error: %s\n", message);
goto error;
}
replay = new_replay( module, s_rate, 1);
if (replay==NULL) {
printf("audio_mixer_play_mod new_replay() failed\n");
goto error;
}
buf_samples = calculate_mix_buf_len(s_rate);
mod_buffer = memalign_alloc(16, ((buf_samples + 15) & ~15) * sizeof(int));
if (!mod_buffer) {
printf("audio_mixer_play_mod cannot allocate mod_buffer !\n");
goto error;
}
samples = replay_calculate_duration(replay);
if (!samples) {
printf("audio_mixer_play_mod cannot retrieve duration !\n");
goto error;
}
voice->types.mod.buffer = mod_buffer;
2017-08-04 16:09:53 +02:00
voice->types.mod.buf_samples = buf_samples;
voice->types.mod.stream = replay;
voice->types.mod.position = 0;
2017-08-04 16:09:53 +02:00
voice->types.mod.samples = 0;//samples;
return true;
error:
if (mod_buffer)
memalign_free(mod_buffer);
dispose_module(module);
return false;
}
#endif
2017-02-23 04:00:40 +01:00
audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound, bool repeat,
float volume, audio_mixer_stop_cb_t stop_cb)
2017-02-22 23:54:28 +00:00
{
unsigned i;
2017-02-23 04:00:40 +01:00
bool res = false;
2017-05-08 05:59:45 +02:00
audio_mixer_voice_t* voice = s_voices;
2017-05-08 20:27:13 +02:00
if (!sound)
return NULL;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_lock(s_locker);
2017-05-21 11:57:10 +01:00
#endif
2017-05-08 20:27:13 +02:00
2017-05-08 05:59:45 +02:00
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
2017-02-22 23:54:28 +00:00
{
2017-05-08 20:44:25 +02:00
if (voice->type != AUDIO_MIXER_TYPE_NONE)
continue;
switch (sound->type)
2017-02-22 23:54:28 +00:00
{
2017-05-08 20:44:25 +02:00
case AUDIO_MIXER_TYPE_WAV:
res = audio_mixer_play_wav(sound, voice, repeat, volume, stop_cb);
break;
case AUDIO_MIXER_TYPE_OGG:
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-05-08 20:44:25 +02:00
res = audio_mixer_play_ogg(sound, voice, repeat, volume, stop_cb);
#endif
break;
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb);
2017-02-23 05:56:31 +01:00
#endif
2017-05-08 20:44:25 +02:00
break;
case AUDIO_MIXER_TYPE_NONE:
break;
2017-02-22 23:54:28 +00:00
}
2017-05-08 20:44:25 +02:00
break;
2017-02-22 23:54:28 +00:00
}
2017-05-21 11:57:10 +01:00
if (res)
{
voice->type = sound->type;
voice->repeat = repeat;
voice->volume = volume;
voice->sound = sound;
voice->stop_cb = stop_cb;
}
else
voice = NULL;
2017-05-08 20:27:13 +02:00
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_unlock(s_locker);
#endif
2017-05-08 20:27:13 +02:00
return voice;
2017-02-22 23:54:28 +00:00
}
void audio_mixer_stop(audio_mixer_voice_t* voice)
{
2017-05-21 11:57:10 +01:00
audio_mixer_stop_cb_t stop_cb = NULL;
audio_mixer_sound_t* sound = NULL;
if (voice)
{
2017-05-21 11:57:10 +01:00
stop_cb = voice->stop_cb;
sound = voice->sound;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_lock(s_locker);
#endif
voice->type = AUDIO_MIXER_TYPE_NONE;
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_unlock(s_locker);
2017-05-21 11:57:10 +01:00
#endif
if (stop_cb)
stop_cb(sound, AUDIO_MIXER_SOUND_STOPPED);
}
2017-02-22 23:54:28 +00:00
}
2017-05-08 20:35:02 +02:00
static void audio_mixer_mix_wav(float* buffer, size_t num_frames,
audio_mixer_voice_t* voice,
float volume)
2017-02-22 23:54:28 +00:00
{
int i;
unsigned buf_free = (unsigned)(num_frames * 2);
2017-02-23 04:00:40 +01:00
const audio_mixer_sound_t* sound = voice->sound;
unsigned pcm_available = sound->types.wav.frames
* 2 - voice->types.wav.position;
2017-05-08 20:35:02 +02:00
const float* pcm = sound->types.wav.pcm +
voice->types.wav.position;
2017-02-22 23:54:28 +00:00
again:
if (pcm_available < buf_free)
{
for (i = pcm_available; i != 0; i--)
*buffer++ += *pcm++ * volume;
if (voice->repeat)
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
2017-02-22 23:54:28 +00:00
2017-02-23 05:56:31 +01:00
buf_free -= pcm_available;
pcm_available = sound->types.wav.frames * 2;
pcm = sound->types.wav.pcm;
voice->types.wav.position = 0;
2017-02-22 23:54:28 +00:00
goto again;
}
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
2017-02-22 23:54:28 +00:00
voice->type = AUDIO_MIXER_TYPE_NONE;
}
else
{
for (i = buf_free; i != 0; i--)
*buffer++ += *pcm++ * volume;
voice->types.wav.position += buf_free;
}
}
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-05-08 20:35:02 +02:00
static void audio_mixer_mix_ogg(float* buffer, size_t num_frames,
audio_mixer_voice_t* voice,
float volume)
2017-02-22 23:54:28 +00:00
{
int i;
2017-03-05 22:21:55 +01:00
struct resampler_data info;
2017-05-08 20:35:02 +02:00
float temp_buffer[AUDIO_MIXER_TEMP_OGG_BUFFER];
2017-02-23 04:00:40 +01:00
unsigned buf_free = num_frames * 2;
unsigned temp_samples = 0;
float* pcm = NULL;
2017-02-22 23:54:28 +00:00
if (voice->types.ogg.position == voice->types.ogg.samples)
{
2017-02-23 04:00:40 +01:00
again:
temp_samples = stb_vorbis_get_samples_float_interleaved(
voice->types.ogg.stream, 2, temp_buffer,
AUDIO_MIXER_TEMP_OGG_BUFFER) * 2;
2017-02-22 23:54:28 +00:00
if (temp_samples == 0)
{
if (voice->repeat)
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
2017-02-22 23:54:28 +00:00
stb_vorbis_seek_start(voice->types.ogg.stream);
goto again;
}
else
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
2017-02-22 23:54:28 +00:00
voice->type = AUDIO_MIXER_TYPE_NONE;
return;
}
}
2017-03-05 22:21:55 +01:00
info.data_in = temp_buffer;
info.data_out = voice->types.ogg.buffer;
info.input_frames = temp_samples / 2;
info.output_frames = 0;
info.ratio = voice->types.ogg.ratio;
2017-02-22 23:54:28 +00:00
voice->types.ogg.resampler->process(voice->types.ogg.resampler_data, &info);
voice->types.ogg.position = 0;
2017-02-23 04:00:40 +01:00
voice->types.ogg.samples = voice->types.ogg.buf_samples;
2017-02-22 23:54:28 +00:00
}
pcm = voice->types.ogg.buffer + voice->types.ogg.position;
if (voice->types.ogg.samples < buf_free)
{
for (i = voice->types.ogg.samples; i != 0; i--)
*buffer++ += *pcm++ * volume;
buf_free -= voice->types.ogg.samples;
goto again;
}
else
{
2017-02-23 05:23:34 +01:00
int i;
for (i = buf_free; i != 0; --i )
2017-02-22 23:54:28 +00:00
*buffer++ += *pcm++ * volume;
voice->types.ogg.position += buf_free;
voice->types.ogg.samples -= buf_free;
}
}
2017-02-23 05:56:31 +01:00
#endif
2017-02-22 23:54:28 +00:00
#ifdef HAVE_IBXM
static void audio_mixer_mix_mod(float* buffer, size_t num_frames,
audio_mixer_voice_t* voice,
float volume)
{
int i;
unsigned temp_samples = 0;
unsigned buf_free = num_frames * 2;
int* pcm = NULL;
if (voice->types.mod.position == voice->types.mod.samples)
{
again:
temp_samples = replay_get_audio( voice->types.mod.stream, voice->types.mod.buffer );
2017-08-04 16:09:53 +02:00
temp_samples *= 2; // stereo
if (temp_samples == 0)
{
if (voice->repeat)
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);
replay_seek( voice->types.mod.stream, 0);
goto again;
}
else
{
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
return;
}
}
voice->types.mod.position = 0;
voice->types.mod.samples = temp_samples;
}
pcm = voice->types.mod.buffer + voice->types.mod.position;
float samplef = 0.0f;
int samplei = 0;
if (voice->types.mod.samples < buf_free)
{
for (i = voice->types.mod.samples; i != 0; i--)
{
samplei = *pcm++ * volume;
samplef = (float)((int)samplei + 32768) / 65535.0f;
samplef = samplef * 2.0f - 1.0f;
*buffer++ = samplef;
}
buf_free -= voice->types.mod.samples;
goto again;
}
else
{
int i;
for (i = buf_free; i != 0; --i )
{
samplei = *pcm++ * volume;
samplef = (float)((int)samplei + 32768) / 65535.0f;
samplef = samplef * 2.0f - 1.0f;
*buffer++ = samplef;
}
voice->types.mod.position += buf_free;
voice->types.mod.samples -= buf_free;
}
}
#endif
2017-06-08 23:53:24 +02:00
void audio_mixer_mix(float* buffer, size_t num_frames, float volume_override, bool override)
2017-02-22 23:54:28 +00:00
{
unsigned i;
2017-02-23 04:00:40 +01:00
size_t j = 0;
float* sample = NULL;
2017-05-08 05:59:45 +02:00
audio_mixer_voice_t* voice = s_voices;
2017-02-22 23:54:28 +00:00
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_lock(s_locker);
2017-05-21 11:57:10 +01:00
#endif
2017-05-08 05:59:45 +02:00
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
2017-02-22 23:54:28 +00:00
{
2017-06-08 23:53:24 +02:00
float volume = (override) ? volume_override : voice->volume;
2017-05-08 20:35:02 +02:00
switch (voice->type)
{
case AUDIO_MIXER_TYPE_WAV:
audio_mixer_mix_wav(buffer, num_frames, voice, volume);
break;
case AUDIO_MIXER_TYPE_OGG:
2017-02-23 05:56:31 +01:00
#ifdef HAVE_STB_VORBIS
2017-05-08 20:35:02 +02:00
audio_mixer_mix_ogg(buffer, num_frames, voice, volume);
#endif
case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
audio_mixer_mix_mod(buffer, num_frames, voice, volume);
2017-02-23 05:56:31 +01:00
#endif
2017-05-08 20:35:02 +02:00
break;
case AUDIO_MIXER_TYPE_NONE:
break;
}
2017-02-22 23:54:28 +00:00
}
2017-05-21 11:57:10 +01:00
#ifdef HAVE_THREADS
slock_unlock(s_locker);
2017-05-21 11:57:10 +01:00
#endif
2017-02-22 23:54:28 +00:00
for (j = 0, sample = buffer; j < num_frames; j++, sample++)
{
if (*sample < -1.0f)
*sample = -1.0f;
else if (*sample > 1.0f)
*sample = 1.0f;
}
}