a little better sound

This commit is contained in:
2025-10-13 00:39:26 +02:00
parent df7c4ff3b9
commit e37f8e3dc8

View File

@@ -342,6 +342,8 @@ public:
squareAlternate = 0;
lastChannel = 0xFF;
filteredFreqHz = 0.0;
lastSquareRaw = {0, 0};
squareStable = {0, 0};
}
[[nodiscard]] uint8_t read(uint16_t addr) const {
@@ -370,6 +372,8 @@ public:
squareAlternate = 0;
lastChannel = 0xFF;
filteredFreqHz = 0.0;
lastSquareRaw = {0, 0};
squareStable = {0, 0};
}
return;
}
@@ -431,10 +435,12 @@ public:
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;
bool enabled = true;
mutable uint8_t squareAlternate = 0;
mutable uint8_t lastChannel = 0xFF;
mutable double filteredFreqHz = 0.0;
mutable std::array<uint16_t, 2> lastSquareRaw{};
mutable std::array<uint8_t, 2> squareStable{};
static constexpr bool inRange(uint16_t addr) {
return addr >= kBaseAddr && addr <= (kBaseAddr + static_cast<uint16_t>(kRegisterCount) - 1);
@@ -442,10 +448,12 @@ public:
[[nodiscard]] uint8_t reg(uint16_t addr) const { return regs[static_cast<std::size_t>(addr - kBaseAddr)]; }
[[nodiscard]] double squareFrequency(int channelIndex) const {
[[nodiscard]] double squareFrequency(int channelIndex, uint16_t* outRaw = nullptr) const {
const uint16_t freqLoAddr = (channelIndex == 0) ? kCh1FreqLoAddr : kCh2FreqLoAddr;
const uint16_t freqHiAddr = (channelIndex == 0) ? kCh1TriggerAddr : kCh2TriggerAddr;
const uint16_t raw = static_cast<uint16_t>(((reg(freqHiAddr) & 0x07U) << 8) | reg(freqLoAddr));
if (outRaw)
*outRaw = raw;
if (raw >= 2048U)
return 0.0;
const double denom = static_cast<double>(2048U - raw);
@@ -499,6 +507,25 @@ public:
std::size_t candidateCount = 0;
constexpr std::size_t kMaxCandidates = sizeof(candidates) / sizeof(candidates[0]);
// Track how stable each square channel's raw frequency is so we can bias selection.
auto updateSquareHistory = [&](int idx, uint16_t raw) {
if (raw == 0 || raw >= 2048U) {
squareStable[static_cast<std::size_t>(idx)] = 0;
lastSquareRaw[static_cast<std::size_t>(idx)] = 0;
return;
}
if (lastSquareRaw[static_cast<std::size_t>(idx)] == raw) {
const uint8_t current = squareStable[static_cast<std::size_t>(idx)];
if (current < 0xFD)
squareStable[static_cast<std::size_t>(idx)] = static_cast<uint8_t>(current + 1);
} else {
lastSquareRaw[static_cast<std::size_t>(idx)] = raw;
squareStable[static_cast<std::size_t>(idx)] = 0;
}
};
bool squareActive[2] = {false, false};
auto pushCandidate = [&](double freq, uint8_t loud, int prio, Channel channel) {
if (candidateCount >= kMaxCandidates)
return;
@@ -513,9 +540,17 @@ public:
const uint8_t vol4 = (env >> 4) & 0x0FU;
const bool routed = ((routing & 0x11U) != 0);
if (vol4 && routed) {
const double freq = squareFrequency(0);
uint16_t raw = 0;
const double freq = squareFrequency(0, &raw);
const uint8_t loud = static_cast<uint8_t>(vol4 * master);
pushCandidate(freq, loud, 3, Channel::Square1);
if (freq > 0.0) {
squareActive[0] = true;
updateSquareHistory(0, raw);
pushCandidate(freq, loud, 3, Channel::Square1);
} else {
squareStable[0] = 0;
lastSquareRaw[0] = 0;
}
}
}
#endif
@@ -525,9 +560,17 @@ public:
const uint8_t vol4 = (env >> 4) & 0x0FU;
const bool routed = ((routing & 0x22U) != 0);
if (vol4 && routed) {
const double freq = squareFrequency(1);
uint16_t raw = 0;
const double freq = squareFrequency(1, &raw);
const uint8_t loud = static_cast<uint8_t>(vol4 * master);
pushCandidate(freq, loud, 3, Channel::Square2);
if (freq > 0.0) {
squareActive[1] = true;
updateSquareHistory(1, raw);
pushCandidate(freq, loud, 3, Channel::Square2);
} else {
squareStable[1] = 0;
lastSquareRaw[1] = 0;
}
}
}
#endif
@@ -580,8 +623,18 @@ public:
return false;
}
for (int idx = 0; idx < 2; ++idx) {
if (!squareActive[idx]) {
squareStable[static_cast<std::size_t>(idx)] = 0;
lastSquareRaw[static_cast<std::size_t>(idx)] = 0;
}
}
const Candidate* squareCandidates[2] = {nullptr, nullptr};
const Candidate* waveCandidate = nullptr;
bool waveBass = false;
const Candidate* bestOther = nullptr;
int bestOtherScore = -1;
for (std::size_t i = 0; i < candidateCount; ++i) {
const Candidate* cand = &candidates[i];
@@ -589,11 +642,27 @@ public:
squareCandidates[0] = cand;
else if (cand->channel == Channel::Square2)
squareCandidates[1] = cand;
else if (!bestOther || cand->loud > bestOther->loud ||
(cand->loud == bestOther->loud && cand->prio > bestOther->prio) ||
(cand->loud == bestOther->loud && cand->prio == bestOther->prio &&
cand->freq > bestOther->freq))
bestOther = cand;
else {
if (cand->channel == Channel::Wave) {
waveCandidate = cand;
waveBass = (cand->freq > 0.0 && cand->freq < 220.0);
}
int score = static_cast<int>(cand->loud);
if (waveBass) {
if (cand->channel == Channel::Wave)
score += 3;
else if (cand->channel == Channel::Noise)
score -= 1;
}
if (score < 0)
score = 0;
if (!bestOther || score > bestOtherScore ||
(score == bestOtherScore && cand->prio > bestOther->prio) ||
(score == bestOtherScore && cand->prio == bestOther->prio && cand->freq > bestOther->freq)) {
bestOther = cand;
bestOtherScore = score;
}
}
}
const Candidate* bestSquare = nullptr;
@@ -602,13 +671,31 @@ public:
static_cast<int>(squareCandidates[0]->loud) - static_cast<int>(squareCandidates[1]->loud);
if (loudDiff < 0)
loudDiff = -loudDiff;
if (loudDiff <= 2) {
const Candidate* preferred = (squareAlternate & 1U) ? squareCandidates[1] : squareCandidates[0];
bestSquare = preferred;
squareAlternate ^= 1U;
} else {
const int stable0 = static_cast<int>(squareStable[0]);
const int stable1 = static_cast<int>(squareStable[1]);
const int stableMargin = waveBass ? 0 : 2;
if (stable0 > stable1 + stableMargin)
bestSquare = squareCandidates[0];
else if (stable1 > stable0 + stableMargin)
bestSquare = squareCandidates[1];
else if (loudDiff > 2) {
bestSquare = (squareCandidates[0]->loud > squareCandidates[1]->loud) ? squareCandidates[0]
: squareCandidates[1];
} else {
if (waveBass && stable0 != stable1)
bestSquare = (stable0 >= stable1) ? squareCandidates[0] : squareCandidates[1];
if (!bestSquare && stable0 <= 1 && stable1 <= 1) {
if (lastChannel == static_cast<uint8_t>(Channel::Square1))
bestSquare = squareCandidates[0];
else if (lastChannel == static_cast<uint8_t>(Channel::Square2))
bestSquare = squareCandidates[1];
}
if (!bestSquare) {
const Candidate* preferred =
(squareAlternate & 1U) ? squareCandidates[1] : squareCandidates[0];
bestSquare = preferred;
squareAlternate ^= 1U;
}
}
} else if (squareCandidates[0] || squareCandidates[1]) {
bestSquare = squareCandidates[0] ? squareCandidates[0] : squareCandidates[1];
@@ -618,13 +705,48 @@ public:
if (!best)
best = bestOther;
else if (bestOther) {
if (bestOther->loud > best->loud || (bestOther->loud == best->loud && bestOther->prio > best->prio) ||
(bestOther->loud == best->loud && bestOther->prio == best->prio &&
int bestScore = static_cast<int>(best->loud);
int otherScore = static_cast<int>(bestOther->loud);
if (waveBass) {
if (best->channel == Channel::Wave)
bestScore += 3;
else if (best->channel == Channel::Noise)
bestScore -= 1;
else if (best->channel == Channel::Square1 || best->channel == Channel::Square2)
bestScore -= 2;
if (bestOther->channel == Channel::Wave)
otherScore += 3;
else if (bestOther->channel == Channel::Noise)
otherScore -= 1;
else if (bestOther->channel == Channel::Square1 || bestOther->channel == Channel::Square2)
otherScore -= 2;
}
if (bestScore < 0)
bestScore = 0;
if (otherScore < 0)
otherScore = 0;
if (otherScore > bestScore || (otherScore == bestScore && bestOther->prio > best->prio) ||
(otherScore == bestScore && bestOther->prio == best->prio &&
static_cast<uint8_t>(bestOther->channel) == lastChannel &&
static_cast<uint8_t>(best->channel) != lastChannel))
best = bestOther;
}
if (waveBass && waveCandidate && best && best->channel != Channel::Wave) {
int waveScore = static_cast<int>(waveCandidate->loud) + 3;
int bestScore = static_cast<int>(best->loud);
if (best->channel == Channel::Noise)
bestScore -= 1;
else if (best->channel == Channel::Square1 || best->channel == Channel::Square2)
bestScore -= 2;
if (waveScore < 0)
waveScore = 0;
if (bestScore < 0)
bestScore = 0;
if (waveScore >= bestScore)
best = waveCandidate;
}
if (!best)
return false;