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;
|
squareAlternate = 0;
|
||||||
lastChannel = 0xFF;
|
lastChannel = 0xFF;
|
||||||
filteredFreqHz = 0.0;
|
filteredFreqHz = 0.0;
|
||||||
|
lastSquareRaw = {0, 0};
|
||||||
|
squareStable = {0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] uint8_t read(uint16_t addr) const {
|
[[nodiscard]] uint8_t read(uint16_t addr) const {
|
||||||
@@ -370,6 +372,8 @@ public:
|
|||||||
squareAlternate = 0;
|
squareAlternate = 0;
|
||||||
lastChannel = 0xFF;
|
lastChannel = 0xFF;
|
||||||
filteredFreqHz = 0.0;
|
filteredFreqHz = 0.0;
|
||||||
|
lastSquareRaw = {0, 0};
|
||||||
|
squareStable = {0, 0};
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -431,10 +435,12 @@ public:
|
|||||||
|
|
||||||
GameboyApp* owner = nullptr;
|
GameboyApp* owner = nullptr;
|
||||||
std::array<uint8_t, kRegisterCount> regs{};
|
std::array<uint8_t, kRegisterCount> regs{};
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
mutable uint8_t squareAlternate = 0;
|
mutable uint8_t squareAlternate = 0;
|
||||||
mutable uint8_t lastChannel = 0xFF;
|
mutable uint8_t lastChannel = 0xFF;
|
||||||
mutable double filteredFreqHz = 0.0;
|
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) {
|
static constexpr bool inRange(uint16_t addr) {
|
||||||
return addr >= kBaseAddr && addr <= (kBaseAddr + static_cast<uint16_t>(kRegisterCount) - 1);
|
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]] 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 freqLoAddr = (channelIndex == 0) ? kCh1FreqLoAddr : kCh2FreqLoAddr;
|
||||||
const uint16_t freqHiAddr = (channelIndex == 0) ? kCh1TriggerAddr : kCh2TriggerAddr;
|
const uint16_t freqHiAddr = (channelIndex == 0) ? kCh1TriggerAddr : kCh2TriggerAddr;
|
||||||
const uint16_t raw = static_cast<uint16_t>(((reg(freqHiAddr) & 0x07U) << 8) | reg(freqLoAddr));
|
const uint16_t raw = static_cast<uint16_t>(((reg(freqHiAddr) & 0x07U) << 8) | reg(freqLoAddr));
|
||||||
|
if (outRaw)
|
||||||
|
*outRaw = raw;
|
||||||
if (raw >= 2048U)
|
if (raw >= 2048U)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
const double denom = static_cast<double>(2048U - raw);
|
const double denom = static_cast<double>(2048U - raw);
|
||||||
@@ -499,6 +507,25 @@ public:
|
|||||||
std::size_t candidateCount = 0;
|
std::size_t candidateCount = 0;
|
||||||
constexpr std::size_t kMaxCandidates = sizeof(candidates) / sizeof(candidates[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) {
|
auto pushCandidate = [&](double freq, uint8_t loud, int prio, Channel channel) {
|
||||||
if (candidateCount >= kMaxCandidates)
|
if (candidateCount >= kMaxCandidates)
|
||||||
return;
|
return;
|
||||||
@@ -513,9 +540,17 @@ public:
|
|||||||
const uint8_t vol4 = (env >> 4) & 0x0FU;
|
const uint8_t vol4 = (env >> 4) & 0x0FU;
|
||||||
const bool routed = ((routing & 0x11U) != 0);
|
const bool routed = ((routing & 0x11U) != 0);
|
||||||
if (vol4 && routed) {
|
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);
|
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
|
#endif
|
||||||
@@ -525,9 +560,17 @@ public:
|
|||||||
const uint8_t vol4 = (env >> 4) & 0x0FU;
|
const uint8_t vol4 = (env >> 4) & 0x0FU;
|
||||||
const bool routed = ((routing & 0x22U) != 0);
|
const bool routed = ((routing & 0x22U) != 0);
|
||||||
if (vol4 && routed) {
|
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);
|
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
|
#endif
|
||||||
@@ -580,8 +623,18 @@ public:
|
|||||||
return false;
|
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* squareCandidates[2] = {nullptr, nullptr};
|
||||||
|
const Candidate* waveCandidate = nullptr;
|
||||||
|
bool waveBass = false;
|
||||||
const Candidate* bestOther = nullptr;
|
const Candidate* bestOther = nullptr;
|
||||||
|
int bestOtherScore = -1;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < candidateCount; ++i) {
|
for (std::size_t i = 0; i < candidateCount; ++i) {
|
||||||
const Candidate* cand = &candidates[i];
|
const Candidate* cand = &candidates[i];
|
||||||
@@ -589,11 +642,27 @@ public:
|
|||||||
squareCandidates[0] = cand;
|
squareCandidates[0] = cand;
|
||||||
else if (cand->channel == Channel::Square2)
|
else if (cand->channel == Channel::Square2)
|
||||||
squareCandidates[1] = cand;
|
squareCandidates[1] = cand;
|
||||||
else if (!bestOther || cand->loud > bestOther->loud ||
|
else {
|
||||||
(cand->loud == bestOther->loud && cand->prio > bestOther->prio) ||
|
if (cand->channel == Channel::Wave) {
|
||||||
(cand->loud == bestOther->loud && cand->prio == bestOther->prio &&
|
waveCandidate = cand;
|
||||||
cand->freq > bestOther->freq))
|
waveBass = (cand->freq > 0.0 && cand->freq < 220.0);
|
||||||
bestOther = cand;
|
}
|
||||||
|
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;
|
const Candidate* bestSquare = nullptr;
|
||||||
@@ -602,13 +671,31 @@ public:
|
|||||||
static_cast<int>(squareCandidates[0]->loud) - static_cast<int>(squareCandidates[1]->loud);
|
static_cast<int>(squareCandidates[0]->loud) - static_cast<int>(squareCandidates[1]->loud);
|
||||||
if (loudDiff < 0)
|
if (loudDiff < 0)
|
||||||
loudDiff = -loudDiff;
|
loudDiff = -loudDiff;
|
||||||
if (loudDiff <= 2) {
|
const int stable0 = static_cast<int>(squareStable[0]);
|
||||||
const Candidate* preferred = (squareAlternate & 1U) ? squareCandidates[1] : squareCandidates[0];
|
const int stable1 = static_cast<int>(squareStable[1]);
|
||||||
bestSquare = preferred;
|
const int stableMargin = waveBass ? 0 : 2;
|
||||||
squareAlternate ^= 1U;
|
if (stable0 > stable1 + stableMargin)
|
||||||
} else {
|
bestSquare = squareCandidates[0];
|
||||||
|
else if (stable1 > stable0 + stableMargin)
|
||||||
|
bestSquare = squareCandidates[1];
|
||||||
|
else if (loudDiff > 2) {
|
||||||
bestSquare = (squareCandidates[0]->loud > squareCandidates[1]->loud) ? squareCandidates[0]
|
bestSquare = (squareCandidates[0]->loud > squareCandidates[1]->loud) ? squareCandidates[0]
|
||||||
: squareCandidates[1];
|
: 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]) {
|
} else if (squareCandidates[0] || squareCandidates[1]) {
|
||||||
bestSquare = squareCandidates[0] ? squareCandidates[0] : squareCandidates[1];
|
bestSquare = squareCandidates[0] ? squareCandidates[0] : squareCandidates[1];
|
||||||
@@ -618,13 +705,48 @@ public:
|
|||||||
if (!best)
|
if (!best)
|
||||||
best = bestOther;
|
best = bestOther;
|
||||||
else if (bestOther) {
|
else if (bestOther) {
|
||||||
if (bestOther->loud > best->loud || (bestOther->loud == best->loud && bestOther->prio > best->prio) ||
|
int bestScore = static_cast<int>(best->loud);
|
||||||
(bestOther->loud == best->loud && bestOther->prio == best->prio &&
|
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>(bestOther->channel) == lastChannel &&
|
||||||
static_cast<uint8_t>(best->channel) != lastChannel))
|
static_cast<uint8_t>(best->channel) != lastChannel))
|
||||||
best = bestOther;
|
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)
|
if (!best)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user