mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
even better sound
This commit is contained in:
@@ -341,6 +341,7 @@ public:
|
||||
enabled = true;
|
||||
squareAlternate = 0;
|
||||
lastChannel = 0xFF;
|
||||
filteredFreqHz = 0.0;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint8_t read(uint16_t addr) const {
|
||||
@@ -366,6 +367,9 @@ public:
|
||||
for (std::size_t i = 0; i < kWaveRamSize; ++i)
|
||||
regs[kWaveOffset + i] = wave[i];
|
||||
regs[kPowerIndex] = static_cast<uint8_t>(value & 0x80U);
|
||||
squareAlternate = 0;
|
||||
lastChannel = 0xFF;
|
||||
filteredFreqHz = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -423,11 +427,14 @@ public:
|
||||
static constexpr uint8_t kInitialWave[kInitialWaveCount] = {0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16,
|
||||
0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48};
|
||||
|
||||
enum class Channel : uint8_t { Square1 = 0, Square2 = 1, Wave = 2, Noise = 3 };
|
||||
|
||||
GameboyApp* owner = nullptr;
|
||||
std::array<uint8_t, kRegisterCount> regs{};
|
||||
bool enabled = true;
|
||||
mutable uint8_t squareAlternate = 0;
|
||||
mutable uint8_t lastChannel = 0xFF;
|
||||
mutable double filteredFreqHz = 0.0;
|
||||
|
||||
static constexpr bool inRange(uint16_t addr) {
|
||||
return addr >= kBaseAddr && addr <= (kBaseAddr + static_cast<uint16_t>(kRegisterCount) - 1);
|
||||
@@ -447,17 +454,40 @@ public:
|
||||
return 131072.0 / denom;
|
||||
}
|
||||
|
||||
[[nodiscard]] static double snapNoiseFrequency(double freq) {
|
||||
static constexpr double kNoisePreferredHz[] = {650.0, 820.0, 990.0, 1200.0, 1500.0,
|
||||
1850.0, 2200.0, 2600.0, 3100.0, 3600.0};
|
||||
if (!(freq > 0.0))
|
||||
return freq;
|
||||
double bestFreq = freq;
|
||||
double bestDiff = 1.0e9;
|
||||
for (double target: kNoisePreferredHz) {
|
||||
double diff = freq - target;
|
||||
if (diff < 0.0)
|
||||
diff = -diff;
|
||||
if (diff < bestDiff) {
|
||||
bestDiff = diff;
|
||||
bestFreq = target;
|
||||
}
|
||||
}
|
||||
if (bestDiff <= 500.0)
|
||||
return bestFreq;
|
||||
return freq;
|
||||
}
|
||||
|
||||
// Mixer: compute best single-tone approximation for the buzzer.
|
||||
// Returns true if a tone is suggested, with outFreqHz set.
|
||||
public:
|
||||
bool computeEffectiveTone(uint32_t& outFreqHz, uint8_t& outLoudness) const {
|
||||
const uint8_t nr50 = reg(kVolumeAddr);
|
||||
const uint8_t master = static_cast<uint8_t>(std::max(nr50 & 0x07U, (nr50 >> 4) & 0x07U));
|
||||
if (master == 0)
|
||||
if (master == 0) {
|
||||
filteredFreqHz = 0.0;
|
||||
lastChannel = 0xFF;
|
||||
return false;
|
||||
}
|
||||
const uint8_t routing = reg(kRoutingAddr);
|
||||
|
||||
enum class Channel : uint8_t { Square1 = 0, Square2 = 1, Wave = 2, Noise = 3 };
|
||||
struct Candidate {
|
||||
double freq;
|
||||
uint8_t loud;
|
||||
@@ -465,9 +495,8 @@ public:
|
||||
Channel channel;
|
||||
};
|
||||
|
||||
Candidate candidates[4];
|
||||
std::size_t candidateCount = 0;
|
||||
|
||||
Candidate candidates[4];
|
||||
std::size_t candidateCount = 0;
|
||||
constexpr std::size_t kMaxCandidates = sizeof(candidates) / sizeof(candidates[0]);
|
||||
|
||||
auto pushCandidate = [&](double freq, uint8_t loud, int prio, Channel channel) {
|
||||
@@ -506,14 +535,20 @@ public:
|
||||
if ((reg(kPowerAddr) & 0x04U) != 0 && (reg(kCh3EnableAddr) & 0x80U) != 0) {
|
||||
const uint8_t levelSel = (reg(kCh3LevelAddr) >> 5) & 0x03U;
|
||||
const bool routed = ((routing & 0x44U) != 0);
|
||||
if (levelSel != 0 && routed) {
|
||||
uint8_t loudBase = 0;
|
||||
if (levelSel == 1)
|
||||
loudBase = 16;
|
||||
else if (levelSel == 2)
|
||||
loudBase = 8;
|
||||
else if (levelSel == 3)
|
||||
loudBase = 4;
|
||||
if (levelSel != 0 && routed && loudBase != 0) {
|
||||
const uint16_t raw =
|
||||
static_cast<uint16_t>(((reg(kCh3TriggerAddr) & 0x07U) << 8) | reg(kCh3FreqLoAddr));
|
||||
if (raw < 2048U) {
|
||||
const double denom = static_cast<double>(2048U - raw);
|
||||
const double freq = 2097152.0 / denom;
|
||||
const uint8_t loudBase = (levelSel == 1 ? 16 : levelSel == 2 ? 8 : 4);
|
||||
const uint8_t loud = static_cast<uint8_t>(loudBase * master);
|
||||
const double denom = static_cast<double>(2048U - raw);
|
||||
const double freq = 2097152.0 / denom;
|
||||
const uint8_t loud = static_cast<uint8_t>(loudBase * master);
|
||||
pushCandidate(freq, loud, 2, Channel::Wave);
|
||||
}
|
||||
}
|
||||
@@ -532,15 +567,18 @@ public:
|
||||
const int div = divLut[dividerId];
|
||||
double freq =
|
||||
4194304.0 / (static_cast<double>(div) * std::pow(2.0, static_cast<double>(shift + 1)));
|
||||
freq = std::clamp(freq, 600.0, 3200.0);
|
||||
freq = snapNoiseFrequency(std::clamp(freq, 600.0, 3600.0));
|
||||
const uint8_t loud = static_cast<uint8_t>(vol4 * master);
|
||||
pushCandidate(freq, loud, 1, Channel::Noise);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (candidateCount == 0)
|
||||
if (candidateCount == 0) {
|
||||
lastChannel = 0xFF;
|
||||
filteredFreqHz = 0.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Candidate* squareCandidates[2] = {nullptr, nullptr};
|
||||
const Candidate* bestOther = nullptr;
|
||||
@@ -590,7 +628,27 @@ public:
|
||||
if (!best)
|
||||
return false;
|
||||
|
||||
const double clamped = std::clamp(best->freq, 40.0, 5500.0);
|
||||
double selectedFreq = best->freq;
|
||||
if (!(selectedFreq > 0.0) || !std::isfinite(selectedFreq))
|
||||
return false;
|
||||
|
||||
const double prevFiltered = filteredFreqHz;
|
||||
if (!(prevFiltered > 0.0) || !std::isfinite(prevFiltered) ||
|
||||
static_cast<uint8_t>(best->channel) != lastChannel) {
|
||||
filteredFreqHz = selectedFreq;
|
||||
} else {
|
||||
double diff = selectedFreq - prevFiltered;
|
||||
if (diff < 0.0 && -diff > 1200.0)
|
||||
filteredFreqHz = selectedFreq;
|
||||
else if (diff > 1200.0)
|
||||
filteredFreqHz = selectedFreq;
|
||||
else {
|
||||
double alpha = (best->channel == Channel::Noise) ? 0.45 : 0.35;
|
||||
filteredFreqHz = prevFiltered + (diff * alpha);
|
||||
}
|
||||
}
|
||||
|
||||
const double clamped = std::clamp(filteredFreqHz, 40.0, 5500.0);
|
||||
outFreqHz = static_cast<uint32_t>(clamped + 0.5);
|
||||
outLoudness = best->loud;
|
||||
lastChannel = static_cast<uint8_t>(best->channel);
|
||||
|
||||
Reference in New Issue
Block a user