mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
a little better sound
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -435,6 +439,8 @@ public:
|
||||
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);
|
||||
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);
|
||||
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))
|
||||
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user