swan: Rewritten audio driver
Now outputs 24000 Hz digital samples, matching real hardware (as opposed to 3072000 Hz samples). It has also been rewritten from scratch to match recent research and make the code significantly more readable.
This commit is contained in:
parent
a02d289d49
commit
03b87258c8
|
@ -629,7 +629,7 @@ src/engine/platform/sound/pokey/AltASAP.cpp
|
||||||
|
|
||||||
src/engine/platform/sound/qsound.c
|
src/engine/platform/sound/qsound.c
|
||||||
|
|
||||||
src/engine/platform/sound/swan.cpp
|
src/engine/platform/sound/swan.c
|
||||||
|
|
||||||
src/engine/platform/sound/su.cpp
|
src/engine/platform/sound/su.cpp
|
||||||
|
|
||||||
|
|
259
src/engine/platform/sound/swan.c
Normal file
259
src/engine/platform/sound/swan.c
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
/*
|
||||||
|
WonderSwan sound core
|
||||||
|
|
||||||
|
Copyright (c) 2025 Adrian "asie" Siekierka
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "swan.h"
|
||||||
|
|
||||||
|
#define HYPERV_ENABLE 0x0080
|
||||||
|
|
||||||
|
#define SND_NOISE_ENABLE 0x10
|
||||||
|
#define SND_NOISE_RESET 0x08
|
||||||
|
|
||||||
|
#define SND_CH1_ENABLE 0x01
|
||||||
|
#define SND_CH2_ENABLE 0x02
|
||||||
|
#define SND_CH3_ENABLE 0x04
|
||||||
|
#define SND_CH4_ENABLE 0x08
|
||||||
|
#define SND_CH2_VOICE 0x20
|
||||||
|
#define SND_CH3_SWEEP 0x40
|
||||||
|
#define SND_CH4_NOISE 0x80
|
||||||
|
|
||||||
|
#define SND_OUT_HEADPHONES 0x80
|
||||||
|
#define SND_OUT_HEADPHONES_ENABLE 0x08
|
||||||
|
#define SND_OUT_SPEAKER_ENABLE 0x01
|
||||||
|
|
||||||
|
#define SND_VOL_CH2_LEFT_HALF 0x08
|
||||||
|
#define SND_VOL_CH2_LEFT_FULL 0x01
|
||||||
|
#define SND_VOL_CH2_RIGHT_HALF 0x02
|
||||||
|
#define SND_VOL_CH2_RIGHT_FULL 0x01
|
||||||
|
|
||||||
|
#define SND_TEST_CH_VOICE 0x20
|
||||||
|
#define SND_TEST_FAST_SWEEP 0x02
|
||||||
|
#define SND_TEST_HOLD_CH 0x01
|
||||||
|
|
||||||
|
static const uint8_t lfsr_tap_bits[8] = { 14, 10, 13, 4, 8, 6, 9, 11 };
|
||||||
|
|
||||||
|
void swan_sound_init(swan_sound_t *snd, bool headphones) {
|
||||||
|
memset(snd, 0, sizeof(swan_sound_t));
|
||||||
|
|
||||||
|
snd->out_ctrl = headphones ? 0x80 : 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t swan_sound_in(swan_sound_t *snd, uint16_t port) {
|
||||||
|
switch (port & 0x1FF) {
|
||||||
|
case 0x6A: return snd->hyper_ctrl;
|
||||||
|
case 0x6B: return snd->hyper_ctrl >> 8;
|
||||||
|
case 0x80: return snd->frequency[0];
|
||||||
|
case 0x81: return snd->frequency[0] >> 8;
|
||||||
|
case 0x82: return snd->frequency[1];
|
||||||
|
case 0x83: return snd->frequency[1] >> 8;
|
||||||
|
case 0x84: return snd->frequency[2];
|
||||||
|
case 0x85: return snd->frequency[2] >> 8;
|
||||||
|
case 0x86: return snd->frequency[3];
|
||||||
|
case 0x87: return snd->frequency[3] >> 8;
|
||||||
|
case 0x88: return snd->volume[0];
|
||||||
|
case 0x89: return snd->volume[1];
|
||||||
|
case 0x8A: return snd->volume[2];
|
||||||
|
case 0x8B: return snd->volume[3];
|
||||||
|
case 0x8C: return snd->sweep_amount;
|
||||||
|
case 0x8D: return snd->sweep_ticks;
|
||||||
|
case 0x8E: return snd->noise_ctrl;
|
||||||
|
case 0x8F: return snd->wave_address;
|
||||||
|
case 0x90: return snd->ch_ctrl;
|
||||||
|
case 0x91: return snd->out_ctrl;
|
||||||
|
case 0x92: return snd->noise_lfsr;
|
||||||
|
case 0x93: return snd->noise_lfsr >> 8;
|
||||||
|
case 0x94: return snd->voice_volume;
|
||||||
|
case 0x95: return snd->test_flags;
|
||||||
|
case 0x96: return snd->synth_output_right;
|
||||||
|
case 0x97: return snd->synth_output_right >> 8;
|
||||||
|
case 0x98: return snd->synth_output_left;
|
||||||
|
case 0x99: return snd->synth_output_left >> 8;
|
||||||
|
case 0x9A: return snd->synth_output_mono;
|
||||||
|
case 0x9B: return snd->synth_output_mono >> 8;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void swan_sound_out(swan_sound_t *snd, uint16_t port, uint8_t value) {
|
||||||
|
switch (port & 0x1FF) {
|
||||||
|
case 0x6A: snd->hyper_ctrl = (snd->hyper_ctrl & 0xFF00) | value; break;
|
||||||
|
case 0x6B: snd->hyper_ctrl = (snd->hyper_ctrl & 0x0FFF) | ((value & 0x70) << 8); break;
|
||||||
|
case 0x80: snd->frequency[0] = (snd->frequency[0] & 0xFF00) | value; break;
|
||||||
|
case 0x81: snd->frequency[0] = (snd->frequency[0] & 0xFF) | ((value & 0x7) << 8); break;
|
||||||
|
case 0x82: snd->frequency[1] = (snd->frequency[1] & 0xFF00) | value; break;
|
||||||
|
case 0x83: snd->frequency[1] = (snd->frequency[1] & 0xFF) | ((value & 0x7) << 8); break;
|
||||||
|
case 0x84: snd->frequency[2] = (snd->frequency[2] & 0xFF00) | value; break;
|
||||||
|
case 0x85: snd->frequency[2] = (snd->frequency[2] & 0xFF) | ((value & 0x7) << 8); break;
|
||||||
|
case 0x86: snd->frequency[3] = (snd->frequency[3] & 0xFF00) | value; break;
|
||||||
|
case 0x87: snd->frequency[3] = (snd->frequency[3] & 0xFF) | ((value & 0x7) << 8); break;
|
||||||
|
case 0x88: snd->volume[0] = value; break;
|
||||||
|
case 0x89: snd->volume[1] = value; break;
|
||||||
|
case 0x8A: snd->volume[2] = value; break;
|
||||||
|
case 0x8B: snd->volume[3] = value; break;
|
||||||
|
case 0x8C: snd->sweep_amount = value; break;
|
||||||
|
case 0x8D: snd->sweep_ticks = value & 0x1F; break;
|
||||||
|
case 0x8E:
|
||||||
|
snd->noise_ctrl = value & 0x17;
|
||||||
|
if (value & SND_NOISE_RESET) {
|
||||||
|
snd->noise_lfsr = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x8F: snd->wave_address = value; break;
|
||||||
|
case 0x90: snd->ch_ctrl = value; break;
|
||||||
|
case 0x91: snd->out_ctrl = (snd->out_ctrl & 0x80) | (value & 0x0F); break;
|
||||||
|
case 0x94: snd->voice_volume = value; break;
|
||||||
|
case 0x95: snd->test_flags = value; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swan_sound_subtick(swan_sound_t *snd, uint32_t cycles) {
|
||||||
|
for (int ch = 0; ch < 4; ch++) {
|
||||||
|
snd->period_counter[ch] += cycles;
|
||||||
|
uint32_t step = 2048 - snd->frequency[ch];
|
||||||
|
while (snd->period_counter[ch] >= step) {
|
||||||
|
snd->sample_index[ch] = (snd->sample_index[ch] + 1) & 0x1F;
|
||||||
|
snd->period_counter[ch] -= step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd->ch_ctrl & SND_CH3_SWEEP) {
|
||||||
|
snd->sweep_counter += cycles;
|
||||||
|
uint32_t step = ((snd->test_flags & SND_TEST_FAST_SWEEP) ? 1 : 8192) * (snd->sweep_ticks + 1);
|
||||||
|
while (snd->sweep_counter >= step) {
|
||||||
|
snd->frequency[2] = (snd->frequency[2] + snd->sweep_amount) & 0x7FF;
|
||||||
|
snd->sweep_counter -= step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sample_render_channel(swan_sound_t *snd, uint8_t ch, uint8_t sample) {
|
||||||
|
snd->ch_output_right[ch] = sample * (snd->volume[ch] & 0xF);
|
||||||
|
snd->ch_output_left[ch] = sample * (snd->volume[ch] >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void wavetable_render_channel(swan_sound_t *snd, uint8_t ch) {
|
||||||
|
uint8_t index = snd->sample_index[ch];
|
||||||
|
uint8_t addr = (ch << 4) | (index >> 1);
|
||||||
|
sample_render_channel(snd, ch, (index & 1) ? (snd->wave_ram[addr] >> 4) : (snd->wave_ram[addr] & 0xF));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void voice_render_channel2(swan_sound_t *snd) {
|
||||||
|
if (snd->voice_volume & SND_VOL_CH2_RIGHT_FULL) {
|
||||||
|
snd->ch_output_right[1] = snd->volume[1];
|
||||||
|
} else if (snd->voice_volume & SND_VOL_CH2_RIGHT_HALF) {
|
||||||
|
snd->ch_output_right[1] = snd->volume[1] >> 1;
|
||||||
|
}
|
||||||
|
if (snd->voice_volume & SND_VOL_CH2_LEFT_FULL) {
|
||||||
|
snd->ch_output_left[1] = snd->volume[1];
|
||||||
|
} else if (snd->voice_volume & SND_VOL_CH2_LEFT_HALF) {
|
||||||
|
snd->ch_output_left[1] = snd->volume[1] >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://ws.nesdev.org/wiki/Sound
|
||||||
|
void swan_sound_tick(swan_sound_t *snd) {
|
||||||
|
// Update counters
|
||||||
|
swan_sound_subtick(snd, 128);
|
||||||
|
|
||||||
|
// Update noise counter
|
||||||
|
if (snd->noise_ctrl & SND_NOISE_ENABLE) {
|
||||||
|
uint8_t lfsr_new = (1 ^ (snd->noise_lfsr >> 7) ^ (snd->noise_lfsr >> lfsr_tap_bits[snd->noise_ctrl & 7])) & 0x1;
|
||||||
|
snd->noise_lfsr = (snd->noise_lfsr << 1) | lfsr_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate synthesizer channels
|
||||||
|
if (!(snd->test_flags & SND_TEST_HOLD_CH)) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
snd->ch_output_left[i] = 0;
|
||||||
|
snd->ch_output_right[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd->ch_ctrl & SND_CH1_ENABLE) {
|
||||||
|
wavetable_render_channel(snd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd->ch_ctrl & SND_CH2_ENABLE) {
|
||||||
|
if (snd->ch_ctrl & SND_CH2_VOICE) {
|
||||||
|
voice_render_channel2(snd);
|
||||||
|
} else {
|
||||||
|
wavetable_render_channel(snd, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd->ch_ctrl & SND_CH3_ENABLE) {
|
||||||
|
wavetable_render_channel(snd, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd->ch_ctrl & SND_CH4_ENABLE) {
|
||||||
|
if (snd->ch_ctrl & SND_CH4_NOISE) {
|
||||||
|
sample_render_channel(snd, 3, (snd->noise_lfsr & 1) * 15);
|
||||||
|
} else {
|
||||||
|
wavetable_render_channel(snd, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum synthesizer channels
|
||||||
|
if (snd->test_flags & SND_TEST_CH_VOICE) {
|
||||||
|
if (snd->ch_ctrl & SND_CH2_VOICE) {
|
||||||
|
snd->synth_output_left = snd->ch_output_left[1] * 5;
|
||||||
|
snd->synth_output_right = snd->ch_output_right[1] * 5;
|
||||||
|
} else {
|
||||||
|
snd->synth_output_left = 0;
|
||||||
|
snd->synth_output_right = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snd->synth_output_left =
|
||||||
|
snd->ch_output_left[0] +
|
||||||
|
snd->ch_output_left[1] +
|
||||||
|
snd->ch_output_left[2] +
|
||||||
|
snd->ch_output_left[3];
|
||||||
|
|
||||||
|
snd->synth_output_right =
|
||||||
|
snd->ch_output_right[0] +
|
||||||
|
snd->ch_output_right[1] +
|
||||||
|
snd->ch_output_right[2] +
|
||||||
|
snd->ch_output_right[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix speaker output
|
||||||
|
snd->synth_output_mono = (snd->synth_output_left & 0x3FF) + (snd->synth_output_right & 0x3FF);
|
||||||
|
if (snd->out_ctrl & SND_OUT_SPEAKER_ENABLE) {
|
||||||
|
snd->output_speaker = (snd->synth_output_mono >> ((snd->out_ctrl >> 1) & 3)) & 0xFF;
|
||||||
|
} else {
|
||||||
|
snd->output_speaker = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix headphone output
|
||||||
|
if (snd->out_ctrl & SND_OUT_HEADPHONES_ENABLE) {
|
||||||
|
snd->output_left = snd->synth_output_left << 5;
|
||||||
|
snd->output_right = snd->synth_output_right << 5;
|
||||||
|
if (snd->hyper_ctrl & HYPERV_ENABLE) {
|
||||||
|
snd->output_left += snd->hyper_output_left;
|
||||||
|
snd->output_right += snd->hyper_output_right;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snd->output_left = 0;
|
||||||
|
snd->output_right = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,363 +0,0 @@
|
||||||
/******************************************************************************/
|
|
||||||
/* Mednafen - Multi-system Emulator */
|
|
||||||
/******************************************************************************/
|
|
||||||
/* sound.cpp - WonderSwan Sound Emulation
|
|
||||||
** Copyright (C) 2007-2017 Mednafen Team
|
|
||||||
** Copyright (C) 2016 Alex 'trap15' Marshall - http://daifukkat.su/
|
|
||||||
**
|
|
||||||
** 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 "swan.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define MK_SAMPLE_CACHE \
|
|
||||||
{ \
|
|
||||||
int sample; \
|
|
||||||
sample = (((wsRAM[(/*(SampleRAMPos << 6) + */(sample_pos[ch] >> 1) + (ch << 4)) ] >> ((sample_pos[ch] & 1) ? 4 : 0)) & 0x0F)); \
|
|
||||||
sample_cache[ch][0] = sample * ((volume[ch] >> 4) & 0x0F); \
|
|
||||||
sample_cache[ch][1] = sample * ((volume[ch] >> 0) & 0x0F); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MK_SAMPLE_CACHE_NOISE \
|
|
||||||
{ \
|
|
||||||
int sample; \
|
|
||||||
sample = ((nreg & 1) ? 0xF : 0x0); \
|
|
||||||
sample_cache[ch][0] = sample * ((volume[ch] >> 4) & 0x0F); \
|
|
||||||
sample_cache[ch][1] = sample * ((volume[ch] >> 0) & 0x0F); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MK_SAMPLE_CACHE_VOICE \
|
|
||||||
{ \
|
|
||||||
int sample, half; \
|
|
||||||
sample = volume[ch]; \
|
|
||||||
half = sample >> 1; \
|
|
||||||
sample_cache[ch][0] = (voice_volume & 4) ? sample : (voice_volume & 8) ? half : 0; \
|
|
||||||
sample_cache[ch][1] = (voice_volume & 1) ? sample : (voice_volume & 2) ? half : 0; \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define SYNCSAMPLE(wt) \
|
|
||||||
{ \
|
|
||||||
int32_t left = sample_cache[ch][0] << 5, right = sample_cache[ch][1] << 5; \
|
|
||||||
if (left!=last_val[ch][0]) { \
|
|
||||||
blip_add_delta(sbuf[0], wt, left - last_val[ch][0]); \
|
|
||||||
last_val[ch][0] = left; \
|
|
||||||
} \
|
|
||||||
if (right!=last_val[ch][1]) { \
|
|
||||||
blip_add_delta(sbuf[1], wt, right - last_val[ch][1]); \
|
|
||||||
last_val[ch][1] = right; \
|
|
||||||
} \
|
|
||||||
oscBuf[ch]->putSample(wt,(left+right)<<1); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SYNCSAMPLE_NOISE(wt) SYNCSAMPLE(wt)
|
|
||||||
|
|
||||||
void WSwan::SoundUpdate(void)
|
|
||||||
{
|
|
||||||
int32_t run_time;
|
|
||||||
|
|
||||||
//printf("%d\n", v30mz_timestamp);
|
|
||||||
//printf("%02x %02x\n", control, noise_control);
|
|
||||||
run_time = v30mz_timestamp - last_ts;
|
|
||||||
|
|
||||||
for(unsigned int ch = 0; ch < 4; ch++)
|
|
||||||
{
|
|
||||||
// Channel is disabled?
|
|
||||||
if(!(control & (1 << ch)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(ch == 1 && (control & 0x20)) // Direct D/A mode?
|
|
||||||
{
|
|
||||||
MK_SAMPLE_CACHE_VOICE;
|
|
||||||
SYNCSAMPLE(v30mz_timestamp);
|
|
||||||
}
|
|
||||||
else if(ch == 2 && (control & 0x40) && sweep_value) // Sweep
|
|
||||||
{
|
|
||||||
uint32_t tmp_pt = 2048 - period[ch];
|
|
||||||
uint32_t meow_timestamp = v30mz_timestamp - run_time;
|
|
||||||
uint32_t tmp_run_time = run_time;
|
|
||||||
|
|
||||||
while(tmp_run_time)
|
|
||||||
{
|
|
||||||
int32_t sub_run_time = tmp_run_time;
|
|
||||||
|
|
||||||
if(sub_run_time > sweep_8192_divider)
|
|
||||||
sub_run_time = sweep_8192_divider;
|
|
||||||
|
|
||||||
sweep_8192_divider -= sub_run_time;
|
|
||||||
if(sweep_8192_divider <= 0)
|
|
||||||
{
|
|
||||||
sweep_8192_divider += 8192;
|
|
||||||
sweep_counter--;
|
|
||||||
if(sweep_counter <= 0)
|
|
||||||
{
|
|
||||||
sweep_counter = sweep_step + 1;
|
|
||||||
period[ch] = (period[ch] + (int8_t)sweep_value) & 0x7FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meow_timestamp += sub_run_time;
|
|
||||||
if(tmp_pt > 4)
|
|
||||||
{
|
|
||||||
period_counter[ch] -= sub_run_time;
|
|
||||||
while(period_counter[ch] <= 0)
|
|
||||||
{
|
|
||||||
sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F;
|
|
||||||
|
|
||||||
MK_SAMPLE_CACHE;
|
|
||||||
SYNCSAMPLE(meow_timestamp + period_counter[ch]);
|
|
||||||
period_counter[ch] += tmp_pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmp_run_time -= sub_run_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(ch == 3 && (control & 0x80) && (noise_control & 0x10)) // Noise
|
|
||||||
{
|
|
||||||
uint32_t tmp_pt = 2048 - period[ch];
|
|
||||||
|
|
||||||
period_counter[ch] -= run_time;
|
|
||||||
while(period_counter[ch] <= 0)
|
|
||||||
{
|
|
||||||
static const uint8_t stab[8] = { 14, 10, 13, 4, 8, 6, 9, 11 };
|
|
||||||
|
|
||||||
nreg = ((nreg << 1) | ((1 ^ (nreg >> 7) ^ (nreg >> stab[noise_control & 0x7])) & 1)) & 0x7FFF;
|
|
||||||
|
|
||||||
if(control & 0x80)
|
|
||||||
{
|
|
||||||
MK_SAMPLE_CACHE_NOISE;
|
|
||||||
SYNCSAMPLE_NOISE(v30mz_timestamp + period_counter[ch]);
|
|
||||||
}
|
|
||||||
else if(tmp_pt > 4)
|
|
||||||
{
|
|
||||||
sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F;
|
|
||||||
MK_SAMPLE_CACHE;
|
|
||||||
SYNCSAMPLE(v30mz_timestamp + period_counter[ch]);
|
|
||||||
}
|
|
||||||
period_counter[ch] += tmp_pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint32_t tmp_pt = 2048 - period[ch];
|
|
||||||
|
|
||||||
if(tmp_pt > 4)
|
|
||||||
{
|
|
||||||
period_counter[ch] -= run_time;
|
|
||||||
while(period_counter[ch] <= 0)
|
|
||||||
{
|
|
||||||
sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F;
|
|
||||||
|
|
||||||
MK_SAMPLE_CACHE;
|
|
||||||
SYNCSAMPLE(v30mz_timestamp + period_counter[ch]); // - period_counter[ch]);
|
|
||||||
period_counter[ch] += tmp_pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(HVoiceCtrl & 0x80)
|
|
||||||
{
|
|
||||||
int16_t sample = (uint8_t)HyperVoice;
|
|
||||||
|
|
||||||
switch(HVoiceCtrl & 0xC)
|
|
||||||
{
|
|
||||||
case 0x0: sample = (uint16_t)sample << (8 - (HVoiceCtrl & 3)); break;
|
|
||||||
case 0x4: sample = (uint16_t)(sample | -0x100) << (8 - (HVoiceCtrl & 3)); break;
|
|
||||||
case 0x8: sample = (uint16_t)((int8_t)sample) << (8 - (HVoiceCtrl & 3)); break;
|
|
||||||
case 0xC: sample = (uint16_t)sample << 8; break;
|
|
||||||
}
|
|
||||||
// bring back to 11bit, keeping signedness
|
|
||||||
sample >>= 5;
|
|
||||||
|
|
||||||
int32_t left, right;
|
|
||||||
left = (HVoiceChanCtrl & 0x40) ? (sample << 5) : 0;
|
|
||||||
right = (HVoiceChanCtrl & 0x20) ? (sample << 5) : 0;
|
|
||||||
|
|
||||||
if (left!=last_hv_val[0]) {
|
|
||||||
blip_add_delta(sbuf[0], v30mz_timestamp, left - last_hv_val[0]);
|
|
||||||
last_hv_val[0] = left;
|
|
||||||
}
|
|
||||||
if (right!=last_hv_val[1]) {
|
|
||||||
blip_add_delta(sbuf[1], v30mz_timestamp, right - last_hv_val[1]);
|
|
||||||
last_hv_val[1] = right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_ts = v30mz_timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WSwan::SoundWrite(uint32_t A, uint8_t V)
|
|
||||||
{
|
|
||||||
SoundUpdate();
|
|
||||||
|
|
||||||
if(A >= 0x80 && A <= 0x87)
|
|
||||||
{
|
|
||||||
int ch = (A - 0x80) >> 1;
|
|
||||||
|
|
||||||
if(A & 1)
|
|
||||||
period[ch] = (period[ch] & 0x00FF) | ((V & 0x07) << 8);
|
|
||||||
else
|
|
||||||
period[ch] = (period[ch] & 0x0700) | ((V & 0xFF) << 0);
|
|
||||||
|
|
||||||
//printf("Period %d: 0x%04x --- %f\n", ch, period[ch], 3072000.0 / (2048 - period[ch]));
|
|
||||||
}
|
|
||||||
else if(A >= 0x88 && A <= 0x8B)
|
|
||||||
{
|
|
||||||
volume[A - 0x88] = V;
|
|
||||||
}
|
|
||||||
else if(A == 0x8C)
|
|
||||||
sweep_value = V;
|
|
||||||
else if(A == 0x8D)
|
|
||||||
{
|
|
||||||
sweep_step = V;
|
|
||||||
sweep_counter = sweep_step + 1;
|
|
||||||
sweep_8192_divider = 8192;
|
|
||||||
}
|
|
||||||
else if(A == 0x8E)
|
|
||||||
{
|
|
||||||
//printf("NOISECONTROL: %02x\n", V);
|
|
||||||
if(V & 0x8)
|
|
||||||
nreg = 0;
|
|
||||||
|
|
||||||
noise_control = V & 0x17;
|
|
||||||
}
|
|
||||||
else if(A == 0x90)
|
|
||||||
{
|
|
||||||
for(int n = 0; n < 4; n++)
|
|
||||||
{
|
|
||||||
if(!(control & (1 << n)) && (V & (1 << n)))
|
|
||||||
{
|
|
||||||
period_counter[n] = 1;
|
|
||||||
sample_pos[n] = 0x1F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
control = V;
|
|
||||||
//printf("Sound Control: %02x\n", V);
|
|
||||||
}
|
|
||||||
else if(A == 0x91)
|
|
||||||
{
|
|
||||||
output_control = V & 0xF;
|
|
||||||
//printf("%02x, %02x\n", V, (V >> 1) & 3);
|
|
||||||
}
|
|
||||||
else if(A == 0x92)
|
|
||||||
nreg = (nreg & 0xFF00) | (V << 0);
|
|
||||||
else if(A == 0x93)
|
|
||||||
nreg = (nreg & 0x00FF) | ((V & 0x7F) << 8);
|
|
||||||
else if(A == 0x94)
|
|
||||||
{
|
|
||||||
voice_volume = V & 0xF;
|
|
||||||
//printf("%02x\n", V);
|
|
||||||
}
|
|
||||||
else switch(A)
|
|
||||||
{
|
|
||||||
case 0x6A: HVoiceCtrl = V; break;
|
|
||||||
case 0x6B: HVoiceChanCtrl = V & 0x6F; break;
|
|
||||||
case 0x8F: SampleRAMPos = V; break;
|
|
||||||
case 0x95: HyperVoice = V; break; // Pick a port, any port?!
|
|
||||||
//default: printf("%04x:%02x\n", A, V); break;
|
|
||||||
}
|
|
||||||
SoundUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t WSwan::SoundRead(uint32_t A)
|
|
||||||
{
|
|
||||||
SoundUpdate();
|
|
||||||
|
|
||||||
if(A >= 0x80 && A <= 0x87)
|
|
||||||
{
|
|
||||||
int ch = (A - 0x80) >> 1;
|
|
||||||
|
|
||||||
if(A & 1)
|
|
||||||
return(period[ch] >> 8);
|
|
||||||
else
|
|
||||||
return(period[ch]);
|
|
||||||
}
|
|
||||||
else if(A >= 0x88 && A <= 0x8B)
|
|
||||||
return(volume[A - 0x88]);
|
|
||||||
else switch(A)
|
|
||||||
{
|
|
||||||
default: /*printf("SoundRead: %04x\n", A);*/ return(0);
|
|
||||||
case 0x6A: return(HVoiceCtrl);
|
|
||||||
case 0x6B: return(HVoiceChanCtrl);
|
|
||||||
case 0x8C: return(sweep_value);
|
|
||||||
case 0x8D: return(sweep_step);
|
|
||||||
case 0x8E: return(noise_control);
|
|
||||||
case 0x8F: return(SampleRAMPos);
|
|
||||||
case 0x90: return(control);
|
|
||||||
case 0x91: return(output_control | 0x80);
|
|
||||||
case 0x92: return((nreg >> 0) & 0xFF);
|
|
||||||
case 0x93: return((nreg >> 8) & 0xFF);
|
|
||||||
case 0x94: return(voice_volume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WSwan::RAMWrite(uint32_t A, uint8_t V)
|
|
||||||
{
|
|
||||||
wsRAM[A & 0x3F] = V;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t WSwan::SoundFlush(int16_t *SoundBuf, const int32_t MaxSoundFrames)
|
|
||||||
{
|
|
||||||
int32_t FrameCount = 0;
|
|
||||||
|
|
||||||
SoundUpdate();
|
|
||||||
|
|
||||||
|
|
||||||
last_ts = 0;
|
|
||||||
|
|
||||||
return(FrameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call before wsRAM is updated
|
|
||||||
void WSwan::SoundCheckRAMWrite(uint32_t A)
|
|
||||||
{
|
|
||||||
if((A >> 6) == SampleRAMPos)
|
|
||||||
SoundUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WSwan::SoundReset(void)
|
|
||||||
{
|
|
||||||
memset(period, 0, sizeof(period));
|
|
||||||
memset(volume, 0, sizeof(volume));
|
|
||||||
voice_volume = 0;
|
|
||||||
sweep_step = 0;
|
|
||||||
sweep_value = 0;
|
|
||||||
noise_control = 0;
|
|
||||||
control = 0;
|
|
||||||
output_control = 0;
|
|
||||||
|
|
||||||
sweep_8192_divider = 8192;
|
|
||||||
sweep_counter = 1;
|
|
||||||
SampleRAMPos = 0;
|
|
||||||
|
|
||||||
for(unsigned ch = 0; ch < 4; ch++)
|
|
||||||
period_counter[ch] = 1;
|
|
||||||
|
|
||||||
memset(sample_pos, 0, sizeof(sample_pos));
|
|
||||||
nreg = 0;
|
|
||||||
|
|
||||||
memset(sample_cache, 0, sizeof(sample_cache));
|
|
||||||
memset(last_val, 0, sizeof(last_val));
|
|
||||||
last_v_val = 0;
|
|
||||||
|
|
||||||
HyperVoice = 0;
|
|
||||||
last_hv_val[0] = last_hv_val[1] = 0;
|
|
||||||
HVoiceCtrl = 0;
|
|
||||||
HVoiceChanCtrl = 0;
|
|
||||||
|
|
||||||
last_ts = 0;
|
|
||||||
v30mz_timestamp = 0;
|
|
||||||
}
|
|
|
@ -1,79 +1,92 @@
|
||||||
/******************************************************************************/
|
/*
|
||||||
/* Mednafen - Multi-system Emulator */
|
WonderSwan sound core
|
||||||
/******************************************************************************/
|
|
||||||
/* sound.h - WonderSwan Sound Emulation
|
Note: Neither Sound DMA nor Hyper Voice DMA is implemented.
|
||||||
** Copyright (C) 2007-2016 Mednafen Team
|
|
||||||
**
|
Copyright (c) 2025 Adrian "asie" Siekierka
|
||||||
** This program is free software; you can redistribute it and/or
|
|
||||||
** modify it under the terms of the GNU General Public License
|
This software is provided 'as-is', without any express or implied
|
||||||
** as published by the Free Software Foundation; either version 2
|
warranty. In no event will the authors be held liable for any damages
|
||||||
** of the License, or (at your option) any later version.
|
arising from the use of this software.
|
||||||
**
|
|
||||||
** This program is distributed in the hope that it will be useful,
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
including commercial applications, and to alter it and redistribute it
|
||||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
freely, subject to the following restrictions:
|
||||||
** GNU General Public License for more details.
|
|
||||||
**
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
** You should have received a copy of the GNU General Public License
|
claim that you wrote the original software. If you use this software
|
||||||
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
in a product, an acknowledgment in the product documentation would be
|
||||||
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __WSWAN_SOUND_H
|
#ifndef _DRIVER_SWAN_H
|
||||||
#define __WSWAN_SOUND_H
|
#define _DRIVER_SWAN_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "blip_buf.h"
|
|
||||||
#include "../../dispatch.h"
|
|
||||||
|
|
||||||
class WSwan
|
typedef struct swan_sound {
|
||||||
{
|
// Ports
|
||||||
public:
|
uint16_t frequency[4];
|
||||||
int32_t SoundFlush(int16_t *SoundBuf, const int32_t MaxSoundFrames);
|
uint8_t volume[4];
|
||||||
|
int8_t sweep_amount;
|
||||||
|
uint8_t sweep_ticks;
|
||||||
|
uint8_t noise_ctrl;
|
||||||
|
uint8_t wave_address;
|
||||||
|
uint8_t ch_ctrl;
|
||||||
|
uint8_t out_ctrl;
|
||||||
|
uint16_t noise_lfsr;
|
||||||
|
uint8_t voice_volume;
|
||||||
|
// TODO: Implement test flag bits 7, 6, 4, 3, 2
|
||||||
|
uint8_t test_flags;
|
||||||
|
|
||||||
void SoundWrite(uint32_t, uint8_t);
|
uint8_t wave_ram[64];
|
||||||
uint8_t SoundRead(uint32_t);
|
|
||||||
void SoundReset(void);
|
|
||||||
void SoundCheckRAMWrite(uint32_t A);
|
|
||||||
|
|
||||||
void SoundUpdate();
|
uint16_t hyper_ctrl;
|
||||||
void RAMWrite(uint32_t, uint8_t);
|
|
||||||
|
|
||||||
int32_t sample_cache[4][2];
|
// State
|
||||||
|
uint8_t sample_index[4];
|
||||||
|
uint32_t period_counter[4];
|
||||||
|
uint32_t sweep_counter;
|
||||||
|
|
||||||
// Blip_Synth<blip_good_quality, 4096> WaveSynth;
|
// Outputs
|
||||||
|
/// Individual channel outputs (range: 0 .. 255)
|
||||||
|
int16_t ch_output_right[4];
|
||||||
|
int16_t ch_output_left[4];
|
||||||
|
|
||||||
// Blip_Buffer *sbuf[2] = { NULL };
|
/// Hyper Voice outputs (range: -32768 .. 32767)
|
||||||
blip_buffer_t* sbuf[2];
|
int16_t hyper_output_left;
|
||||||
DivDispatchOscBuffer* oscBuf[4];
|
int16_t hyper_output_right;
|
||||||
|
|
||||||
uint16_t period[4];
|
/// Stereo synth outputs (range: 0 .. 1023)
|
||||||
uint8_t volume[4]; // left volume in upper 4 bits, right in lower 4 bits
|
uint16_t synth_output_right;
|
||||||
uint8_t voice_volume;
|
uint16_t synth_output_left;
|
||||||
|
|
||||||
uint8_t sweep_step, sweep_value;
|
/// Mono synth output (range: 0 .. 2047)
|
||||||
uint8_t noise_control;
|
uint16_t synth_output_mono;
|
||||||
uint8_t control;
|
|
||||||
uint8_t output_control;
|
|
||||||
|
|
||||||
int32_t sweep_8192_divider;
|
/// Headphones output (range: -32768 .. 32767)
|
||||||
uint8_t sweep_counter;
|
int16_t output_right;
|
||||||
uint8_t SampleRAMPos;
|
int16_t output_left;
|
||||||
|
|
||||||
int32_t last_v_val;
|
/// Internal speaker output (range: 0 .. 255)
|
||||||
|
uint8_t output_speaker;
|
||||||
|
} swan_sound_t;
|
||||||
|
|
||||||
uint8_t HyperVoice;
|
#ifdef __cplusplus
|
||||||
int32_t last_hv_val[2];
|
extern "C" {
|
||||||
uint8_t HVoiceCtrl, HVoiceChanCtrl;
|
#endif
|
||||||
|
|
||||||
int32_t period_counter[4];
|
void swan_sound_init(swan_sound_t *snd, bool headphones);
|
||||||
int32_t last_val[4][2]; // Last outputted value, l&r
|
uint8_t swan_sound_in(swan_sound_t *snd, uint16_t port);
|
||||||
uint8_t sample_pos[4];
|
void swan_sound_out(swan_sound_t *snd, uint16_t port, uint8_t value);
|
||||||
uint16_t nreg;
|
void swan_sound_tick(swan_sound_t *snd);
|
||||||
uint32_t last_ts;
|
|
||||||
uint32_t v30mz_timestamp;
|
|
||||||
|
|
||||||
uint8_t wsRAM[64];
|
#ifdef __cplusplus
|
||||||
};
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
#include "furIcons.h"
|
#include "furIcons.h"
|
||||||
#include "IconsFontAwesome4.h"
|
#include "IconsFontAwesome4.h"
|
||||||
|
#include "sound/swan.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}
|
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}
|
||||||
|
@ -39,12 +40,16 @@ const char* regCheatSheetWS[]={
|
||||||
"CH4_Vol", "0B",
|
"CH4_Vol", "0B",
|
||||||
"Sweep_Value", "0C",
|
"Sweep_Value", "0C",
|
||||||
"Sweep_Time", "0D",
|
"Sweep_Time", "0D",
|
||||||
"Noise", "0E",
|
"Noise_Ctrl", "0E",
|
||||||
"Wave_Base", "0F",
|
"Wave_Base", "0F",
|
||||||
"Ctrl", "10",
|
"Channel_Ctrl", "10",
|
||||||
"Output", "11",
|
"Output_Ctrl", "11",
|
||||||
"Random", "12",
|
"Noise_Random", "12",
|
||||||
"Voice_Ctrl", "14",
|
"CH2_Voice_Vol", "14",
|
||||||
|
"Test", "15",
|
||||||
|
"Output_Right", "16",
|
||||||
|
"Output_Left", "18",
|
||||||
|
"Output_Mono", "1A",
|
||||||
"Wave_Mem", "40",
|
"Wave_Mem", "40",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -53,35 +58,15 @@ const char** DivPlatformSwan::getRegisterSheet() {
|
||||||
return regCheatSheetWS;
|
return regCheatSheetWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformSwan::acquireDirect(blip_buffer_t** bb, size_t len) {
|
void DivPlatformSwan::acquire(short** buf, size_t len) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
oscBuf[i]->begin(len);
|
oscBuf[i]->begin(len);
|
||||||
ws->oscBuf[i]=oscBuf[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws->sbuf[0]=bb[0];
|
|
||||||
ws->sbuf[1]=bb[1];
|
|
||||||
|
|
||||||
for (size_t h=0; h<len; h++) {
|
for (size_t h=0; h<len; h++) {
|
||||||
ws->v30mz_timestamp=h;
|
// PCM
|
||||||
// heuristic
|
|
||||||
int pcmAdvance=1;
|
|
||||||
if (writes.empty()) {
|
|
||||||
if (!pcm || dacSample==-1) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
pcmAdvance=len-h;
|
|
||||||
if (dacRate>0) {
|
|
||||||
int remainTime=(rate-dacPeriod+dacRate-1)/dacRate;
|
|
||||||
if (remainTime<pcmAdvance) pcmAdvance=remainTime;
|
|
||||||
if (remainTime<1) pcmAdvance=1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PCM part
|
|
||||||
if (pcm && dacSample!=-1) {
|
if (pcm && dacSample!=-1) {
|
||||||
dacPeriod+=dacRate*pcmAdvance;
|
dacPeriod+=dacRate;
|
||||||
while (dacPeriod>=rate) {
|
while (dacPeriod>=rate) {
|
||||||
DivSample* s=parent->getSample(dacSample);
|
DivSample* s=parent->getSample(dacSample);
|
||||||
if (s->samples<=0 || dacPos>=s->samples) {
|
if (s->samples<=0 || dacPos>=s->samples) {
|
||||||
|
@ -99,25 +84,32 @@ void DivPlatformSwan::acquireDirect(blip_buffer_t** bb, size_t len) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h+=pcmAdvance-1;
|
// Register writes
|
||||||
|
|
||||||
// the rest
|
|
||||||
while (!writes.empty()) {
|
while (!writes.empty()) {
|
||||||
QueuedWrite w=writes.front();
|
QueuedWrite w=writes.front();
|
||||||
regPool[w.addr]=w.val;
|
if (w.addr < 0x40) {
|
||||||
if (w.addr<0x40) {
|
swan_sound_out(&ws, w.addr|0x80, w.val);
|
||||||
ws->SoundWrite(w.addr|0x80,w.val);
|
|
||||||
} else {
|
} else {
|
||||||
ws->SoundCheckRAMWrite(w.addr&0x3f);
|
ws.wave_ram[w.addr & 0x3f] = w.val;
|
||||||
ws->RAMWrite(w.addr&0x3f,w.val);
|
regPool[w.addr]=w.val;
|
||||||
}
|
}
|
||||||
writes.pop();
|
writes.pop();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ws->v30mz_timestamp=len;
|
swan_sound_tick(&ws);
|
||||||
ws->SoundUpdate();
|
|
||||||
ws->SoundFlush(NULL,0);
|
// Update individual channel buffers
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (isMuted[i]) {
|
||||||
|
oscBuf[i]->putSample(h, 0);
|
||||||
|
} else {
|
||||||
|
oscBuf[i]->putSample(h, (ws.ch_output_left[i] + ws.ch_output_right[i]) << 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0][h] = ws.output_left;
|
||||||
|
buf[1][h] = ws.output_right;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
oscBuf[i]->end(len);
|
oscBuf[i]->end(len);
|
||||||
|
@ -564,9 +556,9 @@ DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* DivPlatformSwan::getRegisterPool() {
|
unsigned char* DivPlatformSwan::getRegisterPool() {
|
||||||
// get Random from emulator
|
for (int i = 0; i < 0x20; i++) {
|
||||||
regPool[0x12]=ws->SoundRead(0x92);
|
regPool[i] = swan_sound_in(&ws, i | 0x80);
|
||||||
regPool[0x13]=ws->SoundRead(0x93);
|
}
|
||||||
return regPool;
|
return regPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +569,7 @@ int DivPlatformSwan::getRegisterPoolSize() {
|
||||||
void DivPlatformSwan::reset() {
|
void DivPlatformSwan::reset() {
|
||||||
while (!writes.empty()) writes.pop();
|
while (!writes.empty()) writes.pop();
|
||||||
while (!postDACWrites.empty()) postDACWrites.pop();
|
while (!postDACWrites.empty()) postDACWrites.pop();
|
||||||
memset(regPool,0,128);
|
memset(regPool,0,64);
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i]=Channel();
|
chan[i]=Channel();
|
||||||
chan[i].vol=15;
|
chan[i].vol=15;
|
||||||
|
@ -590,7 +582,7 @@ void DivPlatformSwan::reset() {
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
addWrite(0xffffffff,0);
|
addWrite(0xffffffff,0);
|
||||||
}
|
}
|
||||||
ws->SoundReset();
|
swan_sound_init(&ws, true);
|
||||||
pcm=false;
|
pcm=false;
|
||||||
sweep=false;
|
sweep=false;
|
||||||
furnaceDac=false;
|
furnaceDac=false;
|
||||||
|
@ -601,18 +593,13 @@ void DivPlatformSwan::reset() {
|
||||||
dacPos=0;
|
dacPos=0;
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
sampleBank=0;
|
sampleBank=0;
|
||||||
rWrite(0x0f,0x00); // wave table at 0x0000
|
rWrite(0x11,0x0f); // enable speakers, minimum headphone volume
|
||||||
rWrite(0x11,0x09); // enable speakers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformSwan::getOutputCount() {
|
int DivPlatformSwan::getOutputCount() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivPlatformSwan::hasAcquireDirect() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DivPlatformSwan::notifyWaveChange(int wave) {
|
void DivPlatformSwan::notifyWaveChange(int wave) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
if (chan[i].wave==wave) {
|
if (chan[i].wave==wave) {
|
||||||
|
@ -639,7 +626,7 @@ void DivPlatformSwan::poke(std::vector<DivRegWrite>& wlist) {
|
||||||
void DivPlatformSwan::setFlags(const DivConfig& flags) {
|
void DivPlatformSwan::setFlags(const DivConfig& flags) {
|
||||||
chipClock=3072000;
|
chipClock=3072000;
|
||||||
CHECK_CUSTOM_CLOCK;
|
CHECK_CUSTOM_CLOCK;
|
||||||
rate=chipClock;
|
rate=chipClock/128;
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
oscBuf[i]->setRate(rate);
|
oscBuf[i]->setRate(rate);
|
||||||
}
|
}
|
||||||
|
@ -653,7 +640,7 @@ int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, const DivConf
|
||||||
isMuted[i]=false;
|
isMuted[i]=false;
|
||||||
oscBuf[i]=new DivDispatchOscBuffer;
|
oscBuf[i]=new DivDispatchOscBuffer;
|
||||||
}
|
}
|
||||||
ws=new WSwan();
|
swan_sound_init(&ws, true);
|
||||||
setFlags(flags);
|
setFlags(flags);
|
||||||
reset();
|
reset();
|
||||||
return 4;
|
return 4;
|
||||||
|
@ -663,7 +650,6 @@ void DivPlatformSwan::quit() {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
delete oscBuf[i];
|
delete oscBuf[i];
|
||||||
}
|
}
|
||||||
delete ws;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DivPlatformSwan::~DivPlatformSwan() {
|
DivPlatformSwan::~DivPlatformSwan() {
|
||||||
|
|
|
@ -53,12 +53,14 @@ class DivPlatformSwan: public DivDispatch {
|
||||||
};
|
};
|
||||||
FixedQueue<QueuedWrite,256> writes;
|
FixedQueue<QueuedWrite,256> writes;
|
||||||
FixedQueue<DivRegWrite,2048> postDACWrites;
|
FixedQueue<DivRegWrite,2048> postDACWrites;
|
||||||
WSwan* ws;
|
|
||||||
|
swan_sound_t ws;
|
||||||
|
|
||||||
void updateWave(int ch);
|
void updateWave(int ch);
|
||||||
friend void putDispatchChip(void*,int);
|
friend void putDispatchChip(void*,int);
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
public:
|
public:
|
||||||
void acquireDirect(blip_buffer_t** bb, size_t len);
|
void acquire(short** buf, size_t len);
|
||||||
int dispatch(DivCommand c);
|
int dispatch(DivCommand c);
|
||||||
void* getChanState(int chan);
|
void* getChanState(int chan);
|
||||||
DivMacroInt* getChanMacroInt(int ch);
|
DivMacroInt* getChanMacroInt(int ch);
|
||||||
|
@ -75,7 +77,6 @@ class DivPlatformSwan: public DivDispatch {
|
||||||
void notifyWaveChange(int wave);
|
void notifyWaveChange(int wave);
|
||||||
void notifyInsDeletion(void* ins);
|
void notifyInsDeletion(void* ins);
|
||||||
int getOutputCount();
|
int getOutputCount();
|
||||||
bool hasAcquireDirect();
|
|
||||||
void poke(unsigned int addr, unsigned short val);
|
void poke(unsigned int addr, unsigned short val);
|
||||||
void poke(std::vector<DivRegWrite>& wlist);
|
void poke(std::vector<DivRegWrite>& wlist);
|
||||||
const char** getRegisterSheet();
|
const char** getRegisterSheet();
|
||||||
|
|
Loading…
Reference in a new issue