twn_audio: rework interface
This commit is contained in:
parent
cc1e64531c
commit
ae8cc5f50b
@ -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);
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user