- [NEW] audio viewer
- [FIX] Sampling rate pujat a 44100Hz, el aliasing quasi desapareix - [NEW] Canals 2 i 3 implementats - [FIX] El control de duració en els canals 1 i 2 era incorrecte
This commit is contained in:
170
APU.cpp
170
APU.cpp
@@ -1,10 +1,12 @@
|
|||||||
#include "APU.h"
|
#include "APU.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
#include "audio_viewer.h"
|
||||||
|
|
||||||
namespace APU
|
namespace APU
|
||||||
{
|
{
|
||||||
#define AUDIO_BUFFER_SIZE 2048
|
#define SAMPLING_FREQ 44100
|
||||||
const float cycles_per_sample = 4194304.0f / 11025.0f;
|
#define AUDIO_BUFFER_SIZE 8192
|
||||||
|
const float cycles_per_sample = 4194304.0f / SAMPLING_FREQ;
|
||||||
|
|
||||||
SDL_AudioDeviceID sdlAudioDevice;
|
SDL_AudioDeviceID sdlAudioDevice;
|
||||||
uint8_t sound_buffer[AUDIO_BUFFER_SIZE];
|
uint8_t sound_buffer[AUDIO_BUFFER_SIZE];
|
||||||
@@ -22,6 +24,8 @@ namespace APU
|
|||||||
|
|
||||||
#define apu_enabled (NR52&0x80)
|
#define apu_enabled (NR52&0x80)
|
||||||
#define DAC1_enabled ((NR12&0xf8)!=0)
|
#define DAC1_enabled ((NR12&0xf8)!=0)
|
||||||
|
#define DAC2_enabled ((NR22&0xf8)!=0)
|
||||||
|
#define DAC3_enabled ((NR30&0x80)!=0)
|
||||||
|
|
||||||
uint8_t duty_cycles[4][8] = {
|
uint8_t duty_cycles[4][8] = {
|
||||||
{1, 1, 1, 1, 1, 1, 1, 0},
|
{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 NR22 = 0; // 0xff17 - Sound channel 2 volume & envelope
|
||||||
uint8_t NR23 = 0; // 0xff18 - Sound channel 2 period low
|
uint8_t NR23 = 0; // 0xff18 - Sound channel 2 period low
|
||||||
uint8_t NR24 = 0; // 0xff19 - Sound channel 2 period high & control
|
uint8_t NR24 = 0; // 0xff19 - Sound channel 2 period high & control
|
||||||
|
|
||||||
uint8_t NR30 = 0; // 0xff1a - Sound channel 3 DAC enable
|
uint8_t NR30 = 0; // 0xff1a - Sound channel 3 DAC enable
|
||||||
uint8_t NR31 = 0; // 0xff1b - Sound channel 3 length timer
|
uint8_t NR31 = 0; // 0xff1b - Sound channel 3 length timer
|
||||||
uint8_t NR32 = 0; // 0xff1c - Sound channel 3 output level
|
uint8_t NR32 = 0; // 0xff1c - Sound channel 3 output level
|
||||||
@@ -91,10 +96,11 @@ namespace APU
|
|||||||
|
|
||||||
void init()
|
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);
|
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
|
||||||
resume();
|
resume();
|
||||||
//samples_time=SDL_GetTicks();
|
//samples_time=SDL_GetTicks();
|
||||||
|
audio_viewer::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
@@ -133,6 +139,26 @@ namespace APU
|
|||||||
// sweep does several things (check documentation)
|
// 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)
|
uint8_t readRegister(uint16_t address)
|
||||||
{
|
{
|
||||||
switch(address)
|
switch(address)
|
||||||
@@ -143,9 +169,37 @@ namespace APU
|
|||||||
case 0xff13: return 0x00; break;
|
case 0xff13: return 0x00; break;
|
||||||
case 0xff14: return NR14 & 0x40; 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 0xff24: return NR50; break;
|
||||||
case 0xff25: return NR51; break;
|
case 0xff25: return NR51; break;
|
||||||
case 0xff26: return NR52; 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
@@ -162,9 +216,37 @@ namespace APU
|
|||||||
case 0xff13: NR13 = value; break;
|
case 0xff13: NR13 = value; break;
|
||||||
case 0xff14: NR14 = value; CH1.length_enable=(value&0x40); if (value&0x80) triggerCH1(); 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 0xff24: NR50 = value; break;
|
||||||
case 0xff25: NR51 = value; break;
|
case 0xff25: NR51 = value; break;
|
||||||
case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); 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) {
|
if (DIVAPU_length==2) {
|
||||||
DIVAPU_length=0;
|
DIVAPU_length=0;
|
||||||
if (CH1.enabled && CH1.length_enable) {
|
if (CH1.enabled && CH1.length_enable) {
|
||||||
CH1.length_timer++;
|
if (CH1.length_timer==63) {
|
||||||
if (CH1.length_timer==0) {
|
|
||||||
CH1.enabled = false;
|
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
|
// Do the envelope sweep thing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t dots = 0;
|
uint32_t dots = 0;
|
||||||
|
uint32_t dotsCH3 = 0;
|
||||||
void update(uint32_t dt)
|
void update(uint32_t dt)
|
||||||
{
|
{
|
||||||
dots += dt;
|
dots += dt;
|
||||||
@@ -221,30 +333,46 @@ namespace APU
|
|||||||
CH1.duty_step++;
|
CH1.duty_step++;
|
||||||
if (CH1.duty_step==8) CH1.duty_step=0;
|
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;
|
t_sound += dt;
|
||||||
samples_t += dt;
|
samples_t += dt;
|
||||||
if (t_sound>=cycles_per_sample) {
|
if (t_sound>=cycles_per_sample) {
|
||||||
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;
|
uint16_t sampleCH1 = 0;
|
||||||
uint8_t sample = 0;
|
if (apu_enabled && DAC1_enabled) sampleCH1 = (duty_cycles[CH1.duty_cycle][CH1.duty_step]*CH1.volume)<<2;
|
||||||
if (apu_enabled && DAC1_enabled) sample = (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;
|
sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = sample;
|
||||||
|
audio_viewer::addsample(sample);
|
||||||
//if (ear) last_1 = sound_pos;
|
//if (ear) last_1 = sound_pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
audio_viewer.cpp
Normal file
39
audio_viewer.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include "audio_viewer.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#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<NUM_SAMPLES; ++i) buffer[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addsample(uint8_t sample)
|
||||||
|
{
|
||||||
|
buffer[pos] = sample;
|
||||||
|
pos = (pos+1)%NUM_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh()
|
||||||
|
{
|
||||||
|
SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
|
||||||
|
SDL_RenderClear(ren);
|
||||||
|
SDL_SetRenderDrawColor(ren, 255, 255, 255, 255);
|
||||||
|
for (int i=0; i<NUM_SAMPLES;++i) {
|
||||||
|
SDL_RenderDrawLine(ren, i, buffer[(pos+i)%NUM_SAMPLES], i+1, buffer[(pos+i+1)%NUM_SAMPLES]);
|
||||||
|
}
|
||||||
|
SDL_RenderPresent(ren);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
audio_viewer.h
Normal file
9
audio_viewer.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace audio_viewer
|
||||||
|
{
|
||||||
|
void init();
|
||||||
|
void addsample(uint8_t sample);
|
||||||
|
void refresh();
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "gbscreen.h"
|
#include "gbscreen.h"
|
||||||
#include "sm83.h"
|
#include "sm83.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "audio_viewer.h"
|
||||||
//#include "zx_ula.h"
|
//#include "zx_ula.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include "ui_window.h"
|
#include "ui_window.h"
|
||||||
@@ -418,7 +419,7 @@ namespace gbscreen
|
|||||||
if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
|
if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
|
||||||
SDL_RenderFillRect(ren, &rect);
|
SDL_RenderFillRect(ren, &rect);
|
||||||
}
|
}
|
||||||
|
audio_viewer::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void present()
|
void present()
|
||||||
|
|||||||
4
mem.cpp
4
mem.cpp
@@ -127,7 +127,7 @@ namespace mem
|
|||||||
} else {
|
} else {
|
||||||
if (address==0xFF00) {
|
if (address==0xFF00) {
|
||||||
hram[address - 0XFE00] = getKeypad(hram[address - 0XFE00]);
|
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];
|
return hram[address - 0XFE00];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ namespace mem
|
|||||||
hram[address-0xFE00] = value;
|
hram[address-0xFE00] = value;
|
||||||
sm83::processInterrupts();
|
sm83::processInterrupts();
|
||||||
return;
|
return;
|
||||||
} else if (address>=0xFF10 && address<=0xFF26) { // APU
|
} else if (address>=0xFF10 && address<=0xFF3F) { // APU
|
||||||
APU::writeRegister(address, value);
|
APU::writeRegister(address, value);
|
||||||
return;
|
return;
|
||||||
} else if ( (address==0xFF46) ) { // OAM DMA
|
} else if ( (address==0xFF46) ) { // OAM DMA
|
||||||
|
|||||||
Reference in New Issue
Block a user