improved the sound implementation

(still has too much latency, although mainly due to other parts of the menu)
This commit is contained in:
Robin Jones 2016-12-29 17:05:09 +00:00
parent 5e5da4d1a8
commit b719c4389f
6 changed files with 308 additions and 50 deletions

20
inc/hashtable.h Normal file
View File

@ -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;
};

5
inc/sound.h Normal file
View File

@ -0,0 +1,5 @@
void sndInit(void);
void sndPlayBGM(char* filename);
void sndStopAll(void);
void sndPlaySFX(char* filename);
void sndUpdate(void);

BIN
res/filesystem/bgm21.it Normal file

Binary file not shown.

159
src/hashtable.c Normal file
View File

@ -0,0 +1,159 @@
/**
* Hashtable implementation
* (c) 2011 @marekweb
*
* Uses dynamic addressing with linear probing.
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#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);
}

View File

@ -36,7 +36,7 @@
#include "menu.h"
//sound
#include <mikmod.h>
#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);

108
src/sound.c Normal file
View File

@ -0,0 +1,108 @@
#include <mikmod.h>
#include <libdragon.h> //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();
}
}