Compare commits

..

No commits in common. "eefd53a63057db00103737b92a53b3e683c42a42" and "3052bb693ae3563a372106aed78a956b35149e08" have entirely different histories.

4 changed files with 25 additions and 173 deletions

View File

@ -7,7 +7,6 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <stb_ds.h> #include <stb_ds.h>
#include <physfs.h> #include <physfs.h>
#include <physfsrwops.h>
#define STB_VORBIS_NO_PUSHDATA_API #define STB_VORBIS_NO_PUSHDATA_API
#define STB_VORBIS_HEADER_ONLY #define STB_VORBIS_HEADER_ONLY
@ -18,7 +17,6 @@
static const char *audio_exts[AUDIO_FILE_TYPE_COUNT] = { static const char *audio_exts[AUDIO_FILE_TYPE_COUNT] = {
".ogg", /* AUDIO_FILE_TYPE_OGG */ ".ogg", /* AUDIO_FILE_TYPE_OGG */
".wav", /* AUDIO_FILE_TYPE_WAV */
".xm", /* AUDIO_FILE_TYPE_XM */ ".xm", /* AUDIO_FILE_TYPE_XM */
}; };
@ -75,7 +73,6 @@ static AudioFileType infer_audio_file_type(const char *path) {
/* TODO: error propagation and clearing of resources on partial success? */ /* TODO: error propagation and clearing of resources on partial success? */
/* or should we expect things to simply fail? */ /* or should we expect things to simply fail? */
/* TODO: reuse often used decoded/decompressed data */
static union AudioContext init_audio_context(const char *path, AudioFileType type) { static union AudioContext init_audio_context(const char *path, AudioFileType type) {
switch (type) { switch (type) {
case AUDIO_FILE_TYPE_OGG: { case AUDIO_FILE_TYPE_OGG: {
@ -105,25 +102,6 @@ static union AudioContext init_audio_context(const char *path, AudioFileType typ
}; };
} }
/* TODO: transform to destination format immediately? */
case AUDIO_FILE_TYPE_WAV: {
SDL_AudioSpec spec;
uint8_t *data;
uint32_t len;
if (!SDL_LoadWAV_RW(PHYSFSRWOPS_openRead(path), 1, &spec, &data, &len)) {
CRY_SDL("Cannot load .wav file:");
break;
}
return (union AudioContext) {
.wav = {
.position = 0,
.samples = data,
.spec = spec
}
};
}
case AUDIO_FILE_TYPE_XM: { case AUDIO_FILE_TYPE_XM: {
unsigned char *data; unsigned char *data;
int64_t len = get_audio_data(path, &data); int64_t len = get_audio_data(path, &data);
@ -160,18 +138,6 @@ static union AudioContext init_audio_context(const char *path, AudioFileType typ
} }
static void free_audio_channel(AudioChannel channel) {
switch (channel.file_type) {
case AUDIO_FILE_TYPE_WAV: {
SDL_free(channel.context.wav.samples);
break;
}
default:
break;
}
}
static void repeat_audio(AudioChannel *channel) { static void repeat_audio(AudioChannel *channel) {
switch (channel->file_type) { switch (channel->file_type) {
case AUDIO_FILE_TYPE_OGG: { case AUDIO_FILE_TYPE_OGG: {
@ -179,11 +145,6 @@ static void repeat_audio(AudioChannel *channel) {
break; break;
} }
case AUDIO_FILE_TYPE_WAV: {
channel->context.wav.position = 0;
break;
}
case AUDIO_FILE_TYPE_XM: { case AUDIO_FILE_TYPE_XM: {
xm_restart(channel->context.xm.handle); xm_restart(channel->context.xm.handle);
break; break;
@ -204,48 +165,29 @@ void audio_play(const char *path,
float volume, float volume,
float panning) float panning)
{ {
if (channel) { AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
/* create a channel if it doesn't exist */ /* create a channel if it doesn't exist */
if (!pair) { if (!pair) {
AudioFileType const file_type = infer_audio_file_type(path); AudioFileType file_type = infer_audio_file_type(path);
AudioChannel new_channel = {
.file_type = file_type,
.context = init_audio_context(path, file_type),
.path = path,
.name = channel,
.repeat = repeat,
.volume = volume,
.panning = panning,
};
shput(ctx.audio_channels, channel, new_channel);
pair = shgetp_null(ctx.audio_channels, channel);
}
/* TODO: destroy and create new context when channel is reused for different file */
/* works for both restarts and new audio */
if (strcmp(pair->value.path, path) == 0)
repeat_audio(&pair->value);
} else {
/* audio without channel plays without repeat and ability to change parameters over its course, nor stop it */
AudioFileType const file_type = infer_audio_file_type(path);
AudioChannel new_channel = { AudioChannel new_channel = {
.file_type = file_type, .file_type = file_type,
.context = init_audio_context(path, file_type), .context = init_audio_context(path, file_type),
.path = path, .path = path,
.name = channel, .name = channel,
.repeat = false, .repeat = repeat,
.volume = volume, .volume = volume,
.panning = panning, .panning = panning,
}; };
shput(ctx.audio_channels, channel, new_channel);
if (repeat) pair = shgetp_null(ctx.audio_channels, channel);
log_warn("Cannot repeat audio played on unnamed scratch channel (for %s)", path);
arrpush(ctx.unnamed_audio_channels, new_channel);
} }
/* TODO: destroy and create new context when channel is reused for different file */
/* works for both restarts and new audio */
if (strcmp(pair->value.path, path) == 0)
repeat_audio(&pair->value);
} }
@ -301,12 +243,12 @@ static void audio_mixin_streams(const AudioChannel *channel,
/* remember: sample is data for all channels where frame is a part of it */ /* remember: sample is data for all channels where frame is a part of it */
static void audio_sample_and_mixin_channel(AudioChannel *channel, static void audio_sample_and_mixin_channel(const AudioChannel *channel,
uint8_t *stream, uint8_t *stream,
int len) int len)
{ {
static uint8_t buffer[16384]; static uint8_t buffer[16384];
const int float_buffer_frames = sizeof (buffer) / sizeof (float) / 2; const int float_buffer_frames = sizeof (buffer) / sizeof (float);
const int stream_frames = len / (int)(sizeof (float)); const int stream_frames = len / (int)(sizeof (float));
switch (channel->file_type) { switch (channel->file_type) {
@ -328,11 +270,9 @@ static void audio_sample_and_mixin_channel(AudioChannel *channel,
/* seek to start and try sampling some more */ /* seek to start and try sampling some more */
stb_vorbis_seek_start(channel->context.vorbis.handle); stb_vorbis_seek_start(channel->context.vorbis.handle);
continue; continue;
} else { } else
/* leave silence */ /* leave silence */
channel->finished = true;
break; break;
}
} }
/* panning and mixing */ /* panning and mixing */
@ -346,66 +286,6 @@ static void audio_sample_and_mixin_channel(AudioChannel *channel,
break; break;
} }
case AUDIO_FILE_TYPE_WAV: {
/* feed stream for needed conversions */
for (int i = 0; i < stream_frames; ) {
const int n_frames = (stream_frames - i) > float_buffer_frames ?
float_buffer_frames : stream_frames - i;
int const limit = MIN(n_frames, channel->context.wav.spec.samples);
switch (channel->context.wav.spec.format) {
case AUDIO_U16: {
if (channel->context.wav.spec.channels == 1) {
for (int x = 0; x < limit; ++x) {
((float *)buffer)[x * 2 + 0] = (float)((uint16_t *)channel->context.wav.samples)[x] / (float)UINT16_MAX;
((float *)buffer)[x * 2 + 1] = (float)((uint16_t *)channel->context.wav.samples)[x] / (float)UINT16_MAX;
}
}
break;
}
case AUDIO_S16: {
if (channel->context.wav.spec.channels == 1) {
for (int x = 0; x < limit; ++x) {
if ((float)((int16_t *)channel->context.wav.samples)[x] < 0) {
((float *)buffer)[x * 2 + 0] = (float)((int16_t *)channel->context.wav.samples)[x] / (float)INT16_MIN;
((float *)buffer)[x * 2 + 1] = (float)((int16_t *)channel->context.wav.samples)[x] / (float)INT16_MIN;
} else {
((float *)buffer)[x * 2 + 0] = (float)((int16_t *)channel->context.wav.samples)[x] / (float)INT16_MAX;
((float *)buffer)[x * 2 + 1] = (float)((int16_t *)channel->context.wav.samples)[x] / (float)INT16_MAX;
}
}
}
break;
}
default:
log_warn("Unsupported .wav PCM format (%x), producing silence", channel->context.wav.spec.format);
return;
}
/* panning and mixing */
audio_mixin_streams(channel, &stream[i * sizeof(float)], buffer, limit * 2);
channel->context.wav.position += limit;
if (channel->context.wav.position == channel->context.wav.spec.samples) {
if (channel->repeat)
channel->context.wav.position = 0;
else {
/* leave silence */
channel->finished = true;
break;
}
}
i += limit * 2;
}
break;
}
case AUDIO_FILE_TYPE_XM: { case AUDIO_FILE_TYPE_XM: {
for (int i = 0; i < stream_frames; ) { for (int i = 0; i < stream_frames; ) {
const int n_frames = (stream_frames - i) > float_buffer_frames ? const int n_frames = (stream_frames - i) > float_buffer_frames ?
@ -421,11 +301,9 @@ static void audio_sample_and_mixin_channel(AudioChannel *channel,
/* seek to start and try sampling some more */ /* seek to start and try sampling some more */
xm_restart(channel->context.xm.handle); xm_restart(channel->context.xm.handle);
continue; continue;
} else { } else
channel->finished = true;
/* leave silence */ /* leave silence */
break; break;
}
} }
/* panning and mixing */ /* panning and mixing */
@ -468,23 +346,8 @@ void audio_callback(void *userdata, uint8_t *stream, int len) {
sanity_check_channel(&ctx.audio_channels[i].value); sanity_check_channel(&ctx.audio_channels[i].value);
audio_sample_and_mixin_channel(&ctx.audio_channels[i].value, stream, len); audio_sample_and_mixin_channel(&ctx.audio_channels[i].value, stream, len);
} }
for (int i = 0; i < arrlen(ctx.unnamed_audio_channels); ++i) {
sanity_check_channel(&ctx.unnamed_audio_channels[i]);
audio_sample_and_mixin_channel(&ctx.unnamed_audio_channels[i], stream, len);
}
/* ditch finished unnamed */
int i = 0;
while (i < arrlen(ctx.unnamed_audio_channels)) {
if (ctx.unnamed_audio_channels[i].finished) {
free_audio_channel(ctx.unnamed_audio_channels[i]);
arrdelswap(ctx.unnamed_audio_channels, i);
} else i++;
}
} }
TWN_API void audio_play_args(PlayAudioArgs args) { TWN_API void audio_play_args(PlayAudioArgs args) {
const char *channel = m_or(args, channel, NULL); const char *channel = m_or(args, channel, NULL);
const bool repeat = m_or(args, repeat, false); const bool repeat = m_or(args, repeat, false);

View File

@ -13,12 +13,9 @@
#define AUDIO_FREQUENCY 48000 #define AUDIO_FREQUENCY 48000
/* TODO: specify which PCM formats are usable with WAV */
/* TODO: specify limitations of libxm */
/* TODO: specify limitations of stb_vorbis */
typedef enum AudioFileType { typedef enum AudioFileType {
AUDIO_FILE_TYPE_OGG, AUDIO_FILE_TYPE_OGG,
AUDIO_FILE_TYPE_WAV,
AUDIO_FILE_TYPE_XM, AUDIO_FILE_TYPE_XM,
AUDIO_FILE_TYPE_COUNT, AUDIO_FILE_TYPE_COUNT,
AUDIO_FILE_TYPE_UNKNOWN, AUDIO_FILE_TYPE_UNKNOWN,
@ -33,12 +30,6 @@ union AudioContext {
uint8_t channel_count; uint8_t channel_count;
} vorbis; } vorbis;
struct {
void *samples;
SDL_AudioSpec spec;
size_t position;
} wav;
struct { struct {
xm_context_t *handle; xm_context_t *handle;
} xm; } xm;
@ -53,7 +44,6 @@ typedef struct AudioChannel {
bool repeat; bool repeat;
float volume; float volume;
float panning; float panning;
bool finished;
} AudioChannel; } AudioChannel;

View File

@ -53,7 +53,6 @@ typedef struct EngineContext {
/* audio */ /* audio */
AudioChannelItem *audio_channels; AudioChannelItem *audio_channels;
AudioChannel *unnamed_audio_channels;
SDL_AudioDeviceID audio_device; SDL_AudioDeviceID audio_device;
int audio_stream_frequency; int audio_stream_frequency;
SDL_AudioFormat audio_stream_format; SDL_AudioFormat audio_stream_format;

View File

@ -68,11 +68,12 @@ static SDL_Surface *gen_missing_texture_surface(void) {
} }
/* same data is reused after being allocated/generated once */ /* same data is reused after being allocated/generated once */
SDL_Surface *missing_texture_surface = SDL_CreateRGBSurfaceFrom(missing_texture_data, 64, 64, SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(missing_texture_data, 64, 64,
3 * 8, 3 * 8,
64 * 3, 64 * 3,
rmask, gmask, bmask, 0); rmask, gmask, bmask, 0);
return missing_texture_surface;
return surface;
} }
@ -327,8 +328,7 @@ void textures_cache_deinit(TextureCache *cache) {
/* free cache hashes */ /* free cache hashes */
for (size_t i = 0; i < shlenu(cache->hash); ++i) { for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.data->pixels != missing_texture_data) stbi_image_free(cache->hash[i].value.data->pixels);
stbi_image_free(cache->hash[i].value.data->pixels);
SDL_FreeSurface(cache->hash[i].value.data); SDL_FreeSurface(cache->hash[i].value.data);
} }
shfree(cache->hash); shfree(cache->hash);