diff --git a/inc/hashtable.h b/inc/hashtable.h new file mode 100644 index 0000000..c31518c --- /dev/null +++ b/inc/hashtable.h @@ -0,0 +1,20 @@ +typedef struct hashtable hashtable; +void hashtable_destroy(hashtable *t); +typedef struct hashtable_entry hashtable_entry; +hashtable_entry *hashtable_body_allocate(unsigned int capacity); +hashtable *hashtable_create(); +void hashtable_remove(hashtable *t,char *key); +void hashtable_resize(hashtable *t,unsigned int capacity); +void hashtable_set(hashtable *t,char *key,void *value); +void *hashtable_get(hashtable *t,char *key); +unsigned int hashtable_find_slot(hashtable *t,char *key); +unsigned long hashtable_hash(char *str); +struct hashtable { + unsigned int size; + unsigned int capacity; + hashtable_entry* body; +}; +struct hashtable_entry { + char* key; + void* value; +}; \ No newline at end of file diff --git a/inc/sound.h b/inc/sound.h new file mode 100644 index 0000000..4edf08b --- /dev/null +++ b/inc/sound.h @@ -0,0 +1,5 @@ +void sndInit(void); +void sndPlayBGM(char* filename); +void sndStopAll(void); +void sndPlaySFX(char* filename); +void sndUpdate(void); \ No newline at end of file diff --git a/res/filesystem/bgm21.it b/res/filesystem/bgm21.it new file mode 100644 index 0000000..b6ed846 Binary files /dev/null and b/res/filesystem/bgm21.it differ diff --git a/src/hashtable.c b/src/hashtable.c new file mode 100644 index 0000000..d0c6697 --- /dev/null +++ b/src/hashtable.c @@ -0,0 +1,159 @@ +/** + * Hashtable implementation + * (c) 2011 @marekweb + * + * Uses dynamic addressing with linear probing. + */ + +#include +#include +#include +#include +#include "hashtable.h" + +/* + * Interface section used for `makeheaders`. + */ +#if INTERFACE +struct hashtable_entry { + char* key; + void* value; +}; + +struct hashtable { + unsigned int size; + unsigned int capacity; + hashtable_entry* body; +}; +#endif + +#define HASHTABLE_INITIAL_CAPACITY 2 + +/** + * Compute the hash value for the given string. + * Implements the djb k=33 hash function. + */ +unsigned long hashtable_hash(char* str) +{ + unsigned long hash = 5381; + int c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + return hash; +} + +/** + * Find an available slot for the given key, using linear probing. + */ +unsigned int hashtable_find_slot(hashtable* t, char* key) +{ + int index = hashtable_hash(key) % t->capacity; + while (t->body[index].key != NULL && strcmp(t->body[index].key, key) != 0) { + index = (index + 1) % t->capacity; + } + return index; +} + +/** + * Return the item associated with the given key, or NULL if not found. + */ +void* hashtable_get(hashtable* t, char* key) +{ + int index = hashtable_find_slot(t, key); + if (t->body[index].key != NULL) { + return t->body[index].value; + } else { + return NULL; + } +} + +/** + * Assign a value to the given key in the table. + */ +void hashtable_set(hashtable* t, char* key, void* value) +{ + int index = hashtable_find_slot(t, key); + if (t->body[index].key != NULL) { + /* Entry exists; update it. */ + t->body[index].value = value; + } else { + t->size++; + /* Create a new entry */ + if ((float)t->size / t->capacity > 0.8) { + /* Resize the hash table */ + hashtable_resize(t, t->capacity * 2); + index = hashtable_find_slot(t, key); + } + t->body[index].key = key; + t->body[index].value = value; + } +} + +/** + * Remove a key from the table + */ +void hashtable_remove(hashtable* t, char* key) +{ + int index = hashtable_find_slot(t, key); + if (t->body[index].key != NULL) { + t->body[index].key = NULL; + t->body[index].value = NULL; + t->size--; + } +} + +/** + * Create a new, empty hashtable + */ +hashtable* hashtable_create() +{ + hashtable* new_ht = malloc(sizeof(hashtable)); + new_ht->size = 0; + new_ht->capacity = HASHTABLE_INITIAL_CAPACITY; + new_ht->body = hashtable_body_allocate(new_ht->capacity); + return new_ht; +} + +#if 0 +/** + * Adds all items from another table. + */ +hashtable* hashtable_merge(hashtable* ht, hashtable* other) +{ +} +#endif + +/** + * Allocate a new memory block with the given capacity. + */ +hashtable_entry* hashtable_body_allocate(unsigned int capacity) +{ + return (hashtable_entry*)calloc(capacity, sizeof(hashtable_entry)); +} + +/** + * Resize the allocated memory. + * Warning: clears the table of all entries. + */ +void hashtable_resize(hashtable* t, unsigned int capacity) +{ + assert(capacity >= t->size); + unsigned int old_capacity = t->capacity; + hashtable_entry* old_body = t->body; + t->body = hashtable_body_allocate(capacity); + t->capacity = capacity; + for (int i = 0; i < old_capacity; i++) { + if (old_body[i].key != NULL) { + hashtable_set(t, old_body[i].key, old_body[i].value); + } + } +} + +/** + * Destroy the table and deallocate it from memory. This does not deallocate the contained items. + */ +void hashtable_destroy(hashtable* t) +{ + free(t->body); + free(t); +} diff --git a/src/menu.c b/src/menu.c index cfdd3e9..41786cb 100644 --- a/src/menu.c +++ b/src/menu.c @@ -36,7 +36,7 @@ #include "menu.h" //sound -#include +#include "sound.h" #include "mp3.h" //debug @@ -2567,48 +2567,18 @@ void playSound(int snd) { //no thread support in libdragon yet, sounds pause the menu for a time :/ - //maybe global - int v1; - - static SAMPLE *add_sfx = NULL; if (snd == 1) - add_sfx = Sample_Load("rom://ed64_mono.wav"); + sndPlaySFX("rom://ed64_mono.wav"); if (snd == 2) - add_sfx = Sample_Load("rom://bamboo.wav"); + sndPlaySFX("rom://bamboo.wav"); if (snd == 3) - add_sfx = Sample_Load("rom://warning.wav"); + sndPlaySFX("rom://warning.wav"); if (snd == 4) - add_sfx = Sample_Load("rom://done.wav"); + sndPlaySFX("rom://done.wav"); - MikMod_SetNumVoices(-1, 2); - - if (!add_sfx) - { - MikMod_Exit(); - } - else - { - MikMod_EnableOutput(); - - audio_write_silence(); - audio_write_silence(); - - v1 = Sample_Play(add_sfx, 0, 0); - Voice_SetVolume(v1, 100); - - //maybe put update function into a int/vblank callback - for (int s = 0; s < 50; s++) - { - MikMod_Update(); - sleep(10); - } - - MikMod_DisableOutput(); - Sample_Free(add_sfx); - } } //draws the next char at the text input screen @@ -3355,21 +3325,11 @@ int main(void) if (sound_on) { - audio_init(44100, 2); //load soundsystem + audio_init(44100, 2); + sndInit(); + timer_init(); - - MikMod_RegisterAllDrivers(); - MikMod_RegisterAllLoaders(); - - md_mode = 0; - md_mode |= DMODE_16BITS; - md_mode |= DMODE_SOFT_MUSIC; - md_mode |= DMODE_SOFT_SNDFX; - - md_mixfreq = audio_get_frequency(); - - MikMod_Init(""); } if (!fast_boot) @@ -3378,6 +3338,9 @@ int main(void) playSound(1); sleep(2000); //splash screen duration + + //todo: if bgm is enabled, we should start it... + //sndPlayBGM("rom://bgm21.it"); } border_color_1 = translate_color(border_color_1_s); @@ -3434,6 +3397,8 @@ int main(void) //system main-loop with controller inputs-scan while (1) { + sndUpdate(); + if (playing == 1) playing = update_mp3(buf_ptr, buf_size); @@ -4329,8 +4294,9 @@ int main(void) drawBoxNumber(disp, 2); display_show(disp); - printText("ALT64: v0.1.8.6.1.1", 9, 8, disp); - printText(" ", 9, -1, disp); + printText("Altra64: v0.1.8.6.1.2", 9, 8, disp); + sprintf(firmware_str, "ED64 firmware: v%03x", evd_getFirmVersion()); + printText(firmware_str , 9, -1, disp); printText("by Saturnu", 9, -1, disp); printText("& JonesAlmighty", 9, -1, disp); printText(" ", 9, -1, disp); diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..7589cc8 --- /dev/null +++ b/src/sound.c @@ -0,0 +1,108 @@ +#include +#include //needed for audio_get_frequency() +#include "hashtable.h" + +MODULE *moduleBGM = NULL; + +/* sound effects */ +hashtable* samples = NULL; + +/* voices */ +SBYTE voiceSFX; + +void sndInit(void) +{ + samples = hashtable_create(); + + /* register all the drivers */ + MikMod_RegisterAllDrivers(); + MikMod_RegisterAllLoaders(); + + /* initialize the library */ + md_mode = 0; + md_mode |= DMODE_16BITS; + md_mode |= DMODE_SOFT_MUSIC; + md_mode |= DMODE_SOFT_SNDFX; + md_mode |= DMODE_INTERP; + + md_mixfreq = audio_get_frequency(); + + MikMod_Init(""); + + /* reserve 2 voices for sound effects */ + MikMod_SetNumVoices(-1, 2); + + /* get ready to play */ + MikMod_EnableOutput(); + + +} + +void sndPlayBGM(char* filename) +{ + if (Player_Active()) + { + Player_Stop(); + } + Player_Free(moduleBGM); + moduleBGM = NULL; + + moduleBGM = Player_Load(filename, 64, 0); + + if (moduleBGM) + { + Player_Start(moduleBGM); + Player_SetVolume(20); + } +} + +void sndStopAll(void) +{ + Voice_Stop(voiceSFX); + Player_Stop(); + MikMod_DisableOutput(); + + int index = 0; + while (index < samples->capacity) { + Sample_Free(samples->body[index].value); + index = index + 1; + } + + hashtable_destroy(samples); + + Player_Free(moduleBGM); + moduleBGM = NULL; + + samples = hashtable_create(); + //MikMod_Exit(); //I dont think we should ever exit as that would mean reinitialising?! +} + +void sndPlaySFX(char* filename) +{ + if (!Voice_Stopped(voiceSFX)) + { + Voice_Stop(voiceSFX); + } + + + if (hashtable_get(samples, filename) == NULL) + { + hashtable_set(samples, filename, Sample_Load(filename)); + } + + //audio_write_silence(); + Voice_SetVolume(voiceSFX, 200); + voiceSFX = Sample_Play(hashtable_get(samples, filename), 0, 0); + + MikMod_Update(); //force an update so that the voice is registered as playing! + +} + +void sndUpdate(void) +{ + if (!Voice_Stopped(voiceSFX) || Player_Active()) + { + MikMod_Update(); + + } +}