diff --git a/APU.cpp b/APU.cpp index 4ed3d23..7db2979 100644 --- a/APU.cpp +++ b/APU.cpp @@ -1,10 +1,12 @@ #include "APU.h" #include +#include "audio_viewer.h" namespace APU { - #define AUDIO_BUFFER_SIZE 2048 - const float cycles_per_sample = 4194304.0f / 11025.0f; + #define SAMPLING_FREQ 44100 + #define AUDIO_BUFFER_SIZE 8192 + const float cycles_per_sample = 4194304.0f / SAMPLING_FREQ; SDL_AudioDeviceID sdlAudioDevice; uint8_t sound_buffer[AUDIO_BUFFER_SIZE]; @@ -22,6 +24,8 @@ namespace APU #define apu_enabled (NR52&0x80) #define DAC1_enabled ((NR12&0xf8)!=0) + #define DAC2_enabled ((NR22&0xf8)!=0) + #define DAC3_enabled ((NR30&0x80)!=0) uint8_t duty_cycles[4][8] = { {1, 1, 1, 1, 1, 1, 1, 0}, @@ -54,6 +58,7 @@ namespace APU uint8_t NR22 = 0; // 0xff17 - Sound channel 2 volume & envelope uint8_t NR23 = 0; // 0xff18 - Sound channel 2 period low uint8_t NR24 = 0; // 0xff19 - Sound channel 2 period high & control + uint8_t NR30 = 0; // 0xff1a - Sound channel 3 DAC enable uint8_t NR31 = 0; // 0xff1b - Sound channel 3 length timer uint8_t NR32 = 0; // 0xff1c - Sound channel 3 output level @@ -91,10 +96,11 @@ namespace APU void init() { - SDL_AudioSpec audioSpec{11025, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE>>2, 0, 0, &audioCallback, NULL}; + SDL_AudioSpec audioSpec{SAMPLING_FREQ, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE>>2, 0, 0, &audioCallback, NULL}; sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0); resume(); //samples_time=SDL_GetTicks(); + audio_viewer::init(); } void reset() @@ -133,6 +139,26 @@ namespace APU // sweep does several things (check documentation) } + void triggerCH2() + { + CH2.enabled = true; + CH2.length_timer=NR21&0x3f; + CH2.period_divider = NR23 | ((NR24 &0x7)<<8); + // envelope timer is reset + CH2.volume = NR22>>4; + // sweep does several things (check documentation) + } + + void triggerCH3() + { + CH3.enabled = true; + CH3.length_timer=NR31; + CH3.period_divider = NR33 | ((NR34 &0x7)<<8); + // envelope timer is reset + CH3.volume = (NR32>>5)&0x3; + // sweep does several things (check documentation) + } + uint8_t readRegister(uint16_t address) { switch(address) @@ -143,9 +169,37 @@ namespace APU case 0xff13: return 0x00; break; case 0xff14: return NR14 & 0x40; break; + case 0xff16: return NR21 & 0xc0; break; + case 0xff17: return NR22; break; + case 0xff18: return 0x00; break; + case 0xff19: return NR24 & 0x40; break; + + case 0xff1a: return NR30; break; + case 0xff1b: return 0x00; break; + case 0xff1c: return NR32; break; + case 0xff1d: return 0x00; break; + case 0xff1e: return NR34 & 0x40; break; + case 0xff24: return NR50; break; case 0xff25: return NR51; break; case 0xff26: return NR52; break; + + case 0xff30: return WaveRAM[0]; break; + case 0xff31: return WaveRAM[1]; break; + case 0xff32: return WaveRAM[2]; break; + case 0xff33: return WaveRAM[3]; break; + case 0xff34: return WaveRAM[4]; break; + case 0xff35: return WaveRAM[5]; break; + case 0xff36: return WaveRAM[6]; break; + case 0xff37: return WaveRAM[7]; break; + case 0xff38: return WaveRAM[8]; break; + case 0xff39: return WaveRAM[9]; break; + case 0xff3a: return WaveRAM[10]; break; + case 0xff3b: return WaveRAM[11]; break; + case 0xff3c: return WaveRAM[12]; break; + case 0xff3d: return WaveRAM[13]; break; + case 0xff3e: return WaveRAM[14]; break; + case 0xff3f: return WaveRAM[15]; break; } return 0x00; } @@ -162,9 +216,37 @@ namespace APU case 0xff13: NR13 = value; break; case 0xff14: NR14 = value; CH1.length_enable=(value&0x40); if (value&0x80) triggerCH1(); break; + case 0xff16: NR21 = value; CH2.length_timer=NR21&0x3f; CH2.duty_cycle = NR21>>6; break; + case 0xff17: NR22 = value; break; + case 0xff18: NR23 = value; break; + case 0xff19: NR24 = value; CH2.length_enable=(value&0x40); if (value&0x80) triggerCH2(); break; + + case 0xff1a: NR30 = value; break; + case 0xff1b: NR31 = value; CH3.length_timer=NR31; break; + case 0xff1c: NR32 = value; break; + case 0xff1d: NR33 = value; break; + case 0xff1e: NR34 = value; CH3.length_enable=(value&0x40); if (value&0x80) triggerCH3(); break; + case 0xff24: NR50 = value; break; case 0xff25: NR51 = value; break; case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); break; + + case 0xff30: WaveRAM[0] = value; break; + case 0xff31: WaveRAM[1] = value; break; + case 0xff32: WaveRAM[2] = value; break; + case 0xff33: WaveRAM[3] = value; break; + case 0xff34: WaveRAM[4] = value; break; + case 0xff35: WaveRAM[5] = value; break; + case 0xff36: WaveRAM[6] = value; break; + case 0xff37: WaveRAM[7] = value; break; + case 0xff38: WaveRAM[8] = value; break; + case 0xff39: WaveRAM[9] = value; break; + case 0xff3a: WaveRAM[10] = value; break; + case 0xff3b: WaveRAM[11] = value; break; + case 0xff3c: WaveRAM[12] = value; break; + case 0xff3d: WaveRAM[13] = value; break; + case 0xff3e: WaveRAM[14] = value; break; + case 0xff3f: WaveRAM[15] = value; break; } } @@ -179,9 +261,27 @@ namespace APU if (DIVAPU_length==2) { DIVAPU_length=0; if (CH1.enabled && CH1.length_enable) { - CH1.length_timer++; - if (CH1.length_timer==0) { + if (CH1.length_timer==63) { CH1.enabled = false; + CH1.length_timer=0; + } else { + CH1.length_timer++; + } + } + if (CH2.enabled && CH2.length_enable) { + if (CH2.length_timer==63) { + CH2.enabled = false; + CH2.length_timer=0; + } else { + CH2.length_timer++; + } + } + if (CH3.enabled && CH3.length_enable) { + if (CH3.length_timer==255) { + CH3.enabled = false; + CH3.length_timer = 0; + } else { + CH3.length_timer++; } } } @@ -204,12 +304,24 @@ namespace APU } } } + if ( CH2.enabled && (NR22&0x7) ) { // If sweep pace != 0, envelope sweep is enabled + CH2.envelope_sweep_timer++; + if ( CH2.envelope_sweep_timer == (NR22&0x07) ) { // if timer == envelope sweep, increase or decrease volume + CH2.envelope_sweep_timer=0; + if (NR22&0x8) { // bit set increases, reset decreases + if (CH2.volume<0x0f) CH2.volume++; + } else { + if (CH2.volume>0) CH2.volume--; + } + } + } // Do the envelope sweep thing } } uint32_t dots = 0; + uint32_t dotsCH3 = 0; void update(uint32_t dt) { dots += dt; @@ -221,30 +333,46 @@ namespace APU CH1.duty_step++; if (CH1.duty_step==8) CH1.duty_step=0; } + CH2.period_divider++; + if (CH2.period_divider==2048) { + CH2.period_divider = NR23 | ((NR24 &0x7)<<8); + CH2.duty_step++; + if (CH2.duty_step==8) CH2.duty_step=0; + } + } + dotsCH3 += dt; + while (dotsCH3>=2) { + dotsCH3 -= 2; + CH3.period_divider++; + if (CH3.period_divider==2048) { + CH3.period_divider = NR33 | ((NR34 &0x7)<<8); + CH3.duty_step++; + if (CH3.duty_step==32) CH3.duty_step=0; + } } t_sound += dt; samples_t += dt; if (t_sound>=cycles_per_sample) { t_sound-=cycles_per_sample; - //samples_generated++; - /*if (samples_t >=z80::getClock()) { - //printf("%i\n", samples_generated); - samples_generated=0; - samples_t = 0; - }*/ - /*if (SDL_GetTicks()>=samples_time+1000) { - printf("%i\n", samples_generated); - samples_generated=0; - samples_time = SDL_GetTicks(); - }*/ - //sound_pos = (sound_pos+1) & 0x3ff; - //sound_buffer[sound_pos] = ear*128; - //if (sound_pos>=AUDIO_BUFFER_SIZE) sound_pos = last_1 = 0; - uint8_t sample = 0; - if (apu_enabled && DAC1_enabled) sample = (duty_cycles[CH1.duty_cycle][CH1.duty_step]*CH1.volume)<<2; + uint16_t sampleCH1 = 0; + if (apu_enabled && DAC1_enabled) sampleCH1 = (duty_cycles[CH1.duty_cycle][CH1.duty_step]*CH1.volume)<<2; + + uint16_t sampleCH2 = 0; + if (apu_enabled && DAC2_enabled) sampleCH2 = (duty_cycles[CH2.duty_cycle][CH2.duty_step]*CH2.volume)<<2; + + uint16_t sampleCH3 = 0; + if (apu_enabled && DAC3_enabled) { + uint8_t step = CH3.duty_step>>1; + uint8_t actual_sample = (CH3.duty_step&1 ? WaveRAM[step]&0x0f : WaveRAM[step]>>4)<<2; + sampleCH3 = CH3.volume==0 ? 0 : CH3.volume==1 ? actual_sample : CH3.volume==2 ? actual_sample>>1 : actual_sample>>2; + } + + uint8_t sample = (sampleCH1+sampleCH2+sampleCH3)&0xff; + sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = sample; + audio_viewer::addsample(sample); //if (ear) last_1 = sound_pos; } } diff --git a/audio_viewer.cpp b/audio_viewer.cpp new file mode 100644 index 0000000..e07c0ab --- /dev/null +++ b/audio_viewer.cpp @@ -0,0 +1,39 @@ +#include "audio_viewer.h" +#include + +#define NUM_SAMPLES 256 +#define MAX_VOLUME 256 +namespace audio_viewer +{ + SDL_Window *win = nullptr; + SDL_Renderer *ren = nullptr; + //SDL_Texture *tex = nullptr; + uint8_t buffer[NUM_SAMPLES]; + uint8_t pos = 0; + uint8_t screen[NUM_SAMPLES*MAX_VOLUME]; + + void init() + { + win = SDL_CreateWindow("Audio viewer", 1, 1, 256, 256, SDL_WINDOW_SHOWN); + ren = SDL_CreateRenderer(win, -1, 0); + //tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 256, 256); + for (int i=0; i + +namespace audio_viewer +{ + void init(); + void addsample(uint8_t sample); + void refresh(); +} diff --git a/gbscreen.cpp b/gbscreen.cpp index 4d54c86..9773506 100644 --- a/gbscreen.cpp +++ b/gbscreen.cpp @@ -1,6 +1,7 @@ #include "gbscreen.h" #include "sm83.h" #include "mem.h" +#include "audio_viewer.h" //#include "zx_ula.h" #include #include "ui_window.h" @@ -418,7 +419,7 @@ namespace gbscreen if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h); SDL_RenderFillRect(ren, &rect); } - + audio_viewer::refresh(); } void present() diff --git a/mem.cpp b/mem.cpp index 9d34f4e..91d8487 100644 --- a/mem.cpp +++ b/mem.cpp @@ -127,7 +127,7 @@ namespace mem } else { if (address==0xFF00) { hram[address - 0XFE00] = getKeypad(hram[address - 0XFE00]); - } else if (address>=0xFF10 && address<=0xFF26) { return APU::readRegister(address); } + } else if (address>=0xFF10 && address<=0xFF3F) { return APU::readRegister(address); } return hram[address - 0XFE00]; } } @@ -156,7 +156,7 @@ namespace mem hram[address-0xFE00] = value; sm83::processInterrupts(); return; - } else if (address>=0xFF10 && address<=0xFF26) { // APU + } else if (address>=0xFF10 && address<=0xFF3F) { // APU APU::writeRegister(address, value); return; } else if ( (address==0xFF46) ) { // OAM DMA