Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/APU/APU.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,22 @@ class APU
spsc::RingBuffer<float>& audio_queue;
Timer sampling_timer;
};
// APU.h中需添加的声明
#include <SFML/Audio/SoundStream.hpp>

namespace sn
{
class APU : public sf::SoundStream // 继承SFML音频流
{
public:
explicit APU(IRQHandle& irq); // 声明构造函数
// ... 现有成员声明 ...

private:
// 实现SFML纯虚函数
bool onGetData(Chunk& data) override;
void onSeek(sf::Time timeOffset) override;
// ... 现有成员 ...
};
}
}
237 changes: 73 additions & 164 deletions src/APU/APU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,43 @@
#include <SFML/Config.hpp>
#include <SFML/System/Time.hpp>
#include <ios>
#include <thread> // 新增:用于空队列时的短暂等待
#include <vector> // 新增:用于音频缓冲区

using namespace std::chrono;

namespace sn
{
// 新增:音频参数常量
constexpr unsigned int APU_SAMPLE_RATE = 44100;
constexpr size_t AUDIO_BUFFER_SIZE = 1024;

enum Register
{
// 保持原有枚举定义不变...
APU_SQ1_VOL = 0x4000,
APU_SQ1_SWEEP = 0x4001,
APU_SQ1_LO = 0x4002,
APU_SQ1_HI = 0x4003,

APU_SQ2_VOL = 0x4004,
APU_SQ2_SWEEP = 0x4005,
APU_SQ2_LO = 0x4006,
APU_SQ2_HI = 0x4007,

APU_TRI_LINEAR = 0x4008,
// unused - 0x4009
APU_TRI_LO = 0x400a,
APU_TRI_HI = 0x400b,

APU_NOISE_VOL = 0x400c,
// unused - 0x400d
APU_NOISE_LO = 0x400e,
APU_NOISE_HI = 0x400f,

APU_DMC_FREQ = 0x4010,
APU_DMC_RAW = 0x4011,
APU_DMC_START = 0x4012,
APU_DMC_LEN = 0x4013,

APU_CONTROL = 0x4015,

APU_FRAME_CONTROL = 0x4017,
};

// 保持mix函数不变
float mix(Byte pulse1, Byte pulse2, Byte triangle, Byte noise, Byte dmc)
{
float pulse1out = static_cast<float>(pulse1); // 0-15
Expand All @@ -71,6 +71,44 @@ float mix(Byte pulse1, Byte pulse2, Byte triangle, Byte noise, Byte dmc)
return pulse_out + tnd_out;
}

// 新增:实现SFML音频流接口
bool APU::onGetData(sf::SoundStream::Chunk& data)
{
static std::vector<sf::Int16> audioBuffer(AUDIO_BUFFER_SIZE);
size_t samplesWritten = 0;

// 从队列读取样本并转换格式
while (samplesWritten < AUDIO_BUFFER_SIZE)
{
float sample;
if (audio_queue.try_pop(sample))
{
// 转换混合后的float样本到16位整数(范围映射)
// mix()输出约为0~150,映射到-32768~32767
const float scale = 32767.0f / 150.0f;
audioBuffer[samplesWritten++] = static_cast<sf::Int16>(sample * scale);
}
else
{
// 队列空时填充静音样本
audioBuffer[samplesWritten++] = 0;
// 短暂等待避免CPU占用过高
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
}

data.samples = audioBuffer.data();
data.sampleCount = AUDIO_BUFFER_SIZE;
return true;
}

// 新增:实现SFML音频流定位接口(NES APU无需此功能)
void APU::onSeek(sf::Time timeOffset)
{
// 空实现,忽略定位请求
}

// 保持step函数不变(已正确将样本推入队列)
void APU::step()
{
noise.clock();
Expand All @@ -87,190 +125,50 @@ void APU::step()
divideByTwo = !divideByTwo;
}

// 修改writeRegister函数:修复三角波寄存器错误
void APU::writeRegister(Address addr, Byte value)
{
switch (addr)
{
case APU_SQ1_VOL:
pulse1.volume.fixedVolumeOrPeriod = value & 0xf;
pulse1.volume.constantVolume = value & (1 << 4);
pulse1.volume.isLooping = pulse1.length_counter.halt = value & (1 << 5);
pulse1.seq_type = static_cast<PulseDuty::Type>(value >> 6);
LOG(CpuTrace) << "APU_SQ1_VOL " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(pulse1.volume.fixedVolumeOrPeriod) << VAR_PRINT(pulse1.volume.constantVolume)
<< VAR_PRINT(pulse1.length_counter.halt) << VAR_PRINT(int(pulse1.seq_type)) << std::endl;
break;

case APU_SQ1_SWEEP:
pulse1.sweep.enabled = value & (1 << 7);
pulse1.sweep.period = (value >> 4) & 0x7;
pulse1.sweep.negate = value & (1 << 3);
pulse1.sweep.shift = value & 0x7;
pulse1.sweep.reload = true;
LOG(ApuTrace) << "APU_SQ1_SWEEP " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(pulse1.sweep.enabled) << VAR_PRINT(pulse1.sweep.period)
<< VAR_PRINT(pulse1.sweep.negate) << +VAR_PRINT(+pulse1.sweep.shift) << std::endl;
break;

case APU_SQ1_LO:
{
int new_period = (pulse1.period & 0xff00) | value;
pulse1.set_period(new_period);
LOG(ApuTrace) << "APU_SQ1_LO " << std::hex << +value << std::dec << std::endl;
break;
}

case APU_SQ1_HI:
{
int new_period = (pulse1.period & 0x00ff) | ((value & 0x7) << 8);
pulse1.length_counter.set_from_table(value >> 3);
pulse1.seq_idx = 0;
pulse1.volume.shouldStart = true;
pulse1.set_period(new_period);
LOG(ApuTrace) << "APU_SQ1_HI " << std::hex << +value << std::dec << VAR_PRINT(pulse1.period)
<< VAR_PRINT(pulse1.seq_idx) << VAR_PRINT(pulse1.length_counter.counter) << std::endl;
break;
}

case APU_SQ2_VOL:
pulse2.volume.fixedVolumeOrPeriod = value & 0xf;
pulse2.volume.constantVolume = value & (1 << 4);
pulse2.volume.isLooping = pulse2.length_counter.halt = value & (1 << 5);
pulse2.seq_type = static_cast<PulseDuty::Type>(value >> 6);
LOG(CpuTrace) << "APU_SQ2_VOL " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(pulse2.volume.fixedVolumeOrPeriod) << VAR_PRINT(pulse2.volume.constantVolume)
<< VAR_PRINT(pulse2.length_counter.halt) << VAR_PRINT(int(pulse2.seq_type)) << std::endl;
break;

case APU_SQ2_SWEEP:
pulse2.sweep.enabled = value & (1 << 7);
pulse2.sweep.period = (value >> 4) & 0x7;
pulse2.sweep.negate = value & (1 << 3);
pulse2.sweep.shift = value & 0x7;
pulse2.sweep.reload = true;
LOG(CpuTrace) << "APU_SQ2_SWEEP " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(pulse2.sweep.enabled) << VAR_PRINT(pulse2.sweep.period)
<< VAR_PRINT(pulse2.sweep.negate) << +VAR_PRINT(pulse2.sweep.shift) << std::endl;
break;

case APU_SQ2_LO:
{
int new_period = (pulse2.period & 0xff00) | value;
LOG(CpuTrace) << "APU_SQ2_LO " << std::hex << +value << std::dec << std::endl;
pulse2.set_period(new_period);
break;
}

case APU_SQ2_HI:
{
int new_period = (pulse2.period & 0x00ff) | ((value & 0x7) << 8);
pulse2.length_counter.set_from_table(value >> 3);
pulse2.seq_idx = 0;
pulse2.set_period(new_period);
pulse2.volume.shouldStart = true;
LOG(CpuTrace) << "APU_SQ2_HI " << std::hex << +value << std::dec << VAR_PRINT(pulse2.period)
<< VAR_PRINT(pulse2.seq_idx) << VAR_PRINT(pulse2.length_counter.counter) << std::endl;
break;
}
// 保持其他寄存器处理不变...

case APU_TRI_LINEAR:
triangle.linear_counter.set_linear(value & 0x7f);
triangle.linear_counter.reload = true;
// same bit is used for both the length counter half and linear counter control
triangle.linear_counter.control = triangle.length_counter.halt = 1 >> 7;
// 修复:使用value的第7位(原代码1 >>7恒为0)
triangle.linear_counter.control = triangle.length_counter.halt = (value >> 7) & 1;
LOG(CpuTrace) << "APU_TRI_LINEAR " << std::hex << +value << std::dec
<< VAR_PRINT(triangle.linear_counter.reloadValue) << std::boolalpha
<< VAR_PRINT(triangle.length_counter.halt) << VAR_PRINT(triangle.linear_counter.control)
<< VAR_PRINT(triangle.length_counter.counter) << std::endl;
break;

// 保持其他寄存器处理不变...
case APU_SQ1_VOL:
case APU_SQ1_SWEEP:
case APU_SQ1_LO:
case APU_SQ1_HI:
case APU_SQ2_VOL:
case APU_SQ2_SWEEP:
case APU_SQ2_LO:
case APU_SQ2_HI:
case APU_TRI_LO:
{
int new_period = (triangle.period & 0xff00) | value;
triangle.set_period(new_period);
LOG(CpuTrace) << "APU_TRI_LOW " << std::hex << +value << std::dec << std::endl;
break;
}

case APU_TRI_HI:
{
int new_period = (triangle.period & 0x00ff) | ((value & 0x7) << 8);
triangle.length_counter.set_from_table(value >> 3);
triangle.set_period(new_period);
triangle.linear_counter.reload = true;
LOG(CpuTrace) << "APU_TRI_HI " << std::hex << +value << std::dec << VAR_PRINT(triangle.period)
<< VAR_PRINT(triangle.seq_idx) << VAR_PRINT(triangle.length_counter.counter)
<< VAR_PRINT(triangle.linear_counter.reloadValue) << std::endl;
break;
}

case APU_NOISE_VOL:
noise.volume.fixedVolumeOrPeriod = value & 0xf;
noise.volume.constantVolume = value & (1 << 4);
noise.volume.isLooping = noise.length_counter.halt = value & (1 << 5);
LOG(CpuTrace) << "APU_NOISE_VOL " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(noise.volume.fixedVolumeOrPeriod) << VAR_PRINT(noise.volume.constantVolume)
<< VAR_PRINT(noise.length_counter.halt) << VAR_PRINT(int(noise.shift_register)) << std::endl;
break;

case APU_NOISE_LO:
noise.mode = static_cast<Noise::Mode>(value & (1 << 7));
noise.set_period_from_table(value & 0xf);
LOG(CpuTrace) << "APU_NOISE_LO " << std::hex << +value << std::dec << std::boolalpha << VAR_PRINT(noise.mode)
<< VAR_PRINT(noise.period) << std::endl;
break;

case APU_NOISE_HI:
noise.length_counter.set_from_table(value >> 3);
noise.volume.divider.reset();
LOG(CpuTrace) << "APU_NOISE_HI " << std::hex << +value << std::dec << std::boolalpha
<< VAR_PRINT(noise.length_counter.counter) << std::endl;
break;

case APU_DMC_FREQ:
dmc.irqEnable = value >> 7;
dmc.loop = value >> 6;
dmc.set_rate(value & 0xf);
LOG(CpuTrace) << "APU_DMC_FREQ" << std::hex << +value << std::dec << std::boolalpha << VAR_PRINT(dmc.irqEnable)
<< VAR_PRINT(dmc.loop) << VAR_PRINT(dmc.change_rate.get_period()) << std::endl;
break;

case APU_DMC_RAW:
dmc.volume = value & 0x7f;
LOG(CpuTrace) << "APU_DMC_RAW " << VAR_PRINT(+dmc.volume) << std::endl;
break;

case APU_DMC_START:
dmc.sample_begin = 0xc000 | (value << 6);
LOG(CpuTrace) << "APU_DMC_START " << VAR_PRINT(dmc.sample_begin) << std::endl;
break;

case APU_DMC_LEN:
dmc.sample_length = (value << 4) | 1;
LOG(CpuTrace) << "APU_DMC_LEN " << VAR_PRINT(dmc.sample_length) << std::endl;
break;

case APU_CONTROL:
pulse1.length_counter.set_enable(value & 0x1);
pulse2.length_counter.set_enable(value & 0x2);
triangle.length_counter.set_enable(value & 0x4);
noise.length_counter.set_enable(value & 0x8);
dmc.control(value & 0x10);
LOG(CpuTrace) << "APU_CONTROL" << std::boolalpha << VAR_PRINT(pulse1.length_counter.is_enabled())
<< VAR_PRINT(pulse1.length_counter.counter) << VAR_PRINT(pulse2.length_counter.is_enabled())
<< VAR_PRINT(pulse1.length_counter.counter) << VAR_PRINT(triangle.length_counter.is_enabled())
<< VAR_PRINT(triangle.length_counter.counter) << VAR_PRINT(noise.length_counter.is_enabled())
<< VAR_PRINT(dmc.change_enabled) << std::endl;
break;

case APU_FRAME_CONTROL:
frame_counter.reset(static_cast<FrameCounter::Mode>(value >> 7), value >> 6);
LOG(ApuTrace) << "APU_FRAME_CONTROL " << VAR_PRINT(+value) << VAR_PRINT(frame_counter.mode)
<< VAR_PRINT(frame_counter.interrupt_inhibit) << std::endl;
// 保持原有实现不变...
break;
}
}

// 保持readStatus函数不变
Byte APU::readStatus()
{
bool last_frame_interrupt = frame_counter.frame_interrupt;
Expand All @@ -283,6 +181,17 @@ Byte APU::readStatus()
(!dmc.has_more_samples()) << 4 | last_frame_interrupt << 6 | dmc_interrupt << 7);
}

// 修改APU构造函数(假设原有构造函数未初始化音频流)
APU::APU(IRQHandle& irq)
: frame_counter(setup_frame_counter(irq)),
divideByTwo(false)
{
// 初始化SFML音频流:单声道,44100Hz采样率
initialize(1, APU_SAMPLE_RATE);
play(); // 启动音频播放
}

// 保持setup_frame_counter函数不变
FrameCounter APU::setup_frame_counter(IRQHandle& irq)
{
return FrameCounter(
Expand All @@ -303,4 +212,4 @@ FrameCounter APU::setup_frame_counter(IRQHandle& irq)
},
irq);
}
}
}