Commit Graph
166 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 9a77ebb743 sceAudio: tighten up the rest of the savestate handling
A re-read of the whole thing after the reservation bug, looking for anything else
it got wrong.

The SRC channel's buffer count is read straight out of the state and then used to
index a two-element array, so a corrupt one is a write outside the struct. It is
range-checked now, the same way the channel count above it already was.

A channel holding a buffer with no samples left of it was stuck: the mixer skipped
it without ever clearing the address, so it read as busy for ever and the game had
no way back to sound. The API cannot produce that - a channel is never reserved
for zero samples - but a savestate can claim it, so the mixer now retires such a
channel instead of stepping over it.

Also took the "get rid of this next time we bump" the version-2 resampler section
came with, since this branch is that bump. Nothing was ever in it.

Checked by loading states written by a released build for a game that uses the
mixer channels and one that uses Output2, plus a round trip of the new format.
Every section ends in a marker, so a conversion path that consumed the wrong
number of bytes would fail the load rather than quietly corrupt what follows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 c1bbd6ae2b sceAudio: stop dropping the reservation when loading an older savestate
Loading a state written by a released build left every channel unreserved, so
the game's next output came back SCE_ERROR_AUDIO_CHANNEL_NOT_INIT and the audio
never recovered.

The old format's reserved flag, sample count, volumes and format are read at the
top of AudioChannel::DoState and are perfectly good. Only the play position
cannot be reconstructed. The conversion path used clear() to zero the fields that
had no equivalent, which threw the rest away with them - my own cleanup two
commits ago swapped explicit field assignments for that call and did not notice.

The SRC channel had the same problem from the other end: older states carry it as
a ninth entry in the channel array, and that record was read into a throwaway.
What reserve agreed on carries over fine, so it now does.

Both halves were checked by loading a state written by a master build, with and
without the fix. Newer states are untouched and still round-trip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 84bd8459e9 sceAudio: implement sceAudioOneshotOutput, and cover the rest of the channel rules
Another pass looking for gaps, all of it now recorded by audio/blocking/channels
and audio/blocking/oneshot.

sceAudioOneshotOutput was the last unimplemented entry in the module, returning
"library not linked" to anyone who called it. It plays one buffer on a channel it
never reserves, so the channel frees itself when the buffer runs out, and its
argument checks are their own set: any positive sample count, aligned or not, no
upper bound, a negative volume rejected rather than skipped, and no busy check at
all. No game is known to use it; it is implemented because tracing it turned out
to be cheap, not because anything needed it.

The channels test confirms four behaviors: sceAudioChReserve(-1) skips a released
channel that is still playing while a reserve of it succeeds, a second channel
joining a running mixer doesn't lose a block unlike a first, mono counts down in
the same 64-sample steps over the same time as stereo, and the panned blocking
output has no extra delay on the high channels.

Also: the SRC resampler now interpolates into the next buffer at a buffer join
instead of holding the last sample, since the codec reads the two descriptors as
one stream. That only shows up at non-native rates and no test can see it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 412d11534c sceAudio: drain on vaudio release, and charge what an output call really costs
Two things the last pass looked at and left, now measured on hardware by the new
audio/blocking/vaudio and audio/blocking/overhead.

sceVaudioChRelease is not shaped like the Output2 and SRC releases. It hands the
channel a null pointer first, which waits for a buffer to finish, so it blocks for
one buffer and returns 0 where the others would refuse - and the buffer is played
out instead of being dropped, which is what we were doing. The reservation goes
now rather than when the drain finishes: the caller is parked either way and gets
the same answer at the same time, and the buffers keep playing because the mixer
does not look at the reservation.

The same test turned up two more: a *failed* sceVaudioChReserve still marks vaudio
reserved, so a caller that lost the channel to Output2 is told 0x80000021 next
time until a release clears it; and sceVaudioChRelease ignores that flag entirely
and releases whatever holds the SRC channel, which pspautotests already called the
"wrong release".

The flat 10000 cycles charged to every Output2 and SRC output turns out to be
right only for the refused case. Every other outcome ends up querying the codec
and costs over 100us on hardware, including finding the channel unreserved, so
those now cost 25000. Mixer channels are the other way round - cheap to refuse,
expensive on the one output that starts the DMA - so that charge moved to the DMA
start. F1 2009 behaves identically and Burnout Dominator's mixed output is
bit-identical to before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 000af25d81 sceAudio: split the SRC channel out, and stop rescheduling mid-syscall
The nine channels were one array of one struct, but only index 8 ever used the
two DMA descriptors, the played/fraction position and the waiting-thread vector,
and only 0-7 ever used the buffer slot - so eight copies of a std::vector sat
there dead. They are two different pieces of hardware and now they are two
structs: AudioChannel for the eight the mixer walks, and a single AudioSRCChannel
for the one the codec reads directly.

Found while rereading it:

- __AudioUpdate can now run from inside an output call, and it reschedules when
  it wakes a thread. A context switch there lands before the syscall writes its
  return value, so the caller loses it and the woken thread gets it instead.
  Deferred with hleReSchedule when a syscall is in flight. The same fix applies
  to the release path, which had the problem before this branch existed.
- A finished SRC buffer whose waiting thread had given up dropped the completion
  entirely, so the next caller waited a buffer too long. Now it walks past dead
  waiters and banks the completion if nobody is left.
- An output with a null pointer changed the channel volume, where the hardware
  returns before touching it.
- A busy channel came back as an error from the Output2 path and as debug from
  the mixer path. It is an ordinary answer a game polls on, so both are debug
  now; the old behavior filled the log with 18k error lines in a minute of F1
  2009.
- AudioChannel::reset had no callers left: releasing a channel with a thread
  parked on it is refused, so there is nobody to wake.
- The two routing modes are globals and were written once per channel. They get
  their own small savestate block.

Savestate: the sceAudio section goes to 3, and the SRC channel gets a section of
its own. States from released builds carry it as a ninth channel record, which is
read and discarded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 ac50cd5759 sceAudio: model the real buffering, so contending threads get told "busy"
I deduced that this was the case, and attempted implementing this path long ago,
but I could never quite get it to work in all games. Set Claude on a quest to
research and implement it, and lo and behold, it works. A bit sobering.

Fixes #12888 and likely more. Additionally, audio latency is likely slightly
improved overall, and memory usage is down by 4.6MB.

Claude says:

The blocking output calls are not a queue that callers line up behind. Each
mixer channel holds exactly one buffer and at most one parked thread; a second
thread arriving while the first is waiting is told the channel is busy and is
expected to skip its turn. The Output2/SRC channel holds two DMA descriptors and
refuses a third caller outright, without waiting at all.

We blocked everyone instead, so a game running a movie thread and a sound-effect
thread over one output made the two alternate - a frame of movie audio, a frame
of effects silence - and the movie played at half rate. That is #12888, seen in
F1 2009 and Colin McRae: DiRT 2. With this, the movie thread keeps the channel
for the whole cutscene and the effects thread is refused, which is what the
hardware trace shows.

The driver also never copies a buffer on the way in: it stores the pointer and
its mixer walks it forward 64 samples at a time out of the game's own memory.
Modelling that fixes #20095 as a side effect, and drops the 4.6MB of per-channel
sample rings we were carrying. The mix event is re-phased to the moment a DMA
starts, since the mixer thread outranks its caller and gets a block in before the
output call returns.

Along the way: the two rest-length calls differ after a null-pointer output,
sceAudioChRelease reports not-reserved rather than not-init,
sceAudioChangeChannelConfig validates the format,
sceAudioChangeChannelVolume validates nothing, and sceAudioOutput2ChangeLength
takes a range of 17..4111. Details in docs/sceAudio.md.

Savestates: AudioChannel goes to version 4. Older ones stored mixed samples that
can't become a pointer and a position again, so they load with the pending audio
dropped and any parked threads released.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:03:07 -06:00
Henrik RydgårdandClaude Opus 5 e4613a3487 WaveFile: don't assert-crash when the wav file couldn't be opened
__StartLogAudio ignored WaveFileWriter::Start()'s return value and set m_logAudio
either way, so a wav file that couldn't be opened - read-only or full audio
directory, most likely - left every subsequent mixed block calling
AddStereoSamples on a closed file. That opens with _assert_msg_(file, ...).

Also other minor fixes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-20 00:40:41 +02:00
Henrik Rydgård c5e4d0d90d Rename the get-memory-pointer functions to make it clear where CPU exceptions can happen. 2026-08-12 14:06:16 +02:00
Henrik Rydgård 3bd41376da More memory cleanup 2026-08-11 20:12:10 +02:00
Henrik Rydgård 1d32f68869 Remove an unnecessary callback mechanism 2026-06-12 12:19:31 +02:00
Henrik Rydgård d9eab4e4a2 sceAudio: Add per-channel mute checkboxes in the debugger 2025-04-15 11:44:28 +02:00
Henrik Rydgård af30c04711 Rename 'chans' to g_audioChans 2025-04-15 11:18:53 +02:00
Henrik Rydgård 44bb7a1ac5 Buildfix, minor savestate fix 2025-03-21 20:45:12 +01:00
Henrik Rydgård 0f840e6240 Move JPEG error codes to the big enum, some include cleanup 2025-03-21 20:44:46 +01:00
Henrik Rydgård 3d0cb44f18 Add comment explaining how sceAudio does queueing wrong 2025-03-17 11:02:59 +01:00
Henrik Rydgård fef4a86225 Upgrade the game volume setting to the new format 2025-02-12 09:49:30 -06:00
Henrik Rydgård cf90b24431 Add UI volume setting (affecting navigation sounds and preview audio). Convert alt speed volume into a 100-step percentage. 2025-02-11 17:42:09 -06:00
Henrik Rydgård 9b923b72c1 Refactor audio output, preparing for better volume control 2025-02-11 16:43:29 -06:00
Henrik Rydgård e93c80db4e Cleaning up our SIMD header includes, using the new header 2024-12-19 16:08:48 +01:00
Henrik Rydgård e01ca5b057 Logging API change (refactor) (#19324)
* Rename LogType to Log

* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.

* Mac/ARM64 buildfix

* Do the same with the hle result log macros

* Rename the log names to mixed case while at it.

* iOS buildfix

* Qt buildfix attempt, ARM32 buildfix
2024-07-14 14:42:59 +02:00
Henrik Rydgård d586ec0d5e Don't create Host objects except in headless/unittest 2023-03-25 10:47:01 +01:00
Henrik Rydgård 5e36acb9eb Remove Host::UpdateSound 2023-03-25 10:18:29 +01:00
Henrik Rydgård 349b73acec Move the resampler usage to a common file, AudioCommon. (#17176)
* Move the resampler usage to a common file, AudioCommon.

Ports that don't want to use the resampler can now simply exclude that
file and provide their own implementation. Next up, libretro will be
converted to do it that way.

* Android.mk typo

* libretro makefile fix

* libretro buildfix

* libretro: try a different approach for the temporary solution

* duh

* double duh
2023-03-24 17:19:57 +01:00
Henrik Rydgård e95662cebf Separate out the core interface around the resampler, in order to move it out from __sceAudio. 2023-03-24 14:48:53 +01:00
Henrik Rydgård a5fdf3d05b Add samplerate argument to NativeMix 2023-03-24 14:48:53 +01:00
Henrik Rydgård 1cd34f9d2b Another buildfix attempt 2022-09-01 14:07:27 +02:00
Henrik Rydgård 7bced814ce Remove a per-audio-frame allocation in sceAudio 2022-09-01 11:59:35 +02:00
Unknown W. Brackets 2479d52202 Global: Reduce includes of common headers.
In many places, string, map, or Common.h were included but not needed.
2022-01-30 16:35:33 -08:00
Henrik Rydgård 025bcb1673 Introduce Path, start using it all over the place.
Still lots left to convert!

Convert GetSysDirectory to return Path.

More buildfixing

Remove unnecessary Path( constructors
2021-05-13 10:39:16 +02:00
Henrik Rydgård 886a8b1ac6 Remove Timer.cpp/h. Move various collections into Common/Data/Collections. 2020-10-05 21:05:23 +02:00
Unknown W. Brackets b8342fb8ec SaveState: Rename ChunkFile files to Serialize.
Makes more sense and less weird than ChunkFileDoMap, etc.
2020-08-10 08:04:05 +00:00
Unknown W. Brackets 4c46867230 Audio: Avoid exposing fixed size queue in header. 2020-08-10 08:03:44 +00:00
Unknown W. Brackets 4b4e3432cd SaveState: Split Do() into a separate header. 2020-08-10 08:03:41 +00:00
Henrik Rydgård a9befb5496 Remove our custom atomics code entirely. 2020-05-17 13:11:36 +02:00
Henrik Rydgård fce09f1865 Simplify how to get the debug stats from StereoResampler 2020-05-16 19:47:13 +02:00
Unknown W. Brackets 580c74c3e7 Audio: Remove latency setting.
This setting only changes some internal values that are shown in tests not
to match PSP behavior.  Removed, it now matches how the PSP works.

This setting wasn't really affecting any actual audio latency.
See #11844.
2020-04-08 00:08:19 -07:00
Unknown W. Brackets 4546db5635 Audio: Quick SRC resampling experiment. 2019-06-30 23:44:18 -07:00
Unknown W. Brackets 20ad7f2914 Audio: Track SRC frequency.
sceAudioSetFrequency is kernel mode only, and it sounds like it changes
the audio controller clock (didn't test.)  SRC only affects the SRC
channel.

Currently, this doesn't actually use the sample rate yet, but at least
it's tracked appropriately.
2019-06-30 23:09:46 -07:00
Unknown W. Brackets fd8a0612fa SaveState: Initialize some memory that is saved.
At the very least, will help them compress better.  Also good not to leak
random memory.
2018-06-22 21:29:18 -07:00
LunaMoo e78a644c00 spaces->underscores:) 2017-11-14 09:46:31 +01:00
LunaMoo aba4a4dfdb Change KernelTimeNow to KernelTimeNowFormatted and return nice timestamp. 2017-11-14 07:16:40 +01:00
LunaMoo 2ff4104ddf Requested changes. 2017-11-14 06:33:49 +01:00
LunaMoo f8f4acc316 Blah;p. Buildfix. 2017-11-14 06:03:52 +01:00
LunaMoo e698414f9b Add an option to allow resetting video/audio dump on save/load state. 2017-11-14 05:12:27 +01:00
LunaMoo 99ab8dfc99 Use gameID and emulated timestamp for Avi/Wav dump filenames. 2017-11-14 03:53:45 +01:00
Henrik Rydgård 6a1fa728d8 Remove Globals.h 2017-08-31 17:15:22 +02:00
Henrik Rydgard ff2b6b3fca Assorted cleanup, UWP memory map fixes. PSPFlower runs in both 32-bit and 64-bit. 2017-03-23 10:02:28 +01:00
Henrik Rydgard 26a2d42731 Big mutex overhaul - remove our custom ones, make them non-recursive where possible 2017-02-28 11:40:29 +01:00
LunaMoo d832b4adfd Make sound recording sane and match video recording behaviour. 2017-02-24 05:36:44 +01:00
Chris Burgener 7d089a0f76 Clean up various files 2016-09-03 19:23:17 -04:00