twn_audio: rework interface
This commit is contained in:
		| @@ -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; | ||||
|     if (!pair) { | ||||
|         log_warn("No channel by the name of %s to set a parameter for", channel); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     return &pair->value.args; | ||||
| } | ||||
|  | ||||
|  | ||||
| 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; | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user