twn_audio: rework interface

This commit is contained in:
veclav talica 2024-10-07 15:21:44 +03:00
parent cc1e64531c
commit ae8cc5f50b
5 changed files with 96 additions and 94 deletions

View File

@ -106,10 +106,9 @@ Scene *ingame_scene(State *state) {
new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
audio_play_ex("music/mod65.xm", "soundtrack", (PlayAudioArgs){
.repeat = true,
.volume = 1.0f
});
m_audio(m_set("music/mod65.xm"),
m_opt(channel, "soundtrack"),
m_opt(repeat, true));
input_set_mouse_captured(&ctx.input, true);

View File

@ -1,31 +0,0 @@
ORIGIN = { x = 320, y = 180 }
RADIUS = 48
offset = { x = 0, y = 0 }
angle = 0
function game_tick()
rectangle {
rect = { x = 0, y = 0, w = 640, h = 360 },
color = { r = 127, g = 0, b = 127, a = 255 },
}
sprite {
path = "/assets/title.png",
rect = {
x = 320 - (320 / 2),
y = 180 - (128 / 2),
w = 320,
h = 128,
},
}
text {
string = "IT KEEPS HAPPENING",
position = offset,
font = "/fonts/kenney-pixel.ttf",
}
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
angle = angle + 0.1
end

View File

@ -2,37 +2,45 @@
#define TWN_AUDIO_H
#include "twn_engine_api.h"
#include "twn_option.h"
#include <stdbool.h>
/* plays audio file at specified channel or at scratch channel if NULL is passed, without ability to refer to it later */
/* path path must contain valid file extension to infer which file format it is */
/* supported formats: .ogg, .xm */
TWN_API void audio_play(const char *path,
const char *channel, /* optional */
bool repeat, /* default: false */
float volume, /* default: 1.0f, range: 0.0f to 1.0f */
float panning); /* default: 0.0f, range: -1.0 to 1.0f */
typedef enum {
AUDIO_PARAM_REPEAT,
AUDIO_PARAM_VOLUME,
AUDIO_PARAM_PANNING,
} AudioParam;
TWN_API void audio_set(const char *channel, AudioParam param, float value);
/* TODO */
// TWN_API bool audio_ended(const char *channel);
#ifndef TWN_NOT_C
typedef struct PlayAudioArgs {
/* default: false */
bool repeat;
/* crossfade between already playing audio on a given channel, if any */
/* default: false */
bool crossfade;
/* range: 0.0f to 1.0f */
/* default: 1.0f */
float volume;
/* range: -1.0 to 1.0f */
/* default: 0.0f */
float panning;
char *path;
m_option_list(
char *, channel,
bool, repeat,
float, volume,
float, panning )
} PlayAudioArgs;
/* plays audio file at specified channel or anywhere if NULL is passed */
/* path must contain valid file extension to infer which file format it is */
/* supported formats: .ogg, .xm */
/* preserves args that are already specified on the channel */
TWN_API void audio_play(const char *path, const char *channel);
TWN_API void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args);
/* could be used for modifying args */
/* warn: is only valid if no other calls to audio are made */
TWN_API PlayAudioArgs *audio_get_args(const char *channel);
TWN_API PlayAudioArgs audio_get_default_args(void);
TWN_API void audio_play_args(PlayAudioArgs args);
#define m_audio(...) (audio_play_args((PlayAudioArgs){__VA_ARGS__}))
#endif
#endif

View File

@ -54,15 +54,6 @@ static int64_t get_audio_data(const char *path, unsigned char **data) {
}
void audio_play(const char *path, const char *channel) {
const AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
if (!pair)
audio_play_ex(path, channel, audio_get_default_args());
else
audio_play_ex(path, channel, pair->value.args);
}
static AudioFileType infer_audio_file_type(const char *path) {
size_t path_len = SDL_strlen(path);
@ -163,18 +154,25 @@ static void repeat_audio(AudioChannel *channel) {
}
void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args) {
void audio_play(const char *path,
const char *channel,
bool repeat,
float volume,
float panning)
{
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
/* create a channel if it doesn't exist */
if (!pair) {
AudioFileType file_type = infer_audio_file_type(path);
AudioChannel new_channel = {
.args = args,
.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);
@ -188,22 +186,40 @@ void audio_play_ex(const char *path, const char *channel, PlayAudioArgs args) {
}
PlayAudioArgs *audio_get_args(const char *channel) {
TWN_API void audio_set(const char *channel, AudioParam param, float value) {
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
if (!pair)
return NULL;
return &pair->value.args;
if (!pair) {
log_warn("No channel by the name of %s to set a parameter for", channel);
return;
}
PlayAudioArgs audio_get_default_args(void) {
return (PlayAudioArgs){
.repeat = false,
.crossfade = false,
.volume = 1.0f,
.panning = 0.0f,
};
switch (param) {
case AUDIO_PARAM_REPEAT:
pair->value.repeat = (bool)value;
break;
case AUDIO_PARAM_VOLUME:
if (value > 1.0f) {
log_warn("Out of range volume for channel %s set", channel);
value = 1.0f;
}
if (value < 0.0f) {
log_warn("Out of range volume for channel %s set", channel);
value = 0.0f;
}
pair->value.volume = value;
break;
case AUDIO_PARAM_PANNING:
if (value > 1.0f) {
log_warn("Out of range panning for channel %s set", channel);
value = 1.0f;
}
if (value < -1.0f) {
log_warn("Out of range panning for channel %s set", channel);
value = -1.0f;
}
pair->value.panning = value;
break;
}
}
@ -216,15 +232,15 @@ static void audio_mixin_streams(const AudioChannel *channel,
float *const sa = (float *)a;
float *const sb = (float *)b;
const float left_panning = fminf(fabsf(channel->args.panning - 1.0f), 1.0f);
const float right_panning = fminf(fabsf(channel->args.panning + 1.0f), 1.0f);
const float left_panning = fminf(fabsf(channel->panning - 1.0f), 1.0f);
const float right_panning = fminf(fabsf(channel->panning + 1.0f), 1.0f);
for (size_t s = 0; s < frames; s += 2) {
/* left channel */
sa[s] += (float)(sb[s] * channel->args.volume * left_panning);
sa[s] += (float)(sb[s] * channel->volume * left_panning);
/* right channel */
sa[s + 1] += (float)(sb[s + 1] * channel->args.volume * right_panning);
sa[s + 1] += (float)(sb[s + 1] * channel->volume * right_panning);
}
}
@ -253,7 +269,7 @@ static void audio_sample_and_mixin_channel(const AudioChannel *channel,
/* handle end of file */
if (samples_per_channel == 0) {
if (channel->args.repeat) {
if (channel->repeat) {
/* seek to start and try sampling some more */
stb_vorbis_seek_start(channel->context.vorbis.handle);
continue;
@ -284,7 +300,7 @@ static void audio_sample_and_mixin_channel(const AudioChannel *channel,
/* handle end of file */
if (samples_per_channel == 0) {
if (channel->args.repeat) {
if (channel->repeat) {
/* seek to start and try sampling some more */
xm_restart(channel->context.xm.handle);
continue;
@ -313,10 +329,10 @@ static void audio_sample_and_mixin_channel(const AudioChannel *channel,
static void sanity_check_channel(const AudioChannel *channel) {
if (channel->args.volume < 0.0f || channel->args.volume > 1.0f)
if (channel->volume < 0.0f || channel->volume > 1.0f)
log_warn("Volume argument is out of range for channel (%s)", channel->name);
if (channel->args.panning < -1.0f || channel->args.panning > 1.0f)
if (channel->panning < -1.0f || channel->panning > 1.0f)
log_warn("Panning argument is out of range for channel (%s)", channel->name);
}
@ -332,3 +348,11 @@ void audio_callback(void *userdata, uint8_t *stream, int len) {
audio_sample_and_mixin_channel(&ctx.audio_channels[i].value, stream, len);
}
}
TWN_API void audio_play_args(PlayAudioArgs args) {
const char *channel = m_or(args, channel, NULL);
const bool repeat = m_or(args, repeat, false);
const float volume = m_or(args, volume, 1.0f);
const float panning = m_or(args, panning, 0.0f);
audio_play(args.path, channel, repeat, volume, panning);
}

View File

@ -36,11 +36,13 @@ union AudioContext {
typedef struct AudioChannel {
PlayAudioArgs args;
AudioFileType file_type;
union AudioContext context; /* interpreted by `file_type` value */
const char *path;
const char *name;
bool repeat;
float volume;
float panning;
} AudioChannel;