#ifndef JA_USESDLMIXER #include "jail_audio.h" #include "stb_vorbis.h" #include #include #define JA_MAX_SIMULTANEOUS_CHANNELS 5 struct JA_Sound_t { Uint32 length {0}; Uint8* buffer {NULL}; }; struct JA_Channel_t { JA_Sound_t *sound; int pos {0}; int times {0}; JA_Channel_state state { JA_CHANNEL_FREE }; }; struct JA_Music_t { int samples {0}; int pos {0}; int times {0}; short* output {NULL}; JA_Music_state state {JA_MUSIC_INVALID}; }; JA_Music_t *current_music{NULL}; JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS]; int JA_freq {48000}; SDL_AudioFormat JA_format {AUDIO_S16}; Uint8 JA_channels {2}; int JA_musicVolume = 128; int JA_soundVolume = 64; bool JA_musicEnabled = true; bool JA_soundEnabled = true; SDL_AudioDeviceID sdlAudioDevice = 0; void audioCallback(void * userdata, uint8_t * stream, int len) { SDL_memset(stream, 0, len); if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) { const int size = SDL_min(len, (current_music->samples-current_music->pos)*2); SDL_MixAudioFormat(stream, (Uint8*)(current_music->output+current_music->pos), AUDIO_S16, size, JA_musicVolume); current_music->pos += size/2; if (size < len) { if (current_music->times != 0) { SDL_MixAudioFormat(stream+size, (Uint8*)current_music->output, AUDIO_S16, len-size, JA_musicVolume); current_music->pos = (len-size)/2; if (current_music->times > 0) current_music->times--; } else { current_music->pos = 0; current_music->state = JA_MUSIC_STOPPED; } } } // Mixar els channels mi amol for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state == JA_CHANNEL_PLAYING) { const int size = SDL_min(len, channels[i].sound->length - channels[i].pos); SDL_MixAudioFormat(stream, channels[i].sound->buffer + channels[i].pos, AUDIO_S16, size, JA_soundVolume); channels[i].pos += size; if (size < len) { if (channels[i].times != 0) { SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len-size, JA_soundVolume); channels[i].pos = len-size; if (channels[i].times > 0) channels[i].times--; } else { JA_StopChannel(i); } } } } } void JA_Init(const int freq, const SDL_AudioFormat format, const int channels) { #ifdef DEBUG SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); #endif SDL_Log("Iniciant JailAudio..."); JA_freq = freq; JA_format = format; JA_channels = channels; SDL_AudioSpec audioSpec{JA_freq, JA_format, JA_channels, 0, 1024, 0, 0, audioCallback, NULL}; if (sdlAudioDevice != 0) SDL_CloseAudioDevice(sdlAudioDevice); sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0); if (sdlAudioDevice==0) { SDL_Log("FAILED!\n"); SDL_Log("Failed to initialize SDL audio!\n"); } else { SDL_Log("OK!\n"); } SDL_PauseAudioDevice(sdlAudioDevice, 0); } void JA_Quit() { SDL_PauseAudioDevice(sdlAudioDevice, 1); if (sdlAudioDevice != 0) SDL_CloseAudioDevice(sdlAudioDevice); sdlAudioDevice = 0; } JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length) { int chan, samplerate; JA_Music_t *music = new JA_Music_t(); music->samples = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &music->output); // [RZC 28/08/22] Abans el descomprimiem mentre el teniem obert // music->samples = stb_vorbis_decode_filename(filename, &chan, &samplerate, &music->output); SDL_AudioCVT cvt; SDL_BuildAudioCVT(&cvt, AUDIO_S16, chan, samplerate, JA_format, JA_channels, JA_freq); SDL_Log("Music length: %f\n", float(music->samples)/float(JA_freq)); if (cvt.needed) { cvt.len = music->samples * chan * 2; cvt.buf = (Uint8 *) SDL_malloc(cvt.len * cvt.len_mult); SDL_memcpy(cvt.buf, music->output, cvt.len); SDL_ConvertAudio(&cvt); free(music->output); music->output = (short*)cvt.buf; } music->pos = 0; music->state = JA_MUSIC_STOPPED; return music; } JA_Music_t *JA_LoadMusic(const char* filename) { // [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid. FILE *f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); Uint8 *buffer = (Uint8*)malloc(fsize + 1); if (fread(buffer, fsize, 1, f)!=1) return NULL; fclose(f); JA_Music_t *music = JA_LoadMusic(buffer, fsize); free(buffer); return music; } void JA_PlayMusic(JA_Music_t *music, const int loop) { if (!JA_musicEnabled) return; if (current_music != NULL) { current_music->pos = 0; current_music->state = JA_MUSIC_STOPPED; } current_music = music; current_music->pos = 0; current_music->state = JA_MUSIC_PLAYING; current_music->times = loop; } void JA_PauseMusic() { if (!JA_musicEnabled) return; if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; current_music->state = JA_MUSIC_PAUSED; } void JA_ResumeMusic() { if (!JA_musicEnabled) return; if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; current_music->state = JA_MUSIC_PLAYING; } void JA_StopMusic() { if (!JA_musicEnabled) return; if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; current_music->pos = 0; current_music->state = JA_MUSIC_STOPPED; } JA_Music_state JA_GetMusicState() { if (!JA_musicEnabled) return JA_MUSIC_DISABLED; if (current_music == NULL) return JA_MUSIC_INVALID; return current_music->state; } void JA_DeleteMusic(JA_Music_t *music) { if (current_music == music) current_music = NULL; free(music->output); delete music; } int JA_SetMusicVolume(int volume) { JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0 : volume; return JA_musicVolume; } void JA_SetMusicPosition(float value) { if (!current_music) return; current_music->pos = value * JA_freq; } float JA_GetMusicPosition() { if (!current_music) return 0; return float(current_music->pos)/float(JA_freq); } void JA_EnableMusic(const bool value) { if (!value && current_music != NULL && current_music->state==JA_MUSIC_PLAYING) JA_StopMusic(); JA_musicEnabled = value; } JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length) { JA_Sound_t *sound = new JA_Sound_t(); sound->buffer = buffer; sound->length = length; return sound; } JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size) { JA_Sound_t *sound = new JA_Sound_t(); SDL_AudioSpec wavSpec; SDL_LoadWAV_RW(SDL_RWFromMem(buffer, size),1, &wavSpec, &sound->buffer, &sound->length); SDL_AudioCVT cvt; SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq); cvt.len = sound->length; cvt.buf = (Uint8 *) SDL_malloc(cvt.len * cvt.len_mult); SDL_memcpy(cvt.buf, sound->buffer, sound->length); SDL_ConvertAudio(&cvt); SDL_FreeWAV(sound->buffer); sound->buffer = cvt.buf; sound->length = cvt.len_cvt; return sound; } JA_Sound_t *JA_LoadSound(const char* filename) { JA_Sound_t *sound = new JA_Sound_t(); SDL_AudioSpec wavSpec; SDL_LoadWAV(filename, &wavSpec, &sound->buffer, &sound->length); SDL_AudioCVT cvt; SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq); cvt.len = sound->length; cvt.buf = (Uint8 *) SDL_malloc(cvt.len * cvt.len_mult); SDL_memcpy(cvt.buf, sound->buffer, sound->length); SDL_ConvertAudio(&cvt); SDL_FreeWAV(sound->buffer); sound->buffer = cvt.buf; sound->length = cvt.len_cvt; return sound; } int JA_PlaySound(JA_Sound_t *sound, const int loop) { if (!JA_soundEnabled) return -1; int channel = 0; while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; } if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) channel = 0; channels[channel].sound = sound; channels[channel].times = loop; channels[channel].pos = 0; channels[channel].state = JA_CHANNEL_PLAYING; return channel; } void JA_DeleteSound(JA_Sound_t *sound) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].sound == sound) JA_StopChannel(i); } SDL_free(sound->buffer); delete sound; } void JA_PauseChannel(const int channel) { if (!JA_soundEnabled) return; if (channel == -1) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state == JA_CHANNEL_PLAYING) channels[i].state = JA_CHANNEL_PAUSED; } } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { if (channels[channel].state == JA_CHANNEL_PLAYING) channels[channel].state = JA_CHANNEL_PAUSED; } } void JA_ResumeChannel(const int channel) { if (!JA_soundEnabled) return; if (channel == -1) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state == JA_CHANNEL_PAUSED) channels[i].state = JA_CHANNEL_PLAYING; } } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { if (channels[channel].state == JA_CHANNEL_PAUSED) channels[channel].state = JA_CHANNEL_PLAYING; } } void JA_StopChannel(const int channel) { if (!JA_soundEnabled) return; if (channel == -1) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { channels[i].state = JA_CHANNEL_FREE; channels[i].pos = 0; channels[i].sound = NULL; } } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { channels[channel].state = JA_CHANNEL_FREE; channels[channel].pos = 0; channels[channel].sound = NULL; } } JA_Channel_state JA_GetChannelState(const int channel) { if (!JA_soundEnabled) return JA_SOUND_DISABLED; if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID; return channels[channel].state; } int JA_SetSoundVolume(int volume) { JA_soundVolume = volume > 128 ? 128 : volume < 0 ? 0 : volume; return JA_soundVolume; } void JA_EnableSound(const bool value) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i); } JA_soundEnabled = value; } int JA_SetVolume(int volume) { JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0 : volume; JA_soundVolume = JA_musicVolume/2; return JA_musicVolume; } #endif