Repository navigation
audio: play RAD1 and O2 audio at the guest's rate, plus an audio test suite - #356
Open
jamesbraid wants to merge 4 commits into
Open
jamesbraid wants to merge 4 commits into
jamesbraid wants to merge 4 commits into
Conversation
The card ran its analog input and output at a fixed 44.1 kHz and stored the clock generator words without reading them. IRIX sets the output rate through those words for every program that plays sound, and keeps the last rate when the program exits. A 48 kHz sound, the rate IRIX boots with, played 9% slow and flat; an 11025 or 22050 Hz game played wrong, and so did every sound after it until something set 44.1 kHz again. That is techomancer#354. The decode follows the public Linux RAD1 driver: 0xa0 selects generators from bit 28 and holds the divisor less one, 0xa4 holds the remainder and its denominator, and each generator's word in 0xa8..0xb4 picks the 176.4 or 192 kHz master. Generator 0 clocks the analog input and 1 the output. A rate change on a running channel takes effect from the write. The WAV capture stays 44.1 kHz, converted from the card's rate, and the host stream's resampler follows the rate. The tests hold the words IRIX 6.5.30 writes for 8 to 48 kHz. A 2 s 1 kHz tone at each of 7 rates, mono and stereo, 8 and 16 bit, now plays for 2.0 s at 1000 Hz through playaiff and sfplay; before, only 44.1 kHz did.
The rate fix was checked by hand with a handful of files. Add the suite the GL tests have: test/audiotest plays a 1 kHz, 2.0 s tone in the guest at 7 rates, mono and stereo, 8 and 16 bit, through the audio library (audiotest, built with MIPSpro) and through playaiff and sfplay on AIFFs that audiotest writes itself, so no audio file is committed or made on the host. Two sticky-rate cases play 22050 Hz and then 44100 Hz. host.sh drives a booted Octane or O2 and notes the capture's size around each case; check.py slices IRIS_OCTANE_AUDIO_WAV, or the O2's guest-rate IRIS_O2_AUDIO_DUMP, and passes a case when its length is within 5% of 2.0 s and its pitch within 3% of 1000 Hz. Measured on IRIX 6.5.30: on the Octane 86 of 86 cases pass with the rate fix, and 72 fail on upstream b188bb9 (everything but 44.1 kHz). On the O2, 62 of 86 pass; the 24 at 8000 and 11025 Hz play at the right rate but with extra samples in the tone (pitch 1036 to 1166 Hz). The Indy and Indigo2 are not covered: HAL2 has no guest-rate capture yet.
O2 sound at 8000 and 11025 Hz played sharp and about 4% long through the audio library, playaiff and sfplay alike. The guest-rate dump showed the right rate but zeros inserted mid-tone, one in about 33 at 8 kHz. IRIX feeds an output ring by reading MACE's status register about once a millisecond and setting the write pointer about 2 ms past the read pointer it finds: 17 frames at 8 kHz, 16 after the block rounding. The engine only moved the read pointer on the service thread's passes, and since techomancer#343 those come about every 2.1 ms on a quiet machine. IRIX then computed its lead from a pointer up to 2 ms stale, a pass wanted 16 or 17 frames where 16 or fewer were queued, and each missing frame played as a zero. At 16 kHz and up the lead rounds to enough spare frames to cover the pass. A read of the audio registers now moves the rings up to now first, as the ISA block's reads already do for the serial DMA. What the outputs play there is kept until the service thread's pass, which dumps and plays it in order, so a capture write that stalls that thread (up to a second on a network filesystem) no longer holds the rings back. The O2 audio suite now passes 86 of 86 cases, 8 kHz at 2.000 s and 1000 Hz.
host_input_arrives_in_the_receive_ring_after_the_timer failed once with "two held in the packer". The test rig never starts the chip, so no receive thread or timer is involved: service() starts the 32 ms receive timer from the wall clock, and every register access first expires any deadline already past. A test thread descheduled for over 32 ms between service() and the RX_PRODUCE read made that read post the entry itself, so the packer was already empty when the test looked. A 33 ms pause at that point fails the test every time. The model is right here. The guest sees the same thing on a late read, and the partial entry still reaches the ring. The test assumed it would run inside the timer's window. The test now checks that the deadline is 32 ms after the character, holds it off while it reads the held count and the arm, then restores it and sleeps until it passes. That also replaces the fixed 60 ms sleep.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #354.
The card ran its analog input and output at a fixed 44.1 kHz and stored the clock generator words without reading them. IRIX sets the output rate through those words for every program that plays sound, and keeps the last rate when the program exits. A 48 kHz sound, the rate IRIX boots with, played 9% slow and flat. An 11025 or 22050 Hz game played wrong, and so did every sound after it until something set 44.1 kHz again.
The decode follows the public Linux RAD1 driver:
Generator 0 clocks the analog input and 1 the output. A rate change on a running channel takes effect from the write. The WAV capture stays 44.1 kHz, converted from the card's rate, and the host stream's resampler follows the rate. The power-on rate before IRIX programs a generator is unknown, so the card keeps 44.1 kHz until then.
sgiquake and sgidoom both set 11025 Hz. Before this change the card read their ring four times too fast and replayed stale samples: Doom's 0.51 s pistol shot came out as about 4.3 s of noise, and Quake's 0.16 s talk sound as 2.1 s. With it they play in 0.53–0.57 s and 0.21 s.
Audio suite
test/audiotestis the audio counterpart oftest/gltest. A small guest program built with MIPSpro plays a 1 kHz, 2 s tone through the audio library, or writes it as an AIFF for playaiff and sfplay. The sweep covers 8 to 48 kHz, mono and stereo, 8 and 16 bit, plus a case that plays 44.1 kHz after a 22.05 kHz program.check.pymeasures each tone's length and pitch in theIRIS_OCTANE_AUDIO_WAVcapture.clanker/devices/audio-test-suite.mdhas the results and how to run it.On an Octane running IRIX 6.5.30, all 86 cases pass with this change. Without it, 72 fail: only 44.1 kHz played right.
O2 at 8 and 11 kHz
The suite reads the O2's
IRIS_O2_AUDIO_DUMPtoo, and it found a separate O2 bug, fixed in the last commit. IRIX keeps only about 2 ms of sound queued in MACE's ring, computed from the read pointer it reads back. iris only advanced that pointer on the service thread, which backs off to a 2 ms poll when CRIME is idle. So at 8 and 11 kHz IRIX's lead fell a frame short, the ring ran dry, and a zero landed in the tone about every 33 samples, playing it up to 17% sharp. A read of an audio register now brings the rings up to date first, as serial DMA already does. All 86 cases pass on the O2 after it.The Indy and Indigo2 aren't covered yet, because HAL2 only records on the host side.
Also
The last commit fixes an unrelated IOC3 test that failed now and then under load (
host_input_arrives_in_the_receive_ring_after_the_timer). The model was right; the test assumed it ran inside the 32 ms receive timer's window. It now holds the deadline off while it reads.