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 };
|
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){
|
m_audio(m_set("music/mod65.xm"),
|
||||||
.repeat = true,
|
m_opt(channel, "soundtrack"),
|
||||||
.volume = 1.0f
|
m_opt(repeat, true));
|
||||||
});
|
|
||||||
|
|
||||||
input_set_mouse_captured(&ctx.input, 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
|
#define TWN_AUDIO_H
|
||||||
|
|
||||||
#include "twn_engine_api.h"
|
#include "twn_engine_api.h"
|
||||||
|
#include "twn_option.h"
|
||||||
|
|
||||||
#include <stdbool.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 {
|
typedef struct PlayAudioArgs {
|
||||||
/* default: false */
|
char *path;
|
||||||
bool repeat;
|
|
||||||
/* crossfade between already playing audio on a given channel, if any */
|
m_option_list(
|
||||||
/* default: false */
|
char *, channel,
|
||||||
bool crossfade;
|
bool, repeat,
|
||||||
/* range: 0.0f to 1.0f */
|
float, volume,
|
||||||
/* default: 1.0f */
|
float, panning )
|
||||||
float volume;
|
|
||||||
/* range: -1.0 to 1.0f */
|
|
||||||
/* default: 0.0f */
|
|
||||||
float panning;
|
|
||||||
} PlayAudioArgs;
|
} PlayAudioArgs;
|
||||||
|
|
||||||
|
TWN_API void audio_play_args(PlayAudioArgs args);
|
||||||
/* plays audio file at specified channel or anywhere if NULL is passed */
|
#define m_audio(...) (audio_play_args((PlayAudioArgs){__VA_ARGS__}))
|
||||||
/* path must contain valid file extension to infer which file format it is */
|
|
||||||
/* supported formats: .ogg, .xm */
|
#endif
|
||||||
/* 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);
|
|
||||||
|
|
||||||
#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) {
|
static AudioFileType infer_audio_file_type(const char *path) {
|
||||||
size_t path_len = SDL_strlen(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);
|
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 file_type = infer_audio_file_type(path);
|
AudioFileType file_type = infer_audio_file_type(path);
|
||||||
AudioChannel new_channel = {
|
AudioChannel new_channel = {
|
||||||
.args = args,
|
|
||||||
.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 = repeat,
|
||||||
|
.volume = volume,
|
||||||
|
.panning = panning,
|
||||||
};
|
};
|
||||||
shput(ctx.audio_channels, channel, new_channel);
|
shput(ctx.audio_channels, channel, new_channel);
|
||||||
pair = shgetp_null(ctx.audio_channels, 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);
|
AudioChannelItem *pair = shgetp_null(ctx.audio_channels, channel);
|
||||||
if (!pair)
|
if (!pair) {
|
||||||
return NULL;
|
log_warn("No channel by the name of %s to set a parameter for", channel);
|
||||||
|
return;
|
||||||
return &pair->value.args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (param) {
|
||||||
PlayAudioArgs audio_get_default_args(void) {
|
case AUDIO_PARAM_REPEAT:
|
||||||
return (PlayAudioArgs){
|
pair->value.repeat = (bool)value;
|
||||||
.repeat = false,
|
break;
|
||||||
.crossfade = false,
|
case AUDIO_PARAM_VOLUME:
|
||||||
.volume = 1.0f,
|
if (value > 1.0f) {
|
||||||
.panning = 0.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 sa = (float *)a;
|
||||||
float *const sb = (float *)b;
|
float *const sb = (float *)b;
|
||||||
|
|
||||||
const float left_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->args.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) {
|
for (size_t s = 0; s < frames; s += 2) {
|
||||||
/* left channel */
|
/* left channel */
|
||||||
sa[s] += (float)(sb[s] * channel->args.volume * left_panning);
|
sa[s] += (float)(sb[s] * channel->volume * left_panning);
|
||||||
|
|
||||||
/* right channel */
|
/* 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 */
|
/* handle end of file */
|
||||||
if (samples_per_channel == 0) {
|
if (samples_per_channel == 0) {
|
||||||
if (channel->args.repeat) {
|
if (channel->repeat) {
|
||||||
/* 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;
|
||||||
@ -284,7 +300,7 @@ static void audio_sample_and_mixin_channel(const AudioChannel *channel,
|
|||||||
|
|
||||||
/* handle end of file */
|
/* handle end of file */
|
||||||
if (samples_per_channel == 0) {
|
if (samples_per_channel == 0) {
|
||||||
if (channel->args.repeat) {
|
if (channel->repeat) {
|
||||||
/* 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;
|
||||||
@ -313,10 +329,10 @@ static void audio_sample_and_mixin_channel(const AudioChannel *channel,
|
|||||||
|
|
||||||
|
|
||||||
static void sanity_check_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);
|
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);
|
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);
|
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 {
|
typedef struct AudioChannel {
|
||||||
PlayAudioArgs args;
|
|
||||||
AudioFileType file_type;
|
AudioFileType file_type;
|
||||||
union AudioContext context; /* interpreted by `file_type` value */
|
union AudioContext context; /* interpreted by `file_type` value */
|
||||||
const char *path;
|
const char *path;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
bool repeat;
|
||||||
|
float volume;
|
||||||
|
float panning;
|
||||||
} AudioChannel;
|
} AudioChannel;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user