feat: audio stack performance improvements
This commit is contained in:
@@ -1074,6 +1074,22 @@ namespace Drivers::Audio::IntelHda {
|
||||
KernelLogStream(OK, "HDA") << "Stream closed";
|
||||
}
|
||||
|
||||
uint32_t GetWriteSpace(int handle) {
|
||||
if (handle != 0 || !g_stream.Active) return 0;
|
||||
|
||||
uint32_t hwPos = g_dmaPos[g_stream.StreamIndex * 2];
|
||||
uint32_t writePos = g_stream.WritePos;
|
||||
uint32_t available;
|
||||
if (writePos >= hwPos) {
|
||||
available = TOTAL_BUFFER_SIZE - (writePos - hwPos);
|
||||
} else {
|
||||
available = hwPos - writePos;
|
||||
}
|
||||
if (available > 64) available -= 64;
|
||||
else available = 0;
|
||||
return available;
|
||||
}
|
||||
|
||||
int Write(int handle, const uint8_t* data, uint32_t size) {
|
||||
if (handle != 0 || !g_stream.Active || !data || size == 0)
|
||||
return -1;
|
||||
|
||||
@@ -274,6 +274,13 @@ namespace Drivers::Audio::IntelHda {
|
||||
// Returns number of bytes written (may be less than requested if buffer full).
|
||||
int Write(int handle, const uint8_t* data, uint32_t size);
|
||||
|
||||
// Free bytes in the DMA ring that a caller may write right now.
|
||||
// Returns 0 if the stream is not active. The mixer uses this to size
|
||||
// each pump so it never produces more than HDA can accept (otherwise
|
||||
// surplus mixed data would be silently dropped and the stream readers
|
||||
// would still advance, scrambling playback).
|
||||
uint32_t GetWriteSpace(int handle);
|
||||
|
||||
// Control commands
|
||||
constexpr int AUDIO_CTL_SET_VOLUME = 0; // value: 0-100
|
||||
constexpr int AUDIO_CTL_GET_VOLUME = 1;
|
||||
|
||||
@@ -164,9 +164,16 @@ namespace Drivers::Audio::Mixer {
|
||||
static void Pump() {
|
||||
if (!g_hdaOpened) return;
|
||||
|
||||
// Ask IntelHda how much room there is. The driver clamps writes to free
|
||||
// space, so we feed it a generous buffer and let it consume what it can.
|
||||
uint32_t frames = MAX_PUMP_FRAMES;
|
||||
// Produce only as many frames as the HDA DMA ring has room for right
|
||||
// now. Producing more would mean the surplus is silently dropped by
|
||||
// IntelHda::Write while the per-stream resamplers had already
|
||||
// advanced — that's what scrambles speech into a sequence of
|
||||
// unrelated chunks. Clamp to MAX_PUMP_FRAMES so the scratch buffers
|
||||
// are bounded.
|
||||
uint32_t freeBytes = IntelHda::GetWriteSpace(g_hdaHandle);
|
||||
uint32_t frames = freeBytes / 4;
|
||||
if (frames == 0) return;
|
||||
if (frames > MAX_PUMP_FRAMES) frames = MAX_PUMP_FRAMES;
|
||||
|
||||
// Always write to HDA, even when no streams are active, so the
|
||||
// hardware ring stays filled with silence instead of looping the
|
||||
@@ -448,15 +455,26 @@ namespace Drivers::Audio::Mixer {
|
||||
void SetMasterVolume(int percent) {
|
||||
if (percent < 0) percent = 0;
|
||||
if (percent > 100) percent = 100;
|
||||
|
||||
// Take the mixer lock just long enough to update software state and
|
||||
// capture a snapshot for the HW write. The IntelHda codec command
|
||||
// path busy-waits on the RIRB for a few hundred microseconds, so
|
||||
// doing it inside the lock would freeze interrupts (BCIS, scheduler,
|
||||
// input) for the whole duration of every drag tick — visible as
|
||||
// slider lag.
|
||||
g_lock.Acquire();
|
||||
bool changed = (g_masterVolume != percent);
|
||||
g_masterVolume = percent;
|
||||
if (g_hdaOpened) {
|
||||
IntelHda::Control(g_hdaHandle, IntelHda::AUDIO_CTL_SET_VOLUME,
|
||||
g_masterMute ? 0 : g_masterVolume);
|
||||
}
|
||||
int curVol = g_masterVolume;
|
||||
bool curMute = g_masterMute;
|
||||
bool hdaOpen = g_hdaOpened;
|
||||
if (changed) BumpSerialLocked();
|
||||
g_lock.Release();
|
||||
|
||||
if (changed && hdaOpen) {
|
||||
IntelHda::Control(g_hdaHandle, IntelHda::AUDIO_CTL_SET_VOLUME,
|
||||
curMute ? 0 : curVol);
|
||||
}
|
||||
if (changed) Sched::WakeObjectWaiters((void*)&g_serial);
|
||||
}
|
||||
|
||||
@@ -468,12 +486,16 @@ namespace Drivers::Audio::Mixer {
|
||||
g_lock.Acquire();
|
||||
bool changed = (g_masterMute != muted);
|
||||
g_masterMute = muted;
|
||||
if (g_hdaOpened) {
|
||||
IntelHda::Control(g_hdaHandle, IntelHda::AUDIO_CTL_SET_VOLUME,
|
||||
g_masterMute ? 0 : g_masterVolume);
|
||||
}
|
||||
int curVol = g_masterVolume;
|
||||
bool curMute = g_masterMute;
|
||||
bool hdaOpen = g_hdaOpened;
|
||||
if (changed) BumpSerialLocked();
|
||||
g_lock.Release();
|
||||
|
||||
if (changed && hdaOpen) {
|
||||
IntelHda::Control(g_hdaHandle, IntelHda::AUDIO_CTL_SET_VOLUME,
|
||||
curMute ? 0 : curVol);
|
||||
}
|
||||
if (changed) Sched::WakeObjectWaiters((void*)&g_serial);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user