2009-05-21 17:48:31 +02:00
|
|
|
/* 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"
|
|
|
|
|
|
|
|
direct_sound_struct direct_sound_channel[2];
|
|
|
|
gbc_sound_struct gbc_sound_channel[4];
|
|
|
|
|
2021-08-23 20:37:39 +02:00
|
|
|
const u32 sound_frequency = GBA_SOUND_FREQUENCY;
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2011-09-05 18:31:58 +02:00
|
|
|
u32 sound_on;
|
|
|
|
static s16 sound_buffer[BUFFER_SIZE];
|
|
|
|
static u32 sound_buffer_base;
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2011-09-05 18:31:58 +02:00
|
|
|
static fixed16_16 gbc_sound_tick_step;
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2014-12-12 17:33:29 +01:00
|
|
|
/* Queue 1 sample to the top of the DS FIFO, wrap around circularly */
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
void sound_timer_queue8(u32 channel, u8 value)
|
|
|
|
{
|
|
|
|
direct_sound_struct *ds = direct_sound_channel + channel;
|
2014-12-12 17:33:29 +01:00
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = value;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-12-12 17:33:29 +01:00
|
|
|
/* Queue 2 samples to the top of the DS FIFO, wrap around circularly */
|
|
|
|
|
2009-05-21 17:48:31 +02:00
|
|
|
void sound_timer_queue16(u32 channel, u16 value)
|
|
|
|
{
|
|
|
|
direct_sound_struct *ds = direct_sound_channel + channel;
|
2014-12-12 17:33:29 +01:00
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = value & 0xFF;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = value >> 8;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-12-12 17:33:29 +01:00
|
|
|
/* Queue 4 samples to the top of the DS FIFO, wrap around circularly */
|
|
|
|
|
2009-05-21 17:48:31 +02:00
|
|
|
void sound_timer_queue32(u32 channel, u32 value)
|
|
|
|
{
|
|
|
|
direct_sound_struct *ds = direct_sound_channel + channel;
|
|
|
|
|
2014-12-12 17:33:29 +01:00
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = value & 0xFF;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = (value >> 8) & 0xFF;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = (value >> 16) & 0xFF;
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
|
|
|
|
|
|
|
*((s8 *)(ds->fifo + ds->fifo_top)) = (value >> 24);
|
|
|
|
ds->fifo_top = (ds->fifo_top + 1) % 32;
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-06 19:18:34 +02:00
|
|
|
void sound_timer(fixed8_24 frequency_step, u32 channel)
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
2021-07-11 13:35:21 +02:00
|
|
|
direct_sound_status_type sample_status = DIRECT_SOUND_INACTIVE;
|
2021-08-24 19:55:37 +02:00
|
|
|
direct_sound_struct *ds = &direct_sound_channel[channel];
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2011-09-06 19:18:34 +02:00
|
|
|
fixed8_24 fifo_fractional = ds->fifo_fractional;
|
2009-05-21 17:48:31 +02:00
|
|
|
u32 buffer_index = ds->buffer_index;
|
2014-12-09 21:21:09 +01:00
|
|
|
s16 current_sample, next_sample;
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2021-06-30 00:29:21 +02:00
|
|
|
current_sample = ds->fifo[ds->fifo_base] * 16;
|
2009-05-21 17:48:31 +02:00
|
|
|
ds->fifo_base = (ds->fifo_base + 1) % 32;
|
2021-06-30 00:29:21 +02:00
|
|
|
next_sample = ds->fifo[ds->fifo_base] * 16;
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
if(sound_on == 1)
|
|
|
|
{
|
|
|
|
if(ds->volume == DIRECT_SOUND_VOLUME_50)
|
|
|
|
{
|
|
|
|
current_sample >>= 1;
|
|
|
|
next_sample >>= 1;
|
|
|
|
}
|
|
|
|
|
2014-12-09 19:02:07 +01:00
|
|
|
sample_status = ds->status;
|
|
|
|
}
|
|
|
|
|
2014-12-09 21:21:09 +01:00
|
|
|
// 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.
|
|
|
|
|
2014-12-09 19:02:07 +01:00
|
|
|
switch(sample_status)
|
|
|
|
{
|
|
|
|
case DIRECT_SOUND_INACTIVE:
|
2014-12-09 21:21:09 +01:00
|
|
|
/* render samples NULL */
|
|
|
|
while(fifo_fractional <= 0xFFFFFF)
|
|
|
|
{
|
|
|
|
fifo_fractional += frequency_step;
|
|
|
|
buffer_index = (buffer_index + 2) % BUFFER_SIZE;
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
break;
|
|
|
|
|
2014-12-09 19:02:07 +01:00
|
|
|
case DIRECT_SOUND_RIGHT:
|
2014-12-09 21:21:09 +01:00
|
|
|
/* render samples RIGHT */
|
|
|
|
while(fifo_fractional <= 0xFFFFFF)
|
|
|
|
{
|
|
|
|
s16 dest_sample = current_sample +
|
|
|
|
fp16_16_to_u32((next_sample - current_sample) * (fifo_fractional >> 8));
|
|
|
|
|
|
|
|
sound_buffer[buffer_index + 1] += dest_sample;
|
|
|
|
|
|
|
|
fifo_fractional += frequency_step;
|
|
|
|
buffer_index = (buffer_index + 2) % BUFFER_SIZE;
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
break;
|
|
|
|
|
2014-12-09 19:02:07 +01:00
|
|
|
case DIRECT_SOUND_LEFT:
|
2014-12-09 21:21:09 +01:00
|
|
|
/* render samples LEFT */
|
|
|
|
while(fifo_fractional <= 0xFFFFFF)
|
|
|
|
{
|
|
|
|
s16 dest_sample = current_sample +
|
|
|
|
fp16_16_to_u32((next_sample - current_sample) * (fifo_fractional >> 8));
|
|
|
|
|
|
|
|
sound_buffer[buffer_index] += dest_sample;
|
|
|
|
|
|
|
|
fifo_fractional += frequency_step;
|
|
|
|
buffer_index = (buffer_index + 2) % BUFFER_SIZE;
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
break;
|
|
|
|
|
2014-12-09 19:02:07 +01:00
|
|
|
case DIRECT_SOUND_LEFTRIGHT:
|
2014-12-09 21:21:09 +01:00
|
|
|
/* render samples LEFT and RIGHT. */
|
|
|
|
while(fifo_fractional <= 0xFFFFFF)
|
|
|
|
{
|
|
|
|
s16 dest_sample = current_sample +
|
|
|
|
fp16_16_to_u32((next_sample - current_sample) * (fifo_fractional >> 8));
|
|
|
|
|
|
|
|
sound_buffer[buffer_index] += dest_sample;
|
|
|
|
sound_buffer[buffer_index + 1] += dest_sample;
|
|
|
|
fifo_fractional += frequency_step;
|
|
|
|
buffer_index = (buffer_index + 2) % BUFFER_SIZE;
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ds->buffer_index = buffer_index;
|
2011-09-06 19:18:34 +02:00
|
|
|
ds->fifo_fractional = fp8_24_fractional_part(fifo_fractional);
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
if(((ds->fifo_top - ds->fifo_base) % 32) <= 16)
|
|
|
|
{
|
|
|
|
if(dma[1].direct_sound_channel == channel)
|
2021-08-24 17:15:27 +02:00
|
|
|
dma_transfer(1);
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
if(dma[2].direct_sound_channel == channel)
|
2021-08-24 17:15:27 +02:00
|
|
|
dma_transfer(2);
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sound_reset_fifo(u32 channel)
|
|
|
|
{
|
2021-08-24 19:55:37 +02:00
|
|
|
memset(direct_sound_channel[channel].fifo, 0, 32);
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
2021-07-11 13:35:21 +02:00
|
|
|
const s8 square_pattern_duty[4][8] =
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
2021-07-11 13:35:21 +02:00
|
|
|
{ -8, -8, -8, -8, 7, -8, -8, -8 },
|
|
|
|
{ -8, -8, -8, -8, 7, 7, -8, -8 },
|
|
|
|
{ -8, -8, 7, 7, 7, 7, -8, -8 },
|
|
|
|
{ 7, 7, 7, 7, -8, -8, 7, 7 },
|
2009-05-21 17:48:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
s8 wave_samples[64];
|
|
|
|
|
|
|
|
u32 noise_table15[1024];
|
|
|
|
u32 noise_table7[4];
|
|
|
|
|
2021-08-23 20:37:39 +02:00
|
|
|
const u32 gbc_sound_master_volume_table[4] = { 1, 2, 4, 0 };
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2021-08-23 20:37:39 +02:00
|
|
|
const u32 gbc_sound_channel_volume_table[8] =
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
2021-08-23 20:37:39 +02:00
|
|
|
const u32 gbc_sound_envelope_volume_table[16] =
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
|
|
|
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); \
|
|
|
|
\
|
2021-02-23 20:27:59 +01:00
|
|
|
if(rate > 2047) { \
|
|
|
|
rate = 2047; \
|
|
|
|
gs->active_flag = 0; \
|
|
|
|
break; \
|
|
|
|
} \
|
2009-05-21 17:48:31 +02:00
|
|
|
\
|
2011-09-06 19:18:34 +02:00
|
|
|
frequency_step = float_to_fp16_16(((131072.0f / (2048 - rate)) * 8.0f) \
|
|
|
|
/ sound_frequency); \
|
2009-05-21 17:48:31 +02:00
|
|
|
\
|
|
|
|
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 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; \
|
|
|
|
|
|
|
|
void update_gbc_sound(u32 cpu_ticks)
|
|
|
|
{
|
2011-09-06 19:18:34 +02:00
|
|
|
fixed16_16 buffer_ticks = float_to_fp16_16((float)(cpu_ticks -
|
|
|
|
gbc_sound_last_cpu_ticks) * sound_frequency / GBC_BASE_RATE);
|
2009-05-21 17:48:31 +02:00
|
|
|
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;
|
2021-06-27 01:16:28 +02:00
|
|
|
u16 sound_status = read_ioreg(REG_SOUNDCNT_X) & 0xFFF0;
|
2021-07-11 13:35:21 +02:00
|
|
|
const s8 *sample_data;
|
2009-05-21 17:48:31 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sound_on == 1)
|
|
|
|
{
|
|
|
|
gs = gbc_sound_channel + 0;
|
|
|
|
if(gs->active_flag)
|
|
|
|
{
|
|
|
|
sound_status |= 0x01;
|
2021-07-27 21:45:15 +02:00
|
|
|
sample_data = &square_pattern_duty[gs->sample_table_idx][0];
|
2009-05-21 17:48:31 +02:00
|
|
|
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;
|
2021-07-27 21:45:15 +02:00
|
|
|
sample_data = &square_pattern_duty[gs->sample_table_idx][0];
|
2009-05-21 17:48:31 +02:00
|
|
|
envelope_volume = gs->envelope_volume;
|
|
|
|
gbc_sound_render_channel(samples, 8, envelope, nosweep);
|
|
|
|
}
|
|
|
|
|
|
|
|
gs = gbc_sound_channel + 2;
|
|
|
|
if(gbc_sound_wave_update)
|
|
|
|
{
|
2014-12-09 19:13:35 +01:00
|
|
|
unsigned bank = (gs->wave_bank == 1) ? 1 : 0;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2014-12-09 19:13:35 +01:00
|
|
|
gbc_sound_wave_update = 0;
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-27 01:16:28 +02:00
|
|
|
write_ioreg(REG_SOUNDCNT_X, sound_status);
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
gbc_sound_last_cpu_ticks = cpu_ticks;
|
|
|
|
gbc_sound_buffer_index =
|
|
|
|
(gbc_sound_buffer_index + (buffer_ticks * 2)) % BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 ^_-
|
|
|
|
|
2014-12-13 05:01:02 +01:00
|
|
|
static void init_noise_table(u32 *table, u32 period, u32 bit_length)
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-10 15:47:19 +01:00
|
|
|
void reset_sound(void)
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
|
|
|
direct_sound_struct *ds = direct_sound_channel;
|
|
|
|
gbc_sound_struct *gs = gbc_sound_channel;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
sound_on = 0;
|
|
|
|
sound_buffer_base = 0;
|
2011-09-06 19:18:34 +02:00
|
|
|
memset(sound_buffer, 0, sizeof(sound_buffer));
|
2009-05-21 17:48:31 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
2021-07-27 21:45:15 +02:00
|
|
|
gs->sample_table_idx = 2;
|
2009-05-21 17:48:31 +02:00
|
|
|
gs->active_flag = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-05 01:00:26 +02:00
|
|
|
void init_sound(int need_reset)
|
2009-05-21 17:48:31 +02:00
|
|
|
{
|
2011-09-06 19:18:34 +02:00
|
|
|
gbc_sound_tick_step =
|
|
|
|
float_to_fp16_16(256.0f / sound_frequency);
|
|
|
|
|
|
|
|
init_noise_table(noise_table15, 32767, 14);
|
|
|
|
init_noise_table(noise_table7, 127, 6);
|
|
|
|
|
2012-10-05 01:00:26 +02:00
|
|
|
if (need_reset)
|
|
|
|
reset_sound();
|
2009-05-21 17:48:31 +02:00
|
|
|
}
|
|
|
|
|
2021-08-24 17:15:27 +02:00
|
|
|
bool sound_read_savestate(const u8 *src)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const u8 *snddoc = bson_find_key(src, "sound");
|
|
|
|
|
|
|
|
if (!(
|
|
|
|
bson_read_int32(snddoc, "on", &sound_on) &&
|
|
|
|
bson_read_int32(snddoc, "buf-base", &sound_buffer_base) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-buf-idx", &gbc_sound_buffer_index) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-last-cpu-ticks", &gbc_sound_last_cpu_ticks) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-partial-ticks", &gbc_sound_partial_ticks) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-ms-vol-left", &gbc_sound_master_volume_left) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-ms-vol-right", &gbc_sound_master_volume_right) &&
|
|
|
|
bson_read_int32(snddoc, "gbc-ms-vol", &gbc_sound_master_volume) &&
|
|
|
|
bson_read_bytes(snddoc, "wav-samples", wave_samples, sizeof(wave_samples))))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
direct_sound_struct *ds = &direct_sound_channel[i];
|
|
|
|
char tn[4] = {'d', 's', '0' + i, 0};
|
|
|
|
const u8 *sndchan = bson_find_key(snddoc, tn);
|
|
|
|
if (!(
|
|
|
|
bson_read_int32(sndchan, "status", &ds->status) &&
|
|
|
|
bson_read_int32(sndchan, "volume", &ds->volume) &&
|
|
|
|
bson_read_int32(sndchan, "fifo-base", &ds->fifo_base) &&
|
|
|
|
bson_read_int32(sndchan, "fifo-top", &ds->fifo_top) &&
|
|
|
|
bson_read_int32(sndchan, "fifo-frac", &ds->fifo_fractional) &&
|
|
|
|
bson_read_bytes(sndchan, "fifo-bytes", ds->fifo, sizeof(ds->fifo)) &&
|
|
|
|
bson_read_int32(sndchan, "buf-idx", &ds->buffer_index)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
gbc_sound_struct *gs = &gbc_sound_channel[i];
|
|
|
|
char tn[4] = {'g', 's', '0' + i, 0};
|
|
|
|
const u8 *sndchan = bson_find_key(snddoc, tn);
|
|
|
|
if (!(
|
|
|
|
bson_read_int32(sndchan, "status", &gs->status) &&
|
|
|
|
bson_read_int32(sndchan, "rate", &gs->rate) &&
|
|
|
|
bson_read_int32(sndchan, "freq-step", &gs->frequency_step) &&
|
|
|
|
bson_read_int32(sndchan, "sample-idx", &gs->sample_index) &&
|
|
|
|
bson_read_int32(sndchan, "tick-cnt", &gs->tick_counter) &&
|
|
|
|
bson_read_int32(sndchan, "volume", &gs->total_volume) &&
|
|
|
|
bson_read_int32(sndchan, "active", &gs->active_flag) &&
|
|
|
|
bson_read_int32(sndchan, "enable", &gs->master_enable) &&
|
|
|
|
|
|
|
|
bson_read_int32(sndchan, "env-vol0", &gs->envelope_initial_volume) &&
|
|
|
|
bson_read_int32(sndchan, "env-vol", &gs->envelope_volume) &&
|
|
|
|
bson_read_int32(sndchan, "env-dir", &gs->envelope_direction) &&
|
|
|
|
bson_read_int32(sndchan, "env-status", &gs->envelope_status) &&
|
|
|
|
bson_read_int32(sndchan, "env-step", &gs->envelope_step) &&
|
|
|
|
bson_read_int32(sndchan, "env-ticks0", &gs->envelope_initial_ticks) &&
|
|
|
|
bson_read_int32(sndchan, "env-ticks", &gs->envelope_ticks) &&
|
|
|
|
bson_read_int32(sndchan, "sweep-status", &gs->sweep_status) &&
|
|
|
|
bson_read_int32(sndchan, "sweep-dir", &gs->sweep_direction) &&
|
|
|
|
bson_read_int32(sndchan, "sweep-ticks0", &gs->sweep_initial_ticks) &&
|
|
|
|
bson_read_int32(sndchan, "sweep-ticks", &gs->sweep_ticks) &&
|
|
|
|
bson_read_int32(sndchan, "sweep-shift", &gs->sweep_shift) &&
|
|
|
|
bson_read_int32(sndchan, "wav-type", &gs->wave_type) &&
|
|
|
|
bson_read_int32(sndchan, "wav-bank", &gs->wave_bank) &&
|
|
|
|
bson_read_int32(sndchan, "wav-vol", &gs->wave_volume) &&
|
|
|
|
|
|
|
|
bson_read_int32(sndchan, "len-status", &gs->length_status) &&
|
|
|
|
bson_read_int32(sndchan, "len-ticks", &gs->length_ticks) &&
|
|
|
|
bson_read_int32(sndchan, "noise-type", &gs->noise_type) &&
|
|
|
|
bson_read_int32(sndchan, "sample-tbl", &gs->sample_table_idx)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2014-12-10 11:06:17 +01:00
|
|
|
}
|
|
|
|
|
2021-08-24 17:15:27 +02:00
|
|
|
unsigned sound_write_savestate(u8 *dst)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u8 *wbptr, *startp = dst;
|
|
|
|
bson_start_document(dst, "sound", wbptr);
|
|
|
|
bson_write_int32(dst, "on", sound_on);
|
|
|
|
bson_write_int32(dst, "buf-base", sound_buffer_base);
|
|
|
|
bson_write_int32(dst, "gbc-buf-idx", gbc_sound_buffer_index);
|
|
|
|
bson_write_int32(dst, "gbc-last-cpu-ticks", gbc_sound_last_cpu_ticks);
|
|
|
|
bson_write_int32(dst, "gbc-partial-ticks", gbc_sound_partial_ticks);
|
|
|
|
bson_write_int32(dst, "gbc-ms-vol-left", gbc_sound_master_volume_left);
|
|
|
|
bson_write_int32(dst, "gbc-ms-vol-right", gbc_sound_master_volume_right);
|
|
|
|
bson_write_int32(dst, "gbc-ms-vol", gbc_sound_master_volume);
|
|
|
|
bson_write_bytes(dst, "wav-samples", wave_samples, sizeof(wave_samples));
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
u8 *wbptr2;
|
|
|
|
char tn[4] = {'d', 's', '0' + i, 0};
|
|
|
|
bson_start_document(dst, tn, wbptr2);
|
|
|
|
bson_write_int32(dst, "status", direct_sound_channel[i].status);
|
|
|
|
bson_write_int32(dst, "volume", direct_sound_channel[i].volume);
|
|
|
|
bson_write_int32(dst, "fifo-base", direct_sound_channel[i].fifo_base);
|
|
|
|
bson_write_int32(dst, "fifo-top", direct_sound_channel[i].fifo_top);
|
|
|
|
bson_write_int32(dst, "fifo-frac", direct_sound_channel[i].fifo_fractional);
|
|
|
|
bson_write_bytes(dst, "fifo-bytes", direct_sound_channel[i].fifo,
|
|
|
|
sizeof(direct_sound_channel[i].fifo));
|
|
|
|
bson_write_int32(dst, "buf-idx", direct_sound_channel[i].buffer_index);
|
|
|
|
bson_finish_document(dst, wbptr2);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
gbc_sound_struct *gs = &gbc_sound_channel[i];
|
|
|
|
u8 *wbptr2;
|
|
|
|
char tn[4] = {'g', 's', '0' + i, 0};
|
|
|
|
bson_start_document(dst, tn, wbptr2);
|
|
|
|
bson_write_int32(dst, "status", gs->status);
|
|
|
|
bson_write_int32(dst, "rate", gs->rate);
|
|
|
|
bson_write_int32(dst, "freq-step", gs->frequency_step);
|
|
|
|
bson_write_int32(dst, "sample-idx", gs->sample_index);
|
|
|
|
bson_write_int32(dst, "tick-cnt", gs->tick_counter);
|
|
|
|
bson_write_int32(dst, "volume", gs->total_volume);
|
|
|
|
bson_write_int32(dst, "active", gs->active_flag);
|
|
|
|
bson_write_int32(dst, "enable", gs->master_enable);
|
|
|
|
|
|
|
|
bson_write_int32(dst, "env-vol0", gs->envelope_initial_volume);
|
|
|
|
bson_write_int32(dst, "env-vol", gs->envelope_volume);
|
|
|
|
bson_write_int32(dst, "env-dir", gs->envelope_direction);
|
|
|
|
bson_write_int32(dst, "env-status", gs->envelope_status);
|
|
|
|
bson_write_int32(dst, "env-step", gs->envelope_step);
|
|
|
|
bson_write_int32(dst, "env-ticks0", gs->envelope_initial_ticks);
|
|
|
|
bson_write_int32(dst, "env-ticks", gs->envelope_ticks);
|
|
|
|
bson_write_int32(dst, "sweep-status", gs->sweep_status);
|
|
|
|
bson_write_int32(dst, "sweep-dir", gs->sweep_direction);
|
|
|
|
bson_write_int32(dst, "sweep-ticks0", gs->sweep_initial_ticks);
|
|
|
|
bson_write_int32(dst, "sweep-ticks", gs->sweep_ticks);
|
|
|
|
bson_write_int32(dst, "sweep-shift", gs->sweep_shift);
|
|
|
|
bson_write_int32(dst, "wav-type", gs->wave_type);
|
|
|
|
bson_write_int32(dst, "wav-bank", gs->wave_bank);
|
|
|
|
bson_write_int32(dst, "wav-vol", gs->wave_volume);
|
|
|
|
|
|
|
|
bson_write_int32(dst, "len-status", gs->length_status);
|
|
|
|
bson_write_int32(dst, "len-ticks", gs->length_ticks);
|
|
|
|
bson_write_int32(dst, "noise-type", gs->noise_type);
|
|
|
|
bson_write_int32(dst, "sample-tbl", gs->sample_table_idx);
|
|
|
|
bson_finish_document(dst, wbptr2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bson_finish_document(dst, wbptr);
|
|
|
|
return (unsigned int)(dst - startp);
|
|
|
|
}
|
2009-05-21 17:48:31 +02:00
|
|
|
|
2014-12-09 00:17:28 +01:00
|
|
|
|
|
|
|
#include "libretro.h"
|
|
|
|
|
|
|
|
static retro_audio_sample_batch_t audio_batch_cb;
|
|
|
|
void retro_set_audio_sample(retro_audio_sample_t cb) { }
|
|
|
|
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
|
|
|
|
|
|
|
|
void render_audio(void)
|
|
|
|
{
|
2014-12-09 05:47:45 +01:00
|
|
|
static s16 stream_base[512];
|
2014-12-09 00:17:28 +01:00
|
|
|
s16 *source;
|
|
|
|
u32 i;
|
|
|
|
|
2014-12-09 17:52:17 +01:00
|
|
|
while (((gbc_sound_buffer_index - sound_buffer_base) & BUFFER_SIZE_MASK) > 512)
|
|
|
|
{
|
|
|
|
source = (s16 *)(sound_buffer + sound_buffer_base);
|
|
|
|
for(i = 0; i < 512; i++)
|
|
|
|
{
|
|
|
|
s32 current_sample = source[i];
|
|
|
|
if(current_sample > 2047)
|
|
|
|
current_sample = 2047;
|
|
|
|
if(current_sample < -2048)
|
|
|
|
current_sample = -2048;
|
2021-06-30 00:29:21 +02:00
|
|
|
stream_base[i] = current_sample * 16;
|
2014-12-09 17:52:17 +01:00
|
|
|
source[i] = 0;
|
|
|
|
}
|
2014-12-09 00:17:28 +01:00
|
|
|
audio_batch_cb(stream_base, 256);
|
|
|
|
sound_buffer_base += 512;
|
2014-12-09 05:36:30 +01:00
|
|
|
sound_buffer_base &= BUFFER_SIZE_MASK;
|
2014-12-09 00:17:28 +01:00
|
|
|
}
|
|
|
|
}
|