#include "jaudio.h" #include "jfile.h" #include "stb_vorbis.h" #include #include namespace audio { #define MAX_CHANNELS 5 struct sound { SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 }; Uint32 length { 0 }; Uint8 *buffer { NULL }; }; struct channel { audio::sound *sound { nullptr }; int pos { 0 }; int times { 0 }; SDL_AudioStream *stream { nullptr }; audio::state state { audio::state::available }; }; struct music { SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 }; Uint32 length { 0 }; Uint8 *buffer { nullptr }; char *filename { nullptr }; int pos { 0 }; int times { 0 }; SDL_AudioStream *stream { nullptr }; audio::state state { audio::state::invalid }; }; audio::music *current_music { nullptr }; audio::channel channels[MAX_CHANNELS]; SDL_AudioSpec audio_spec { SDL_AUDIO_S16, 2, 48000 }; float music_volume { 1.0f }; float sound_volume { 0.5f }; bool music_enabled { true }; bool sound_enabled { true }; SDL_AudioDeviceID audio_device { 0 }; SDL_TimerID timer_id { 0 }; bool fading { false }; int fade_start_time; int fade_duration; int fade_initial_volume; Uint32 updateCallback(void *userdata, SDL_TimerID timer_id, Uint32 interval) { if (music_enabled && current_music && current_music->state == audio::state::playing) { if (fading) { const int time = SDL_GetTicks(); if (time > (fade_start_time+fade_duration)) { fading = false; stopMusic(); return 30; } else { const int time_passed = time - fade_start_time; const float percent = (float)time_passed / (float)fade_duration; SDL_SetAudioStreamGain(current_music->stream, music_volume*(1.0 - percent)); } } if (current_music->times != 0) { if (SDL_GetAudioStreamAvailable(current_music->stream) < int(current_music->length/2)) { SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length); } if (current_music->times>0) current_music->times--; } else { if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) stopMusic(); } } if (sound_enabled) { for (int i=0; i < MAX_CHANNELS; ++i) if (channels[i].state == audio::state::playing) { if (channels[i].times != 0) { if (SDL_GetAudioStreamAvailable(channels[i].stream) < int(channels[i].sound->length/2)) { SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length); if (channels[i].times>0) channels[i].times--; } } else { if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) stopChannel(i); } } } return 30; } void init(const int freq, const SDL_AudioFormat format, const int num_channels) { #ifdef DEBUG SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); #endif SDL_Log("Iniciant JailAudio..."); audio_spec = {format, num_channels, freq }; if (!audio_device) SDL_CloseAudioDevice(audio_device); audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec); SDL_Log( (audio_device==0) ? "Failed to initialize SDL audio!\n" : "OK!\n"); for (int i=0;ilength = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2; music->spec.channels = chan; music->spec.freq = samplerate; music->spec.format = SDL_AUDIO_S16; music->buffer = (Uint8*)SDL_malloc(music->length); SDL_memcpy(music->buffer, output, music->length); free(output); music->pos = 0; music->state = audio::state::stopped; return music; } audio::music *loadMusic(const char* filename) { int size; char *buffer = file_getfilebuffer(filename, size); if (buffer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadMusic): No s'ha trobat l'arxiu %s\n", filename); exit(1); } audio::music *music = loadMusic((Uint8*)buffer, size); music->filename = (char*)malloc(strlen(filename)+1); strcpy(music->filename, filename); free(buffer); return music; } void playMusic(audio::music *music, const int loop) { if (!music_enabled) return; stopMusic(); current_music = music; current_music->pos = 0; current_music->state = audio::state::playing; current_music->times = loop; current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &audio_spec); if (!current_music->stream) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_CreateAudioStream: %s\n", SDL_GetError()); exit(1); } if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_PutAudioStreamData: %s\n", SDL_GetError()); exit(1); } SDL_SetAudioStreamGain(current_music->stream, music_volume); if (!SDL_BindAudioStream(audio_device, current_music->stream)) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_BindAudioStream: %s\n", SDL_GetError()); exit(1); } } void loadAndPlayMusic(const char* filename, const bool force_reinit) { if ( (getMusicState() != audio::state::playing) || (strcmp(getMusicFilename(), filename)!=0) || force_reinit ) { playMusic(loadMusic(filename)); } } char *getMusicFilename(audio::music *music) { if (!music) music = current_music; return music->filename; } void pauseMusic() { if (!music_enabled) return; if (!current_music || current_music->state == audio::state::invalid) return; current_music->state = audio::state::paused; SDL_UnbindAudioStream(current_music->stream); } void resumeMusic() { if (!music_enabled) return; if (!current_music || current_music->state == audio::state::invalid) return; current_music->state = audio::state::playing; SDL_BindAudioStream(audio_device, current_music->stream); } void stopMusic() { if (!music_enabled) return; if (!current_music || current_music->state == audio::state::invalid) return; current_music->pos = 0; current_music->state = audio::state::stopped; SDL_DestroyAudioStream(current_music->stream); current_music->stream = nullptr; free(current_music->filename); current_music->filename = nullptr; } void fadeOutMusic(const int milliseconds) { if (!music_enabled) return; if (current_music == NULL || current_music->state == audio::state::invalid) return; fading = true; fade_start_time = SDL_GetTicks(); fade_duration = milliseconds; fade_initial_volume = music_volume; } audio::state getMusicState() { if (!music_enabled) return audio::state::disabled; if (!current_music) return audio::state::invalid; return current_music->state; } void deleteMusic(audio::music *music) { if (current_music == music) current_music = nullptr; SDL_free(music->buffer); if (music->stream) SDL_DestroyAudioStream(music->stream); delete music; } float setMusicVolume(float volume) { music_volume = SDL_clamp( volume, 0.0f, 1.0f ); if (current_music) SDL_SetAudioStreamGain(current_music->stream, music_volume); return music_volume; } void setMusicPosition(float value) { if (!current_music) return; current_music->pos = value * current_music->spec.freq; } float getMusicPosition() { if (!current_music) return 0; return float(current_music->pos)/float(current_music->spec.freq); } void enableMusic(const bool value) { if ( !value && current_music && (current_music->state==audio::state::playing) ) stopMusic(); music_enabled = value; } audio::sound *newSound(Uint8* buffer, Uint32 length) { audio::sound *sound = new audio::sound(); sound->buffer = buffer; sound->length = length; return sound; } audio::sound *loadSound(uint8_t* buffer, uint32_t size) { audio::sound *sound = new audio::sound(); SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length); return sound; } audio::sound *loadSound(const char* filename) { int size; uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename, size); if (buffer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadSound): No s'ha trobat l'arxiu %s\n", filename); exit(1); } audio::sound *sound = loadSound(buffer, size); free(buffer); return sound; } int playSound(audio::sound *sound, const int loop) { if (!sound_enabled) return -1; int channel = 0; while (channel < MAX_CHANNELS && channels[channel].state != audio::state::available) { channel++; } if (channel == MAX_CHANNELS) channel = 0; stopChannel(channel); channels[channel].sound = sound; channels[channel].times = loop; channels[channel].pos = 0; channels[channel].state = audio::state::playing; channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec); SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length); SDL_SetAudioStreamGain(channels[channel].stream, sound_volume); SDL_BindAudioStream(audio_device, channels[channel].stream); return channel; } int playSoundOnChannel(audio::sound *sound, const int channel, const int loop) { if (!sound_enabled) return -1; if (channel < 0 || channel >= MAX_CHANNELS) return -1; stopChannel(channel); channels[channel].sound = sound; channels[channel].times = loop; channels[channel].pos = 0; channels[channel].state = audio::state::playing; channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec); SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length); SDL_SetAudioStreamGain(channels[channel].stream, sound_volume); SDL_BindAudioStream(audio_device, channels[channel].stream); return channel; } void deleteSound(audio::sound *sound) { for (int i = 0; i < MAX_CHANNELS; i++) { if (channels[i].sound == sound) stopChannel(i); } SDL_free(sound->buffer); delete sound; } void pauseChannel(const int channel) { if (!sound_enabled) return; if (channel == -1) { for (int i = 0; i < MAX_CHANNELS; i++) if (channels[i].state == audio::state::playing) { channels[i].state = audio::state::paused; SDL_UnbindAudioStream(channels[i].stream); } } else if (channel >= 0 && channel < MAX_CHANNELS) { if (channels[channel].state == audio::state::playing) { channels[channel].state = audio::state::paused; SDL_UnbindAudioStream(channels[channel].stream); } } } void resumeChannel(const int channel) { if (!sound_enabled) return; if (channel == -1) { for (int i = 0; i < MAX_CHANNELS; i++) if (channels[i].state == audio::state::paused) { channels[i].state = audio::state::playing; SDL_BindAudioStream(audio_device, channels[i].stream); } } else if (channel >= 0 && channel < MAX_CHANNELS) { if (channels[channel].state == audio::state::paused) { channels[channel].state = audio::state::playing; SDL_BindAudioStream(audio_device, channels[channel].stream); } } } void stopChannel(const int channel) { if (!sound_enabled) return; if (channel == -1) { for (int i = 0; i < MAX_CHANNELS; i++) { if (channels[i].state != audio::state::available) SDL_DestroyAudioStream(channels[i].stream); channels[i].stream = nullptr; channels[i].state = audio::state::available; channels[i].pos = 0; channels[i].sound = NULL; } } else if (channel >= 0 && channel < MAX_CHANNELS) { if (channels[channel].state != audio::state::available) SDL_DestroyAudioStream(channels[channel].stream); channels[channel].stream = nullptr; channels[channel].state = audio::state::available; channels[channel].pos = 0; channels[channel].sound = NULL; } } audio::state getChannelState(const int channel) { if (!sound_enabled) return audio::state::disabled; if (channel < 0 || channel >= MAX_CHANNELS) return audio::state::invalid; return channels[channel].state; } float setSoundVolume(float volume) { sound_volume = SDL_clamp( volume, 0.0f, 1.0f ); for (int i = 0; i < MAX_CHANNELS; i++) if ( (channels[i].state == audio::state::playing) || (channels[i].state == audio::state::paused) ) SDL_SetAudioStreamGain(channels[i].stream, sound_volume); return sound_volume; } void enableSound(const bool value) { for (int i = 0; i < MAX_CHANNELS; i++) { if (channels[i].state == audio::state::playing) stopChannel(i); } sound_enabled = value; } float setVolume(float volume) { setSoundVolume(setMusicVolume(volume) / 2.0f); return music_volume; } #undef MAX_CHANNELS }