From 7ecd11a16995c52b60f89aeea8cd534f4c7131be Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Wed, 11 Jan 2023 18:58:15 +0100 Subject: [PATCH] - [WIP] Migrating to jail_audio --- chirp.cpp | 120 ++++++++++++++++++----- data/game.lua | 44 +++------ jail_audio.cpp | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ jail_audio.h | 30 ++++++ 4 files changed, 400 insertions(+), 53 deletions(-) create mode 100644 jail_audio.cpp create mode 100644 jail_audio.h diff --git a/chirp.cpp b/chirp.cpp index d6e12c4..05807db 100644 --- a/chirp.cpp +++ b/chirp.cpp @@ -13,7 +13,7 @@ #define MAX_CHANNELS 5 -typedef void (*waveform_t)(const uint16_t, const uint32_t, const uint8_t); //, uint8_t*, const uint8_t); +typedef void (*waveform_t)(const uint16_t, const uint32_t, uint8_t*, const uint8_t, const uint8_t); static waveform_t waveforms[6]; const static uint16_t lengths[10] = { 313, 625, 938, 1250, 1875, 2500, 3750, 5000, 7500, 10000 }; @@ -23,28 +23,35 @@ const float periods[108] = { 1348.49207, 1272.80688, 1201.37, 1133.94214, 1070.2 SDL_AudioDeviceID audio_device; uint8_t audio_state = AUDIO_NONE; +#define RELATIVE 0 +#define SEQUENTIAL 1 + struct instrument_t { uint8_t waveform = 0; int8_t volume[8] = {0,0,0,0,0,0,0,0}; + uint8_t volume_data = RELATIVE; int8_t pitch[8] = {0,0,0,0,0,0,0,0}; + uint8_t pitch_data = RELATIVE; }; struct channel_t { char* song = NULL; char* song_ptr = NULL; + char* song_start = NULL; uint32_t count = 0; float length = 0.25f; uint8_t volume = 32; uint8_t octave = 4; uint32_t tempo = 44100; uint8_t waveform = 0; + uint8_t instrument = 0; Uint8* play_pos; int32_t play_len; Uint8 play_buffer[132300]; char* stack[10] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; uint8_t stackpos = 0; char* labels[10] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; - instrument_t instruments[5]; + instrument_t instruments[10]; }; static channel_t channels[MAX_CHANNELS]; @@ -90,31 +97,31 @@ void audioCallback(void * userdata, uint8_t * stream, int len) { #define COUNT (channels[c].count++ % period) -void square_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { - for( uint32_t i = 0; i < length; i++ ) channels[c].play_buffer[i] = ( COUNT < (period >> 1) ? channels[c].volume : -channels[c].volume ); +void square_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { + for( uint32_t i = 0; i < length; i++ ) buffer[i] = ( COUNT < (period >> 1) ? volume : -volume ); } -void saw_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { - for( uint32_t i = 0; i < length; i++ ) channels[c].play_buffer[i] = -channels[c].volume + uint16_t( float(COUNT) / float(period) * channels[c].volume*2 ); +void saw_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { + for( uint32_t i = 0; i < length; i++ ) buffer[i] = -volume + uint16_t( float(COUNT) / float(period) * volume*2 ); } -void triangle_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { +void triangle_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { for( uint32_t i = 0; i < length; i++ ) { uint16_t pos = COUNT; uint16_t half_period = period >> 1; if (pos < half_period) { - channels[c].play_buffer[i] = -channels[c].volume + uint16_t( (float(pos) / float(half_period)) * float(channels[c].volume*2) ); + buffer[i] = -volume + uint16_t( (float(pos) / float(half_period)) * float(volume*2) ); } else { - channels[c].play_buffer[i] = channels[c].volume - uint16_t( (float(pos-half_period) / float(half_period)) * float(channels[c].volume*2) ); + buffer[i] = volume - uint16_t( (float(pos-half_period) / float(half_period)) * float(volume*2) ); } } } -void pulse12_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { - for( uint32_t i = 0; i < length; i++ ) channels[c].play_buffer[i] = ( COUNT < (period >> 3) ? channels[c].volume : -channels[c].volume ); +void pulse12_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { + for( uint32_t i = 0; i < length; i++ ) buffer[i] = ( COUNT < (period >> 3) ? volume : -volume ); } -void pulse25_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { - for( uint32_t i = 0; i < length; i++ ) channels[c].play_buffer[i] = ( COUNT < (period >> 2) ? channels[c].volume : -channels[c].volume ); +void pulse25_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { + for( uint32_t i = 0; i < length; i++ ) buffer[i] = ( COUNT < (period >> 2) ? volume : -volume ); } -void pulse75_waveform(const uint16_t period, const uint32_t length, const uint8_t c) { - for( uint32_t i = 0; i < length; i++ ) channels[c].play_buffer[i] = ( COUNT < uint16_t(period-(period >> 2)) ? channels[c].volume : -channels[c].volume ); +void noise_waveform(const uint16_t period, const uint32_t length, uint8_t* buffer, const uint8_t volume, const uint8_t c) { + for( uint32_t i = 0; i < length; i++ ) buffer[i] = rand()%2==0 ? volume : -volume; } void chirp_init() { @@ -126,17 +133,34 @@ void chirp_init() { waveforms[2] = &triangle_waveform; waveforms[3] = &pulse12_waveform; waveforms[4] = &pulse25_waveform; - waveforms[5] = &pulse75_waveform; + waveforms[5] = &noise_waveform; for (uint8_t i=0;i= 48 && param <= 57 ) { channels[c].tempo = tempos[param - 48] * 10; ++*token; } return 0; - case 'w': + case 'i': + param = *++*token; + if( param >= 48 && param <= 57 ) { channels[c].instrument = param - 48; ++*token; } + return 0; +/* case 'w': param = *++*token; if( param >= 48 && param <= 57 ) { channels[c].waveform = param - 48; ++*token; } + return 0;*/ + case '{': + { + uint8_t instrument = 0; + param = *++*token; + if( param >= 48 && param <= 57 ) { instrument = param - 48; param = *++*token; } + while (param != '}') { + switch (param) { + case 'w': + param = *++*token; + if( param >= 48 && param <= 57 ) { channels[c].instruments[instrument].waveform = param - 48; param = *++*token; } + break; + case 'v': + channels[c].instruments[instrument].volume_data = RELATIVE; + param = *++*token; + if (param=='r') { channels[c].instruments[instrument].volume_data = RELATIVE; param = *++*token; } + if (param=='s') { channels[c].instruments[instrument].volume_data = SEQUENTIAL; param = *++*token; } + for (int i=0;i<8;++i) { + int8_t sign = 1; + if (param=='-') { sign=-1; param = *++*token; } + if( param >= 48 && param <= 57 ) channels[c].instruments[instrument].volume[i] = (param-48)*sign; + param = *++*token; + } + break; + case 'p': + channels[c].instruments[instrument].pitch_data = RELATIVE; + param = *++*token; + if (param=='r') { channels[c].instruments[instrument].pitch_data = RELATIVE; param = *++*token; } + if (param=='s') { channels[c].instruments[instrument].pitch_data = SEQUENTIAL; param = *++*token; } + for (int i=0;i<8;++i) { + int8_t sign = 1; + if (param=='-') { sign=-1; param = *++*token; } + if( param >= 48 && param <= 57 ) channels[c].instruments[instrument].pitch[i] = (param-48)*sign; + param = *++*token; + } + break; + case '}': + default: + param = *++*token; + break; + } + //param = *++*token; + } + return 0; + } + case '!': + channels[c].stackpos = 0; + channels[c].song_start = ++*token; return 0; case '=': channels[c].stackpos = 0; - channels[c].song_ptr = channels[c].song; + channels[c].song_ptr = channels[c].song_start; return 0; case '[': param = *++*token; diff --git a/data/game.lua b/data/game.lua index faf1e46..1f87d2d 100644 --- a/data/game.lua +++ b/data/game.lua @@ -2,15 +2,9 @@ x=0 function _init() text="HOLA MINI" - --playchirp("l0v4o4fg#o5cv2o4fg#o5cv1o4fg#o5cv4o4fg#o5cv2o4fg#o5cv1o4fg#o5c") - - --playchirp("w3t4l0o2fv2fv4l1fv2l0fv1fv4l1fv2l0fv1fv4l1fv2l0fv1fv4fv2fv4l1fv2l0fv1fv4l1g#v2l0g#v1g#v4l1a#v2l0a#v1a#v4o3l1cv2l0cv1c") - --playchirp("t9 v4l0 o2fco1f#o0g# l6r o2l0f v3f v2f v1f v4l4r l0rv1rv4fv3fv2fv1fv4fco1f#o0g#l3ro2l0fco1f#o0g#l3ro2l0fv3fv2fv1fl6r=") - - playchirp("w4t9l0v4o3fg#o4cv2o3fg#o4cv1o3fg#o4cl4rl0rv4o3fg#o4cv2o3fg#o4cv1o3fg#o4cl4rl0r") - playchirp("w0t9 [0v4o2l0fv3fv2fv1fv4fv3fv2fv1fl3rv4l0fv3fv2fv1frv3rv2rv1rv4fv3fv2fv1fl3rv4l0fv3fv2fv1f][1v4fv3fv2fv1fv4l3rl0dv3dv2dv1dv4l3rl0d#v3d#v2d#v1d#v4l3rl0ev3ev2ev1el3r]@0[2v4fv3fv2fv1fv4l3rl0gv3gv2gv1gv4l3rl0g#v3g#v2g#v1g#v4l3rl0av3av2av1al3r]=") - playchirp("w3t9 v3l0o2fco1f#o0g# l6r o2l0nv3nv2nv1n l5r v4l0nv3nv2nv1n v3l0o2fco1f#o0g# l3r v3l0o2fco1f#o0g# l3r o2l0nv3nv2nv1n l6r=") - + playchirp("{0w3vs-10000000ps0-5-6-90000}{1w5v0-1-2-30000p00000000}!t9v4o2 l3i0f l6r i1l3f l5r l3f i0f r f r i1f l6r=") -- drums + playchirp("t9v4{0w0v0-1-2-30000}o2 ! l9 rrrrrrrr l3 [0 frrfg#rgr][1 frrfa#>cfrfrfr<] =") -- bass + playchirp("t9v1 {1w0v44422211p037037037} l9 rrrrrrrrrrrrrrrr i1 [0 l5frfl3rl5dl3rl5]") -- arp end function _update() @@ -19,26 +13,14 @@ function _update() x=x+1 if x>160 then x=-strlen(text)*4 end end -0 x -1 xx -2 xxx -3 xxxx -4 xxxxxx -5 xxxxxxxx -6 xxxxxxxxxxxx -7 xxxxxxxxxxxxxxxx -8 xxxxxxxxxxxxxxxxxxxxxxxx -9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -t9 {1w4v44422211a037037037} i1 l5 f l4 r l0 r l5 f l4 r l0 r - -w4 t9 -l0 -v4 o3f g# o4c -v2 o3f g# o4c -v1 o3f g# o4c -l4r l0r -v4 o3f g# o4c -v2 o3f g# o4c -v1 o3f g# o4c -l4r l0r \ No newline at end of file +-- 0 * +-- 1 ** +-- 2 *** +-- 3 **** +-- 4 ****** +-- 5 ******** +-- 6 ************ +-- 7 **************** +-- 8 ************************ +-- 9 ******************************** \ No newline at end of file diff --git a/jail_audio.cpp b/jail_audio.cpp new file mode 100644 index 0000000..eaa2fd9 --- /dev/null +++ b/jail_audio.cpp @@ -0,0 +1,259 @@ +#include "jail_audio.h" +#include "stb_vorbis.c" +#include +#include +#include "jfile.h" + +#define JA_MAX_SIMULTANEOUS_CHANNELS 5 + +struct JA_Sound_t { + Uint32 length {0}; + Uint8* buffer {NULL}; +}; + +struct JA_Channel_t { + JA_Sound 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 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_volume = 128; +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*2-current_music->pos); + SDL_MixAudioFormat(stream, (Uint8*)(current_music->output+current_music->pos), AUDIO_S16, size, JA_volume); + 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_volume); + 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_volume/2); + 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_volume/2); + 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) { + 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); + SDL_PauseAudioDevice(sdlAudioDevice, 0); +} + +void JA_Quit() { + SDL_PauseAudioDevice(sdlAudioDevice, 1); + if (sdlAudioDevice != 0) SDL_CloseAudioDevice(sdlAudioDevice); + sdlAudioDevice = 0; +} + +JA_Music JA_LoadMusic(const char* filename) { + int chan, samplerate; + JA_Music music = new JA_Music_t(); + + int fsize; + Uint8 *buffer = (Uint8*)file_getfilebuffer(filename, fsize); + + // [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); + fread(buffer, fsize, 1, f); + fclose(f); + */ + + music->samples = stb_vorbis_decode_memory(buffer, fsize, &chan, &samplerate, &music->output); + free(buffer); + // [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); + 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; +} + +void JA_PlayMusic(JA_Music music, const int loop) { + 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 (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; + current_music->state = JA_MUSIC_PAUSED; +} + +void JA_ResumeMusic() { + if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; + current_music->state = JA_MUSIC_PLAYING; +} + +void JA_StopMusic() { + 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 (current_music == NULL) return JA_MUSIC_INVALID; + return current_music->state; +} + +void JA_DeleteMusic(JA_Music music) { + if (current_music == music) current_music = NULL; + free(music->output); + delete music; +} + +JA_Sound JA_NewSound(Uint8* buffer, Uint32 length) { + JA_Sound sound = new JA_Sound_t(); + sound->buffer = buffer; + sound->length = length; + return sound; +} + +JA_Sound JA_LoadSound(const char* filename) { + JA_Sound sound = new JA_Sound_t(); + SDL_AudioSpec wavSpec; + + //SDL_LoadWAV(filename, &wavSpec, &sound->buffer, &sound->length); + int size; + char *buffer = file_getfilebuffer(filename, size); + SDL_LoadWAV_RW(SDL_RWFromMem(buffer, size),1, &wavSpec, &sound->buffer, &sound->length); + free(buffer); + + 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 sound, const int loop) { + 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 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 (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 (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 (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 (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID; + return channels[channel].state; +} + +int JA_SetVolume(int volume) { + JA_volume = volume > 128 ? 128 : volume < 0 ? 0 : volume; + return JA_volume; +} \ No newline at end of file diff --git a/jail_audio.h b/jail_audio.h new file mode 100644 index 0000000..7d03400 --- /dev/null +++ b/jail_audio.h @@ -0,0 +1,30 @@ +#pragma once +#include + +enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED }; +enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED }; + +typedef struct JA_Sound_t *JA_Sound; +typedef struct JA_Music_t *JA_Music; + +void JA_Init(const int freq, const SDL_AudioFormat format, const int channels); +void JA_Quit(); + +JA_Music JA_LoadMusic(const char* filename); +void JA_PlayMusic(JA_Music music, const int loop = -1); +void JA_PauseMusic(); +void JA_ResumeMusic(); +void JA_StopMusic(); +JA_Music_state JA_GetMusicState(); +void JA_DeleteMusic(JA_Music music); + +JA_Sound JA_NewSound(Uint8* buffer, Uint32 length); +JA_Sound JA_LoadSound(const char* filename); +int JA_PlaySound(JA_Sound sound, const int loop = 0); +void JA_PauseChannel(const int channel); +void JA_ResumeChannel(const int channel); +void JA_StopChannel(const int channel); +JA_Channel_state JA_GetChannelState(const int channel); +void JA_DeleteSound(JA_Sound sound); + +int JA_SetVolume(int volume); \ No newline at end of file