.ogg playback
This commit is contained in:
		@@ -19,10 +19,13 @@ add_subdirectory(third-party/physfs)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
set(SOURCE_FILES
 | 
					set(SOURCE_FILES
 | 
				
			||||||
        third-party/physfs/extras/physfsrwops.c
 | 
					        third-party/physfs/extras/physfsrwops.c
 | 
				
			||||||
 | 
					        third-party/stb/stb_vorbis.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        src/config.h
 | 
					        src/config.h
 | 
				
			||||||
        src/context.h
 | 
					        src/context.h
 | 
				
			||||||
        src/context.c
 | 
					        src/context.c
 | 
				
			||||||
        src/main.c
 | 
					        src/main.c
 | 
				
			||||||
 | 
					        src/audio.c
 | 
				
			||||||
        src/util.c src/util.h
 | 
					        src/util.c src/util.h
 | 
				
			||||||
        src/rendering.c src/rendering.h
 | 
					        src/rendering.c src/rendering.h
 | 
				
			||||||
        src/textures.c src/textures.h
 | 
					        src/textures.c src/textures.h
 | 
				
			||||||
@@ -75,8 +78,7 @@ else()
 | 
				
			|||||||
                -Wshadow
 | 
					                -Wshadow
 | 
				
			||||||
                -Wdouble-promotion
 | 
					                -Wdouble-promotion
 | 
				
			||||||
                -Wconversion -Wno-sign-conversion
 | 
					                -Wconversion -Wno-sign-conversion
 | 
				
			||||||
                -Werror=vla
 | 
					                -Werror=vla)
 | 
				
			||||||
                -Werror=alloca)
 | 
					 | 
				
			||||||
        set(BUILD_FLAGS
 | 
					        set(BUILD_FLAGS
 | 
				
			||||||
                # these SHOULDN'T break anything...
 | 
					                # these SHOULDN'T break anything...
 | 
				
			||||||
                -fno-math-errno
 | 
					                -fno-math-errno
 | 
				
			||||||
@@ -88,6 +90,7 @@ else()
 | 
				
			|||||||
                -flto)
 | 
					                -flto)
 | 
				
			||||||
        set(BUILD_FLAGS_DEBUG
 | 
					        set(BUILD_FLAGS_DEBUG
 | 
				
			||||||
                -g3
 | 
					                -g3
 | 
				
			||||||
 | 
					                -gdwarf
 | 
				
			||||||
                -fsanitize-trap=undefined)
 | 
					                -fsanitize-trap=undefined)
 | 
				
			||||||
        target_compile_options(${PROJECT_NAME} PRIVATE
 | 
					        target_compile_options(${PROJECT_NAME} PRIVATE
 | 
				
			||||||
                                               ${WARNING_FLAGS}
 | 
					                                               ${WARNING_FLAGS}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								data/music/test.ogg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/music/test.ogg
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										228
									
								
								src/audio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/audio.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,228 @@
 | 
				
			|||||||
 | 
					#include "private/audio.h"
 | 
				
			||||||
 | 
					#include "audio.h"
 | 
				
			||||||
 | 
					#include "context.h"
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL2/SDL.h>
 | 
				
			||||||
 | 
					#include <stb_ds.h>
 | 
				
			||||||
 | 
					#include <physfs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define STB_VORBIS_HEADER_ONLY
 | 
				
			||||||
 | 
					#include <stb_vorbis.c>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* TODO: default to float sampling format? */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *audio_exts[audio_file_type_count] = {
 | 
				
			||||||
 | 
					    ".ogg", /* audio_file_type_ogg */
 | 
				
			||||||
 | 
					    ".xm",  /* audio_file_type_xm */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* TODO: count frames without use, free the memory when threshold is met */
 | 
				
			||||||
 | 
					/* stores path to data hash, useful for sound effects */
 | 
				
			||||||
 | 
					static struct audio_file_cache {
 | 
				
			||||||
 | 
					    char *key;
 | 
				
			||||||
 | 
					    struct audio_file_cache_value {
 | 
				
			||||||
 | 
					        unsigned char *data;
 | 
				
			||||||
 | 
					        size_t len;
 | 
				
			||||||
 | 
					    } value;
 | 
				
			||||||
 | 
					} *audio_file_cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int64_t get_audio_data(const char *path, unsigned char **data) {
 | 
				
			||||||
 | 
					    const struct audio_file_cache *cache = shgetp_null(audio_file_cache, path);
 | 
				
			||||||
 | 
					    if (!cache) {
 | 
				
			||||||
 | 
					        unsigned char *file;
 | 
				
			||||||
 | 
					        int64_t len = file_to_bytes(path, &file);
 | 
				
			||||||
 | 
					        if (len == -1) {
 | 
				
			||||||
 | 
					            CRY("Audio error", "Error reading file");
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const struct audio_file_cache_value value = { file, (size_t)len };
 | 
				
			||||||
 | 
					        shput(audio_file_cache, path, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *data = file;
 | 
				
			||||||
 | 
					        return len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *data = cache->value.data;
 | 
				
			||||||
 | 
					    return (int64_t)cache->value.len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void play_audio(const char *path, const char *channel) {
 | 
				
			||||||
 | 
					    const struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel);
 | 
				
			||||||
 | 
					    if (!pair)
 | 
				
			||||||
 | 
					        play_audio_ex(path, channel, get_default_audio_args());
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        play_audio_ex(path, channel, pair->value.args);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static t_audio_file_type infer_audio_file_type(const char *path) {
 | 
				
			||||||
 | 
					    size_t path_len = strlen(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < audio_file_type_count; ++i) {
 | 
				
			||||||
 | 
					        size_t ext_length = strlen(audio_exts[i]);
 | 
				
			||||||
 | 
					        if (path_len <= ext_length)
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (strcmp(&path[path_len - ext_length], audio_exts[i]) == 0)
 | 
				
			||||||
 | 
					            return (t_audio_file_type)i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return audio_file_type_unknown;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* TODO: error propagation and clearing of resources on partial success? */
 | 
				
			||||||
 | 
					/*       or should we expect things to simply fail? */
 | 
				
			||||||
 | 
					static union audio_context init_audio_context(const char *path, t_audio_file_type type) {
 | 
				
			||||||
 | 
					    switch (type) {
 | 
				
			||||||
 | 
					    case audio_file_type_ogg: {
 | 
				
			||||||
 | 
					        unsigned char *data;
 | 
				
			||||||
 | 
					        int64_t len = get_audio_data(path, &data);
 | 
				
			||||||
 | 
					        if (len == -1) {
 | 
				
			||||||
 | 
					            CRY("Audio error", "Error reading file");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int error = 0;
 | 
				
			||||||
 | 
					        stb_vorbis* handle = stb_vorbis_open_memory(data, (int)len, &error, NULL);
 | 
				
			||||||
 | 
					        if (error != 0) {
 | 
				
			||||||
 | 
					            CRY("Audio error", "Error reading .ogg file");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stb_vorbis_info info = stb_vorbis_get_info(handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (union audio_context){
 | 
				
			||||||
 | 
					            .vorbis = {
 | 
				
			||||||
 | 
					                .data = data,
 | 
				
			||||||
 | 
					                .handle = handle,
 | 
				
			||||||
 | 
					                .frequency = info.sample_rate,
 | 
				
			||||||
 | 
					                .channel_count = (uint8_t)info.channels,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        CRY("Audio error", "Unhandled audio format (in init)");
 | 
				
			||||||
 | 
					        return (union audio_context){0};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (union audio_context){0};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void repeat_audio(struct audio_channel *channel) {
 | 
				
			||||||
 | 
					    (void)channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* TODO */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void play_audio_ex(const char *path, const char *channel, t_play_audio_args args) {
 | 
				
			||||||
 | 
					    struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* create a channel if it doesn't exist */
 | 
				
			||||||
 | 
					    if (!pair) {
 | 
				
			||||||
 | 
					        t_audio_file_type file_type = infer_audio_file_type(path);
 | 
				
			||||||
 | 
					        struct audio_channel new_channel = {
 | 
				
			||||||
 | 
					            .args = args,
 | 
				
			||||||
 | 
					            .file_type = file_type,
 | 
				
			||||||
 | 
					            .context = init_audio_context(path, file_type),
 | 
				
			||||||
 | 
					            .path = path,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					t_play_audio_args get_default_audio_args(void) {
 | 
				
			||||||
 | 
					    return (t_play_audio_args){
 | 
				
			||||||
 | 
					        .repeat = false,
 | 
				
			||||||
 | 
					        .crossfade = false,
 | 
				
			||||||
 | 
					        .volume = 1.0f,
 | 
				
			||||||
 | 
					        .panning = 0.0f,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void audio_sample_and_mixin_channel(struct audio_channel *channel,
 | 
				
			||||||
 | 
					                                           uint8_t *stream,
 | 
				
			||||||
 | 
					                                           int len) {
 | 
				
			||||||
 | 
					    int16_t *const sstream = (int16_t *)stream;
 | 
				
			||||||
 | 
					    static uint8_t buffer[16384];
 | 
				
			||||||
 | 
					    const int buffer_frames = sizeof (buffer) / sizeof (int16_t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (channel->file_type) {
 | 
				
			||||||
 | 
					    case audio_file_type_ogg: {
 | 
				
			||||||
 | 
					        /* feed stream for needed conversions */
 | 
				
			||||||
 | 
					        for (int i = 0; i < (len / 2);) {
 | 
				
			||||||
 | 
					            const int n_frames = ((len / 2) - i) > buffer_frames ? buffer_frames : (len / 2) - i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* TODO: handle end of file */
 | 
				
			||||||
 | 
					            const int samples_per_channel = stb_vorbis_get_samples_short_interleaved(
 | 
				
			||||||
 | 
					                channel->context.vorbis.handle,
 | 
				
			||||||
 | 
					                channel->context.vorbis.channel_count,
 | 
				
			||||||
 | 
					                (int16_t *)buffer,
 | 
				
			||||||
 | 
					                n_frames);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* panning and mixing */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if AUDIO_N_CHANNELS == 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (int s = 0; s < samples_per_channel * ctx.audio_stream_channel_count; s += 2) {
 | 
				
			||||||
 | 
					                /* left channel */
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    const float panning = fminf(fabsf(channel->args.panning - 1.0f), 1.0f);
 | 
				
			||||||
 | 
					                    const float volume = channel->args.volume * panning;
 | 
				
			||||||
 | 
					                    sstream[i + s] += (int16_t)(((int16_t *)buffer)[s] * volume);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /* right channel */
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    const float panning = fminf(fabsf(channel->args.panning + 1.0f), 1.0f);
 | 
				
			||||||
 | 
					                    const float volume = channel->args.volume * panning;
 | 
				
			||||||
 | 
					                    sstream[i + s + 1] += (int16_t)(((int16_t *)buffer)[s + 1] * volume);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#error "Unimplemented channel count"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            i += samples_per_channel * ctx.audio_stream_channel_count;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        CRY("Audio error", "Unhandled audio format (in sampling)");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void audio_callback(void *userdata, uint8_t *stream, int len) {
 | 
				
			||||||
 | 
					    (void)userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* prepare for mixing */
 | 
				
			||||||
 | 
					    SDL_memset(stream, sizeof (uint16_t), len * ctx.audio_stream_channel_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < shlen(ctx.audio_channels); ++i) {
 | 
				
			||||||
 | 
					        audio_sample_and_mixin_channel(&ctx.audio_channels[i].value, stream, len);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/audio.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#ifndef AUDIO_H
 | 
				
			||||||
 | 
					#define AUDIO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct play_audio_args {
 | 
				
			||||||
 | 
					    /* 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: 100.0f */
 | 
				
			||||||
 | 
					    float volume;
 | 
				
			||||||
 | 
					    /* range: -1.0 to 1.0f */
 | 
				
			||||||
 | 
					    /* default: 0.0f */
 | 
				
			||||||
 | 
					    float panning;
 | 
				
			||||||
 | 
					} t_play_audio_args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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 */
 | 
				
			||||||
 | 
					void play_audio(const char *path, const char *channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void play_audio_ex(const char *path, const char *channel, t_play_audio_args args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_audio_args(const char *channel, t_play_audio_args args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					t_play_audio_args get_audio_args(const char *channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					t_play_audio_args get_default_audio_args(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -24,6 +24,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define NUM_KEYBIND_SLOTS 8
 | 
					#define NUM_KEYBIND_SLOTS 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AUDIO_FREQUENCY 48000
 | 
				
			||||||
 | 
					#define AUDIO_N_CHANNELS 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 1024 * 1024 */
 | 
					/* 1024 * 1024 */
 | 
				
			||||||
/* #define UMKA_STACK_SIZE 1048576 */
 | 
					/* #define UMKA_STACK_SIZE 1048576 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
#include "context.h"
 | 
					#include "context.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
t_ctx ctx;
 | 
					t_ctx ctx = {0};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,11 @@ typedef struct context {
 | 
				
			|||||||
    struct rect_primitive *render_queue_rectangles;
 | 
					    struct rect_primitive *render_queue_rectangles;
 | 
				
			||||||
    struct circle_primitive *render_queue_circles;
 | 
					    struct circle_primitive *render_queue_circles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct audio_channel *audio_channels;
 | 
					    struct audio_channel_pair *audio_channels;
 | 
				
			||||||
 | 
					    SDL_AudioDeviceID audio_device;
 | 
				
			||||||
 | 
					    int audio_stream_frequency;
 | 
				
			||||||
 | 
					    SDL_AudioFormat audio_stream_format;
 | 
				
			||||||
 | 
					    uint8_t audio_stream_channel_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct circle_radius_cache_item *circle_radius_hash;
 | 
					    struct circle_radius_cache_item *circle_radius_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@
 | 
				
			|||||||
#include "title.h"
 | 
					#include "title.h"
 | 
				
			||||||
#include "scene.h"
 | 
					#include "scene.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../audio.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ingame_tick(struct state *state) {
 | 
					static void ingame_tick(struct state *state) {
 | 
				
			||||||
    struct scene_ingame *scn = (struct scene_ingame *)state->scene;
 | 
					    struct scene_ingame *scn = (struct scene_ingame *)state->scene;
 | 
				
			||||||
@@ -28,5 +30,10 @@ struct scene *ingame_scene(struct state *state) {
 | 
				
			|||||||
    new_scene->world = world_create();
 | 
					    new_scene->world = world_create();
 | 
				
			||||||
    new_scene->player = player_create(new_scene->world);
 | 
					    new_scene->player = player_create(new_scene->world);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    play_audio_ex("music/test.ogg", "soundtrack", (t_play_audio_args){
 | 
				
			||||||
 | 
					        .volume = 0.8f,
 | 
				
			||||||
 | 
					        .panning = -0.5f
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (struct scene *)new_scene;
 | 
					    return (struct scene *)new_scene;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/main.c
									
									
									
									
									
								
							@@ -4,10 +4,11 @@
 | 
				
			|||||||
#include "util.h"
 | 
					#include "util.h"
 | 
				
			||||||
#include "textures.h"
 | 
					#include "textures.h"
 | 
				
			||||||
#include "game/game.h"
 | 
					#include "game/game.h"
 | 
				
			||||||
 | 
					#include "private/audio.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <SDL2/SDL.h>
 | 
					#include <SDL2/SDL.h>
 | 
				
			||||||
#include <SDL2/SDL_image.h>
 | 
					 | 
				
			||||||
#include <SDL2/SDL_ttf.h>
 | 
					#include <SDL2/SDL_ttf.h>
 | 
				
			||||||
 | 
					#include <SDL2/SDL_image.h>
 | 
				
			||||||
#include <physfs.h>
 | 
					#include <physfs.h>
 | 
				
			||||||
#include <stb_ds.h>
 | 
					#include <stb_ds.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -179,6 +180,26 @@ static bool initialize(void) {
 | 
				
			|||||||
    SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
 | 
					    SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
 | 
				
			||||||
    SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
 | 
					    SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* audio initialization */
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_AudioSpec request, got;
 | 
				
			||||||
 | 
					    SDL_zero(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request.freq = AUDIO_FREQUENCY;
 | 
				
			||||||
 | 
					    request.format = AUDIO_S16;
 | 
				
			||||||
 | 
					    request.channels = AUDIO_N_CHANNELS;
 | 
				
			||||||
 | 
					    request.callback = audio_callback;
 | 
				
			||||||
 | 
					    /* TODO: check for errors */
 | 
				
			||||||
 | 
					    ctx.audio_device = SDL_OpenAudioDevice(NULL, 0, &request, &got, 0);
 | 
				
			||||||
 | 
					    ctx.audio_stream_format = got.format;
 | 
				
			||||||
 | 
					    ctx.audio_stream_frequency = got.freq;
 | 
				
			||||||
 | 
					    ctx.audio_stream_channel_count = got.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_PauseAudioDevice(ctx.audio_device, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* images */
 | 
					    /* images */
 | 
				
			||||||
    if (IMG_Init(IMG_INIT_PNG) == 0) {
 | 
					    if (IMG_Init(IMG_INIT_PNG) == 0) {
 | 
				
			||||||
        CRY_SDL("SDL_image initialization failed.");
 | 
					        CRY_SDL("SDL_image initialization failed.");
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								src/private/audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/private/audio.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					#ifndef PRIVATE_AUDIO_H
 | 
				
			||||||
 | 
					#define PRIVATE_AUDIO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../audio.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL2/SDL_audio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define STB_VORBIS_HEADER_ONLY
 | 
				
			||||||
 | 
					#include <stb_vorbis.c>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum audio_file_type {
 | 
				
			||||||
 | 
					    audio_file_type_ogg,
 | 
				
			||||||
 | 
					    audio_file_type_xm,
 | 
				
			||||||
 | 
					    audio_file_type_count,
 | 
				
			||||||
 | 
					    audio_file_type_unknown,
 | 
				
			||||||
 | 
					} t_audio_file_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					union audio_context {
 | 
				
			||||||
 | 
					    struct {
 | 
				
			||||||
 | 
					        stb_vorbis *handle;
 | 
				
			||||||
 | 
					        unsigned char *data;
 | 
				
			||||||
 | 
					        int frequency;
 | 
				
			||||||
 | 
					        uint8_t channel_count;
 | 
				
			||||||
 | 
					    } vorbis;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct audio_channel {
 | 
				
			||||||
 | 
					    t_play_audio_args args;
 | 
				
			||||||
 | 
					    enum audio_file_type file_type;
 | 
				
			||||||
 | 
					    union audio_context context; /* interpreted by `file_type` value */
 | 
				
			||||||
 | 
					    const char *path;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct audio_channel_pair {
 | 
				
			||||||
 | 
					    char *key;
 | 
				
			||||||
 | 
					    struct audio_channel value;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void audio_callback(void *userdata, uint8_t *stream, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -101,7 +101,7 @@ void *ccalloc(size_t num, size_t size) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int64_t file_to_bytes(char *path, unsigned char **buf_out) {
 | 
					int64_t file_to_bytes(const char *path, unsigned char **buf_out) {
 | 
				
			||||||
    SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
 | 
					    SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (handle == NULL) {
 | 
					    if (handle == NULL) {
 | 
				
			||||||
@@ -119,7 +119,7 @@ int64_t file_to_bytes(char *path, unsigned char **buf_out) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
char *file_to_str(char *path) {
 | 
					char *file_to_str(const char *path) {
 | 
				
			||||||
    SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
 | 
					    SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (handle == NULL) {
 | 
					    if (handle == NULL) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,10 +51,10 @@ void *ccalloc(size_t num, size_t size);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* sets buf_out to a pointer to a byte buffer which must be freed. */
 | 
					/* sets buf_out to a pointer to a byte buffer which must be freed. */
 | 
				
			||||||
/* returns the size of this buffer. */
 | 
					/* returns the size of this buffer. */
 | 
				
			||||||
int64_t file_to_bytes(char *path, unsigned char **buf_out);
 | 
					int64_t file_to_bytes(const char *path, unsigned char **buf_out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* returns a pointer to a string which must be freed */
 | 
					/* returns a pointer to a string which must be freed */
 | 
				
			||||||
char *file_to_str(char *path);
 | 
					char *file_to_str(const char *path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* returns true if str ends with suffix */
 | 
					/* returns true if str ends with suffix */
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user