summaryrefslogtreecommitdiff
path: root/sound.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound.c')
-rw-r--r--sound.c812
1 files changed, 812 insertions, 0 deletions
diff --git a/sound.c b/sound.c
new file mode 100644
index 0000000..55985fc
--- /dev/null
+++ b/sound.c
@@ -0,0 +1,812 @@
+/* gameplaySP
+ *
+ * Copyright (C) 2006 Exophase <exophase@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include "common.h"
+#include <SDL.h>
+u32 global_enable_audio = 1;
+
+direct_sound_struct direct_sound_channel[2];
+gbc_sound_struct gbc_sound_channel[4];
+
+#if defined(GP2X_BUILD) || defined(TAVI_BUILD)
+u32 sound_frequency = 44100;
+#else
+u32 sound_frequency = 44100;
+#endif
+
+SDL_AudioSpec sound_settings;
+SDL_mutex *sound_mutex;
+SDL_cond *sound_cv;
+
+#ifndef PSP_BUILD
+u32 audio_buffer_size_number = 7;
+#else
+u32 audio_buffer_size_number = 1;
+#endif
+
+u32 audio_buffer_size;
+u32 sound_on = 0;
+s16 sound_buffer[BUFFER_SIZE];
+u32 sound_buffer_base = 0;
+
+u32 sound_last_cpu_ticks = 0;
+fixed16_16 gbc_sound_tick_step;
+
+// Queue 1, 2, or 4 samples to the top of the DS FIFO, wrap around circularly
+
+#define sound_timer_queue(size, value) \
+ *((s##size *)(ds->fifo + ds->fifo_top)) = value; \
+ ds->fifo_top = (ds->fifo_top + 1) % 32; \
+
+void sound_timer_queue8(u32 channel, u8 value)
+{
+ direct_sound_struct *ds = direct_sound_channel + channel;
+ sound_timer_queue(8, value);
+}
+
+void sound_timer_queue16(u32 channel, u16 value)
+{
+ direct_sound_struct *ds = direct_sound_channel + channel;
+ sound_timer_queue(8, value & 0xFF);
+ sound_timer_queue(8, value >> 8);
+}
+
+void sound_timer_queue32(u32 channel, u32 value)
+{
+ direct_sound_struct *ds = direct_sound_channel + channel;
+
+ sound_timer_queue(8, value & 0xFF);
+ sound_timer_queue(8, (value >> 8) & 0xFF);
+ sound_timer_queue(8, (value >> 16) & 0xFF);
+ sound_timer_queue(8, value >> 24);
+}
+
+// Unqueue 1 sample from the base of the DS FIFO and place it on the audio
+// buffer for as many samples as necessary. If the DS FIFO is 16 bytes or
+// smaller and if DMA is enabled for the sound channel initiate a DMA transfer
+// to the DS FIFO.
+
+#define render_sample_null() \
+
+#define render_sample_left() \
+ sound_buffer[buffer_index] += current_sample + \
+ fp16_16_to_u32((next_sample - current_sample) * fifo_fractional) \
+
+#define render_sample_right() \
+ sound_buffer[buffer_index + 1] += current_sample + \
+ fp16_16_to_u32((next_sample - current_sample) * fifo_fractional) \
+
+#define render_sample_both() \
+ dest_sample = current_sample + \
+ fp16_16_to_u32((next_sample - current_sample) * fifo_fractional); \
+ sound_buffer[buffer_index] += dest_sample; \
+ sound_buffer[buffer_index + 1] += dest_sample \
+
+#define render_samples(type) \
+ while(fifo_fractional <= 0xFFFF) \
+ { \
+ render_sample_##type(); \
+ fifo_fractional += frequency_step; \
+ buffer_index = (buffer_index + 2) % BUFFER_SIZE; \
+ } \
+
+void sound_timer(fixed16_16 frequency_step, u32 channel)
+{
+ direct_sound_struct *ds = direct_sound_channel + channel;
+
+ fixed16_16 fifo_fractional = ds->fifo_fractional;
+ u32 buffer_index = ds->buffer_index;
+ s16 current_sample, next_sample, dest_sample;
+
+ current_sample = ds->fifo[ds->fifo_base] << 4;
+ ds->fifo_base = (ds->fifo_base + 1) % 32;
+ next_sample = ds->fifo[ds->fifo_base] << 4;
+
+ if(sound_on == 1)
+ {
+ if(ds->volume == DIRECT_SOUND_VOLUME_50)
+ {
+ current_sample >>= 1;
+ next_sample >>= 1;
+ }
+
+ switch(ds->status)
+ {
+ case DIRECT_SOUND_INACTIVE:
+ render_samples(null);
+ break;
+
+ case DIRECT_SOUND_RIGHT:
+ render_samples(right);
+ break;
+
+ case DIRECT_SOUND_LEFT:
+ render_samples(left);
+ break;
+
+ case DIRECT_SOUND_LEFTRIGHT:
+ render_samples(both);
+ break;
+ }
+ }
+ else
+ {
+ render_samples(null);
+ }
+
+ ds->buffer_index = buffer_index;
+ ds->fifo_fractional = fp16_16_fractional_part(fifo_fractional);
+
+ if(((ds->fifo_top - ds->fifo_base) % 32) <= 16)
+ {
+ if(dma[1].direct_sound_channel == channel)
+ dma_transfer(dma + 1);
+
+ if(dma[2].direct_sound_channel == channel)
+ dma_transfer(dma + 2);
+ }
+}
+
+void sound_reset_fifo(u32 channel)
+{
+ direct_sound_struct *ds = direct_sound_channel;
+
+ memset(ds->fifo, 0, 32);
+}
+
+// Initial pattern data = 4bits (signed)
+// Channel volume = 12bits
+// Envelope volume = 14bits
+// Master volume = 2bits
+
+// Recalculate left and right volume as volume changes.
+// To calculate the current sample, use (sample * volume) >> 16
+
+// Square waves range from -8 (low) to 7 (high)
+
+s8 square_pattern_duty[4][8] =
+{
+ { 0xF8, 0xF8, 0xF8, 0xF8, 0x07, 0xF8, 0xF8, 0xF8 },
+ { 0xF8, 0xF8, 0xF8, 0xF8, 0x07, 0x07, 0xF8, 0xF8 },
+ { 0xF8, 0xF8, 0x07, 0x07, 0x07, 0x07, 0xF8, 0xF8 },
+ { 0x07, 0x07, 0x07, 0x07, 0xF8, 0xF8, 0x07, 0x07 },
+};
+
+s8 wave_samples[64];
+
+u32 noise_table15[1024];
+u32 noise_table7[4];
+
+u32 gbc_sound_master_volume_table[4] = { 1, 2, 4, 0 };
+
+u32 gbc_sound_channel_volume_table[8] =
+{
+ fixed_div(0, 7, 12),
+ fixed_div(1, 7, 12),
+ fixed_div(2, 7, 12),
+ fixed_div(3, 7, 12),
+ fixed_div(4, 7, 12),
+ fixed_div(5, 7, 12),
+ fixed_div(6, 7, 12),
+ fixed_div(7, 7, 12)
+};
+
+u32 gbc_sound_envelope_volume_table[16] =
+{
+ fixed_div(0, 15, 14),
+ fixed_div(1, 15, 14),
+ fixed_div(2, 15, 14),
+ fixed_div(3, 15, 14),
+ fixed_div(4, 15, 14),
+ fixed_div(5, 15, 14),
+ fixed_div(6, 15, 14),
+ fixed_div(7, 15, 14),
+ fixed_div(8, 15, 14),
+ fixed_div(9, 15, 14),
+ fixed_div(10, 15, 14),
+ fixed_div(11, 15, 14),
+ fixed_div(12, 15, 14),
+ fixed_div(13, 15, 14),
+ fixed_div(14, 15, 14),
+ fixed_div(15, 15, 14)
+};
+
+u32 gbc_sound_buffer_index = 0;
+u32 gbc_sound_last_cpu_ticks = 0;
+u32 gbc_sound_partial_ticks = 0;
+
+u32 gbc_sound_master_volume_left;
+u32 gbc_sound_master_volume_right;
+u32 gbc_sound_master_volume;
+
+#define update_volume_channel_envelope(channel) \
+ volume_##channel = gbc_sound_envelope_volume_table[envelope_volume] * \
+ gbc_sound_channel_volume_table[gbc_sound_master_volume_##channel] * \
+ gbc_sound_master_volume_table[gbc_sound_master_volume] \
+
+#define update_volume_channel_noenvelope(channel) \
+ volume_##channel = gs->wave_volume * \
+ gbc_sound_channel_volume_table[gbc_sound_master_volume_##channel] * \
+ gbc_sound_master_volume_table[gbc_sound_master_volume] \
+
+#define update_volume(type) \
+ update_volume_channel_##type(left); \
+ update_volume_channel_##type(right) \
+
+#define update_tone_sweep() \
+ if(gs->sweep_status) \
+ { \
+ u32 sweep_ticks = gs->sweep_ticks - 1; \
+ \
+ if(sweep_ticks == 0) \
+ { \
+ u32 rate = gs->rate; \
+ \
+ if(gs->sweep_direction) \
+ rate = rate - (rate >> gs->sweep_shift); \
+ else \
+ rate = rate + (rate >> gs->sweep_shift); \
+ \
+ if(rate > 2048) \
+ rate = 2048; \
+ \
+ frequency_step = float_to_fp16_16(((131072.0 / (2048 - rate)) * 8.0) / \
+ sound_frequency); \
+ \
+ gs->frequency_step = frequency_step; \
+ gs->rate = rate; \
+ \
+ sweep_ticks = gs->sweep_initial_ticks; \
+ } \
+ gs->sweep_ticks = sweep_ticks; \
+ } \
+
+#define update_tone_nosweep() \
+
+#define update_tone_envelope() \
+ if(gs->envelope_status) \
+ { \
+ u32 envelope_ticks = gs->envelope_ticks - 1; \
+ envelope_volume = gs->envelope_volume; \
+ \
+ if(envelope_ticks == 0) \
+ { \
+ if(gs->envelope_direction) \
+ { \
+ if(envelope_volume != 15) \
+ envelope_volume = gs->envelope_volume + 1; \
+ } \
+ else \
+ { \
+ if(envelope_volume != 0) \
+ envelope_volume = gs->envelope_volume - 1; \
+ } \
+ \
+ update_volume(envelope); \
+ \
+ gs->envelope_volume = envelope_volume; \
+ gs->envelope_ticks = gs->envelope_initial_ticks; \
+ } \
+ else \
+ { \
+ gs->envelope_ticks = envelope_ticks; \
+ } \
+ } \
+
+#define update_tone_noenvelope() \
+
+#define gbc_sound_synchronize() \
+ while(((gbc_sound_buffer_index - sound_buffer_base) % BUFFER_SIZE) > \
+ (audio_buffer_size * 2)) \
+ { \
+ SDL_CondWait(sound_cv, sound_mutex); \
+ } \
+
+#define update_tone_counters(envelope_op, sweep_op) \
+ tick_counter += gbc_sound_tick_step; \
+ if(tick_counter > 0xFFFF) \
+ { \
+ if(gs->length_status) \
+ { \
+ u32 length_ticks = gs->length_ticks - 1; \
+ gs->length_ticks = length_ticks; \
+ \
+ if(length_ticks == 0) \
+ { \
+ gs->active_flag = 0; \
+ break; \
+ } \
+ } \
+ \
+ update_tone_##envelope_op(); \
+ update_tone_##sweep_op(); \
+ \
+ tick_counter &= 0xFFFF; \
+ } \
+
+#define gbc_sound_render_sample_right() \
+ sound_buffer[buffer_index + 1] += (current_sample * volume_right) >> 22 \
+
+#define gbc_sound_render_sample_left() \
+ sound_buffer[buffer_index] += (current_sample * volume_left) >> 22 \
+
+#define gbc_sound_render_sample_both() \
+ gbc_sound_render_sample_right(); \
+ gbc_sound_render_sample_left() \
+
+#define gbc_sound_render_samples(type, sample_length, envelope_op, sweep_op) \
+ for(i = 0; i < buffer_ticks; i++) \
+ { \
+ current_sample = \
+ sample_data[fp16_16_to_u32(sample_index) % sample_length]; \
+ gbc_sound_render_sample_##type(); \
+ \
+ sample_index += frequency_step; \
+ buffer_index = (buffer_index + 2) % BUFFER_SIZE; \
+ \
+ update_tone_counters(envelope_op, sweep_op); \
+ } \
+
+#define gbc_noise_wrap_full 32767
+
+#define gbc_noise_wrap_half 126
+
+#define get_noise_sample_full() \
+ current_sample = \
+ ((s32)(noise_table15[fp16_16_to_u32(sample_index) >> 5] << \
+ (fp16_16_to_u32(sample_index) & 0x1F)) >> 31) & 0x0F \
+
+#define get_noise_sample_half() \
+ current_sample = \
+ ((s32)(noise_table7[fp16_16_to_u32(sample_index) >> 5] << \
+ (fp16_16_to_u32(sample_index) & 0x1F)) >> 31) & 0x0F \
+
+#define gbc_sound_render_noise(type, noise_type, envelope_op, sweep_op) \
+ for(i = 0; i < buffer_ticks; i++) \
+ { \
+ get_noise_sample_##noise_type(); \
+ gbc_sound_render_sample_##type(); \
+ \
+ sample_index += frequency_step; \
+ \
+ if(sample_index >= u32_to_fp16_16(gbc_noise_wrap_##noise_type)) \
+ sample_index -= u32_to_fp16_16(gbc_noise_wrap_##noise_type); \
+ \
+ buffer_index = (buffer_index + 2) % BUFFER_SIZE; \
+ update_tone_counters(envelope_op, sweep_op); \
+ } \
+
+#define gbc_sound_render_channel(type, sample_length, envelope_op, sweep_op) \
+ buffer_index = gbc_sound_buffer_index; \
+ sample_index = gs->sample_index; \
+ frequency_step = gs->frequency_step; \
+ tick_counter = gs->tick_counter; \
+ \
+ update_volume(envelope_op); \
+ \
+ switch(gs->status) \
+ { \
+ case GBC_SOUND_INACTIVE: \
+ break; \
+ \
+ case GBC_SOUND_LEFT: \
+ gbc_sound_render_##type(left, sample_length, envelope_op, sweep_op); \
+ break; \
+ \
+ case GBC_SOUND_RIGHT: \
+ gbc_sound_render_##type(right, sample_length, envelope_op, sweep_op); \
+ break; \
+ \
+ case GBC_SOUND_LEFTRIGHT: \
+ gbc_sound_render_##type(both, sample_length, envelope_op, sweep_op); \
+ break; \
+ } \
+ \
+ gs->sample_index = sample_index; \
+ gs->tick_counter = tick_counter; \
+
+#define gbc_sound_load_wave_ram(bank) \
+ wave_bank = wave_samples + (bank * 32); \
+ for(i = 0, i2 = 0; i < 16; i++, i2 += 2) \
+ { \
+ current_sample = wave_ram[i]; \
+ wave_bank[i2] = (((current_sample >> 4) & 0x0F) - 8); \
+ wave_bank[i2 + 1] = ((current_sample & 0x0F) - 8); \
+ } \
+
+void synchronize_sound()
+{
+ SDL_LockMutex(sound_mutex);
+
+ gbc_sound_synchronize();
+
+ SDL_UnlockMutex(sound_mutex);
+}
+
+void update_gbc_sound(u32 cpu_ticks)
+{
+ fixed16_16 buffer_ticks = float_to_fp16_16(((float)(cpu_ticks -
+ gbc_sound_last_cpu_ticks) * sound_frequency) / 16777216.0);
+ u32 i, i2;
+ gbc_sound_struct *gs = gbc_sound_channel;
+ fixed16_16 sample_index, frequency_step;
+ fixed16_16 tick_counter;
+ u32 buffer_index;
+ s32 volume_left, volume_right;
+ u32 envelope_volume;
+ s32 current_sample;
+ u32 sound_status = address16(io_registers, 0x84) & 0xFFF0;
+ s8 *sample_data;
+ s8 *wave_bank;
+ u8 *wave_ram = ((u8 *)io_registers) + 0x90;
+
+ gbc_sound_partial_ticks += fp16_16_fractional_part(buffer_ticks);
+ buffer_ticks = fp16_16_to_u32(buffer_ticks);
+
+ if(gbc_sound_partial_ticks > 0xFFFF)
+ {
+ buffer_ticks += 1;
+ gbc_sound_partial_ticks &= 0xFFFF;
+ }
+
+ SDL_LockMutex(sound_mutex);
+ if(synchronize_flag)
+ {
+ if(((gbc_sound_buffer_index - sound_buffer_base) % BUFFER_SIZE) >
+ (audio_buffer_size * 3 / 2))
+ {
+ while(((gbc_sound_buffer_index - sound_buffer_base) % BUFFER_SIZE) >
+ (audio_buffer_size * 3 / 2))
+ {
+ SDL_CondWait(sound_cv, sound_mutex);
+ }
+
+#ifdef PSP_BUILD
+ if(current_frameskip_type == auto_frameskip)
+ {
+ sceDisplayWaitVblankStart();
+ real_frame_count = 0;
+ virtual_frame_count = 0;
+ }
+#endif
+
+/*
+
+#ifdef GP2X_BUILD
+ if(current_frameskip_type == auto_frameskip)
+ {
+ u64 current_ticks;
+ u64 next_ticks;
+ get_ticks_us(&current_ticks);
+
+ next_ticks = ((current_ticks + 16666) / 16667) * 16667;
+ delay_us(next_ticks - current_ticks);
+
+ get_ticks_us(&frame_count_initial_timestamp);
+ real_frame_count = 0;
+ virtual_frame_count = 0;
+ }
+#endif
+
+*/
+ }
+ }
+ if(sound_on == 1)
+ {
+ gs = gbc_sound_channel + 0;
+ if(gs->active_flag)
+ {
+ sound_status |= 0x01;
+ sample_data = gs->sample_data;
+ envelope_volume = gs->envelope_volume;
+ gbc_sound_render_channel(samples, 8, envelope, sweep);
+ }
+
+ gs = gbc_sound_channel + 1;
+ if(gs->active_flag)
+ {
+ sound_status |= 0x02;
+ sample_data = gs->sample_data;
+ envelope_volume = gs->envelope_volume;
+ gbc_sound_render_channel(samples, 8, envelope, nosweep);
+ }
+
+ gs = gbc_sound_channel + 2;
+ if(gbc_sound_wave_update)
+ {
+ if(gs->wave_bank == 1)
+ {
+ gbc_sound_load_wave_ram(1);
+ }
+ else
+ {
+ gbc_sound_load_wave_ram(0);
+ }
+
+ gbc_sound_wave_update = 0;
+ }
+
+ if((gs->active_flag) && (gs->master_enable))
+ {
+ sound_status |= 0x04;
+ sample_data = wave_samples;
+ if(gs->wave_type == 0)
+ {
+ if(gs->wave_bank == 1)
+ sample_data += 32;
+
+ gbc_sound_render_channel(samples, 32, noenvelope, nosweep);
+ }
+ else
+ {
+ gbc_sound_render_channel(samples, 64, noenvelope, nosweep);
+ }
+ }
+
+ gs = gbc_sound_channel + 3;
+ if(gs->active_flag)
+ {
+ sound_status |= 0x08;
+ envelope_volume = gs->envelope_volume;
+
+ if(gs->noise_type == 1)
+ {
+ gbc_sound_render_channel(noise, half, envelope, nosweep);
+ }
+ else
+ {
+ gbc_sound_render_channel(noise, full, envelope, nosweep);
+ }
+ }
+ }
+
+ address16(io_registers, 0x84) = sound_status;
+
+ SDL_CondSignal(sound_cv);
+
+ SDL_UnlockMutex(sound_mutex);
+
+ gbc_sound_last_cpu_ticks = cpu_ticks;
+ gbc_sound_buffer_index =
+ (gbc_sound_buffer_index + (buffer_ticks * 2)) % BUFFER_SIZE;
+}
+
+#define sound_copy_normal() \
+ current_sample = source[i] \
+
+#define sound_copy(source_offset, length, render_type) \
+ _length = (length) / 2; \
+ source = (s16 *)(sound_buffer + source_offset); \
+ for(i = 0; i < _length; i++) \
+ { \
+ sound_copy_##render_type(); \
+ if(current_sample > 2047) \
+ current_sample = 2047; \
+ if(current_sample < -2048) \
+ current_sample = -2048; \
+ \
+ stream_base[i] = current_sample << 4; \
+ source[i] = 0; \
+ } \
+
+#define sound_copy_null(source_offset, length) \
+ _length = (length) / 2; \
+ source = (s16 *)(sound_buffer + source_offset); \
+ for(i = 0; i < _length; i++) \
+ { \
+ stream_base[i] = 0; \
+ source[i] = 0; \
+ } \
+
+
+void sound_callback(void *userdata, Uint8 *stream, int length)
+{
+ u32 sample_length = length / 2;
+ u32 _length;
+ u32 i;
+ s16 *stream_base = (s16 *)stream;
+ s16 *source;
+ s32 current_sample;
+
+ SDL_LockMutex(sound_mutex);
+
+ while(((gbc_sound_buffer_index - sound_buffer_base) % BUFFER_SIZE) <
+ length)
+ {
+ SDL_CondWait(sound_cv, sound_mutex);
+ }
+
+ if(global_enable_audio)
+ {
+ if((sound_buffer_base + sample_length) >= BUFFER_SIZE)
+ {
+ u32 partial_length = (BUFFER_SIZE - sound_buffer_base) * 2;
+ sound_copy(sound_buffer_base, partial_length, normal);
+ source = (s16 *)sound_buffer;
+ sound_copy(0, length - partial_length, normal);
+ sound_buffer_base = (length - partial_length) / 2;
+ }
+ else
+ {
+ sound_copy(sound_buffer_base, length, normal);
+ sound_buffer_base += sample_length;
+ }
+ }
+ else
+ {
+ if((sound_buffer_base + sample_length) >= BUFFER_SIZE)
+ {
+ u32 partial_length = (BUFFER_SIZE - sound_buffer_base) * 2;
+ sound_copy_null(sound_buffer_base, partial_length);
+ source = (s16 *)sound_buffer;
+ sound_copy(0, length - partial_length, normal);
+ sound_buffer_base = (length - partial_length) / 2;
+ }
+ else
+ {
+ sound_copy_null(sound_buffer_base, length);
+ sound_buffer_base += sample_length;
+ }
+ }
+
+ SDL_CondSignal(sound_cv);
+
+ SDL_UnlockMutex(sound_mutex);
+}
+
+// Special thanks to blarrg for the LSFR frequency used in Meridian, as posted
+// on the forum at http://meridian.overclocked.org:
+// http://meridian.overclocked.org/cgi-bin/wwwthreads/showpost.pl?Board=merid
+// angeneraldiscussion&Number=2069&page=0&view=expanded&mode=threaded&sb=4
+// Hope you don't mind me borrowing it ^_-
+
+void init_noise_table(u32 *table, u32 period, u32 bit_length)
+{
+ u32 shift_register = 0xFF;
+ u32 mask = ~(1 << bit_length);
+ s32 table_pos, bit_pos;
+ u32 current_entry;
+ u32 table_period = (period + 31) / 32;
+
+ // Bits are stored in reverse order so they can be more easily moved to
+ // bit 31, for sign extended shift down.
+
+ for(table_pos = 0; table_pos < table_period; table_pos++)
+ {
+ current_entry = 0;
+ for(bit_pos = 31; bit_pos >= 0; bit_pos--)
+ {
+ current_entry |= (shift_register & 0x01) << bit_pos;
+
+ shift_register =
+ ((1 & (shift_register ^ (shift_register >> 1))) << bit_length) |
+ ((shift_register >> 1) & mask);
+ }
+
+ table[table_pos] = current_entry;
+ }
+}
+
+void reset_sound()
+{
+ direct_sound_struct *ds = direct_sound_channel;
+ gbc_sound_struct *gs = gbc_sound_channel;
+ u32 i;
+
+ sound_on = 0;
+ sound_buffer_base = 0;
+ sound_last_cpu_ticks = 0;
+ memset(sound_buffer, 0, audio_buffer_size);
+
+ for(i = 0; i < 2; i++, ds++)
+ {
+ ds->buffer_index = 0;
+ ds->status = DIRECT_SOUND_INACTIVE;
+ ds->fifo_top = 0;
+ ds->fifo_base = 0;
+ ds->fifo_fractional = 0;
+ ds->last_cpu_ticks = 0;
+ memset(ds->fifo, 0, 32);
+ }
+
+ gbc_sound_buffer_index = 0;
+ gbc_sound_last_cpu_ticks = 0;
+ gbc_sound_partial_ticks = 0;
+
+ gbc_sound_master_volume_left = 0;
+ gbc_sound_master_volume_right = 0;
+ gbc_sound_master_volume = 0;
+ memset(wave_samples, 0, 64);
+
+ for(i = 0; i < 4; i++, gs++)
+ {
+ gs->status = GBC_SOUND_INACTIVE;
+ gs->sample_data = square_pattern_duty[2];
+ gs->active_flag = 0;
+ }
+}
+
+void sound_exit()
+{
+ gbc_sound_buffer_index =
+ (sound_buffer_base + audio_buffer_size) % BUFFER_SIZE;
+ SDL_PauseAudio(1);
+ SDL_CondSignal(sound_cv);
+}
+
+void init_sound()
+{
+#ifdef PSP_BUILD
+ audio_buffer_size = (audio_buffer_size_number * 1024) + 3072;
+#elif defined(TAVI_BUILD) || defined(ARM_ARCH)
+ audio_buffer_size = 16 << audio_buffer_size_number;
+// audio_buffer_size = 16384;
+#else
+ audio_buffer_size = 16384;
+#endif
+
+ SDL_AudioSpec desired_spec =
+ {
+ sound_frequency,
+ AUDIO_S16,
+ 2,
+ 0,
+ audio_buffer_size / 4,
+ 0,
+ 0,
+ sound_callback,
+ NULL
+ };
+
+ gbc_sound_tick_step =
+ float_to_fp16_16(256.0 / sound_frequency);
+
+ init_noise_table(noise_table15, 32767, 14);
+ init_noise_table(noise_table7, 127, 6);
+
+ reset_sound();
+
+ SDL_OpenAudio(&desired_spec, &sound_settings);
+ sound_frequency = sound_settings.freq;
+ sound_mutex = SDL_CreateMutex();
+ sound_cv = SDL_CreateCond();
+ SDL_PauseAudio(0);
+}
+
+#define sound_savestate_builder(type) \
+void sound_##type##_savestate(file_tag_type savestate_file) \
+{ \
+ file_##type##_variable(savestate_file, sound_on); \
+ file_##type##_variable(savestate_file, sound_buffer_base); \
+ file_##type##_variable(savestate_file, sound_last_cpu_ticks); \
+ file_##type##_variable(savestate_file, gbc_sound_buffer_index); \
+ file_##type##_variable(savestate_file, gbc_sound_last_cpu_ticks); \
+ file_##type##_variable(savestate_file, gbc_sound_partial_ticks); \
+ file_##type##_variable(savestate_file, gbc_sound_master_volume_left); \
+ file_##type##_variable(savestate_file, gbc_sound_master_volume_right); \
+ file_##type##_variable(savestate_file, gbc_sound_master_volume); \
+ file_##type##_array(savestate_file, wave_samples); \
+ file_##type##_array(savestate_file, direct_sound_channel); \
+ file_##type##_array(savestate_file, gbc_sound_channel); \
+} \
+
+sound_savestate_builder(read);
+sound_savestate_builder(write_mem);
+