Commit graph

39828 commits

Author SHA1 Message Date
Michael Giacomelli
27a8ca3f55 test_codec: stop SID files after 2 minutes of decoding
The SID codec never finishes on its own, so test_codec would decode
forever and a directory test would stall on the first SID file.  Count
the decoded samples and halt the codec after 120 seconds of audio.  Also
set the track length to that limit so the benchmark results are correct
and show the elapsed time while decoding.

Fixes FS#13662

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I4e7e99bf41d00c5597aae9d48b837e28df5b75c0
2026-10-08 22:44:19 -04:00
Michael Giacomelli
619f2f1093 afr: fix gapless playback with fatigue reduction enabled
afr_configure() returned 1 for every message. For
DSP_PROC_NEW_FORMAT that is PROC_NEW_FORMAT_DEACTIVATED, so the
DSP did not call the stage for the buffer that brought a new
format: the first buffer of each track went out unfiltered, and
the filters started on the second. That is 128 samples of a WAV
file, and the whole first block of a codec with 32-bit
non-interleaved output, 4608 samples of a FLAC file.

A codec sets the format again at the start of every track, so
between gapless tracks this put a short stretch of unfiltered
audio into filtered audio, heard as a click at the join.

Return 0, as the other stages do.

Found with test_codec's DSP checksums on a Sansa Clip+: a model
of the DSP with fatigue reduction left out of each file's first
buffer reproduces the sixteen checksums of two runs, and none of
them with it left in. Listening on the same player, the click
between gapless tracks is gone with this change.

Change-Id: I7a1193fa0a4ee0c983cbcc5c1af201e1678579d0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:12:55 -04:00
Michael Giacomelli
49b4d8f2c9 test_cyc: AS3525v2 cache and write testing
The rows that calibrated the Clip+ model in utils/perfsim, timed on
AS3525v2 with the tick timer's count for a 2/3 microsecond clock:

  - calls, returns and loads into pc, interlocks after a load and
    after a multiply, multiplies by operand size
  - a miss with work after it, on each word of its line, back to
    back, and evicting a dirty line; a load or store to the line
    still filling
  - stores to lines not cached: one stream, a word a line, four
    words at a time, two streams turn about
  - copies between uncached buffers, within a memory and across
  - fetch misses in code written where it runs
  - loops of adds and of branches by how many lines they cover, and
    a cached pointer chase likewise
  - the TTA filter stage by stage and Tremor's window loop

On AS3525v2 the miss rows run in DRAM and again in the RAM inside
the SoC, where codecs are loaded.

Rows that turned out to say nothing, or that a later row replaced,
stay in the file under TEST_CYC_ARCHIVE, each with why.
TEST_CYC_QUICK runs only the newest rows.

With these rows the plugin is about 140 KB on ARMv5, so it is left
out where the plugin buffer is 128 KB or less: the Clip, the m200v4
and the c200v2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I4ec45da514d0abaa02e1e041fd1a0387490994ed
2026-10-08 09:11:36 -04:00
Michael Giacomelli
73b1afabd3 plugins: test_cyc, a per-instruction cycle benchmark
A test plugin that measures what ARM instructions cost on the running
device. Each row times sixteen copies of one instruction in a loop and
subtracts the empty loop, giving cycles per instruction. Where plugins
get IRAM, each row is run with code and data in each combination of
IRAM and DRAM.

It covers ALU, shifts, branches, the load and store forms (including
halfword and register-offset loads), load-use, and the multiplier at
narrow and full-width operands. ARMv5 adds the DSP multiplies, clz,
qadd, ldrd/strd and pld; ARMv6 adds the top-word and dual 16-bit
multiplies, umaal, ssat, rev, the extends, pkhbt, the SIMD adds and
multiply result latency. Cache misses are priced by a pointer chase
and by sequential streams over working sets either side of the cache.

Timing uses the SoC's microsecond counter on PP502x, PP5002, S5L870x,
S5L8720, TCC7801 and i.MX233, and the tick elsewhere. Results go to
the screen and to /test_cyc.txt.

Built only with test plugins enabled, on native ARM targets that run
ARM code (not Cortex-M).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I4a41f6daaa27fb817f411de89901a117c5c70f4d
2026-10-08 09:08:38 -04:00
Amaury Pouly
cea6fca67b hwstub: add stub gdb backend
Change-Id: I1e3fe2bec3f5d926a02027c13b5056d461da2f9d
2026-10-08 08:13:49 -04:00
Amaury Pouly
eb39277d4a hwstub: allow minor version mismatch in net layer
Change-Id: I81a75bd7b9f3fd1a56e5bea521966c1847adfec7
2026-10-08 08:06:30 -04:00
Amaury Pouly
966c1d7650 hwstub: bugfix in library
Change-Id: I1af4b53d872b0691025cc0efd99c1927c2d9384b
2026-10-08 08:04:23 -04:00
Amaury Pouly
2facad7424 hwstub tools: fix/improve usage help
Change-Id: Id1afbe8adaf29887fc97c570ae33127a40a046b6
2026-10-08 08:02:23 -04:00
Amaury Pouly
860582a63a hwstub_load: fix missing break in switch
Change-Id: I7758bcbf9c0726a44c9dc0b4968f8d2d8c978874
2026-10-08 07:58:00 -04:00
Marcin Bukat
8036fda6f0 rk27load: fix build flags for s1 and s2 images
Change-Id: Id3ba41337fc1ac4b3764a344682147173580fd95
2026-10-08 13:43:21 +02:00
Marcin Bukat
be0153afbd YP-CP3: record the FM radio at +12 dB by default
The radio is heard through the codec's input PGA at +12 dB, but on
the recording screen the gain started at 0 dB, so a recording was
much quieter than what was heard just before. Start the line gain,
which the radio uses, at +12 dB.

Defining the defaults also makes the line gain a saved setting on
this target; before, it went back to 0 dB at every boot.

Tested on a YP-CP3: switching from the FM screen to the recording
screen with FM as the source keeps the same loudness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Id65ce25515d7143b2ca21e5ef2a110a356303380
2026-10-08 11:23:23 +02:00
Marcin Bukat
aba3fe1d57 YP-CP3: switch the headphone amplifier pin to GPIO
The headphone amplifier enable, GPIO F2, shares its pin with SDRAM
address line A12 and is A12 until switched in IOMUXB. Rockbox never
switched it, so driving F2 never reached the amplifier: started from
the Rockbox bootloader, playback and the FM radio were near silent
and distorted. They only worked when the original firmware had run
first, as it switches the pin. The 16 MB of SDRAM does not use A12.

Tested on a YP-CP3 booted from the Rockbox bootloader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I40610aaf88f06fd4831d07174b0f66a4ba32204d
2026-10-08 10:40:40 +02:00
Michael Giacomelli
853274937c test_disk: scroll the log when the screen is full
When the text reached the bottom of the screen it started again at
the top, over what was there, so the old lines showed through the
new.  Scroll up a line instead, as test_codec does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I024eda05bbcca6f1539893ea609c89f562436e2f
2026-10-07 22:58:44 -04:00
Michael Giacomelli
303c534022 test_disk: ask which disk to test
The tests always ran on HOME_DIR, so a card could not be tested, and
the internal storage could not be left alone.

Where there is more than one volume the plugin now asks which to
test when it starts: HOME_DIR as before, or any volume the root
directory lists.  "Select disk" in its menu changes it.  The test
directory, the test file and the log are all on the disk chosen,
where the log used to go to HOME_DIR, so that testing a card writes
nothing to the internal storage.  The log names the disk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ib243fdd32fa205e00bc2b39d776e0fc8a420a1f0
2026-10-07 22:58:44 -04:00
Marcin Bukat
a3227cadb5 skin_engine: more tags for the recording screen
What the built-in recording screen shows and the skin tags did not:

- %RS  size of the current recording, as "1.5MB"
- %RP  seconds in the pre-record buffer, empty unless pre-recording
- %Rc  clips counted by the peak meter
- %Rt  trigger state by name; as a conditional off, ready, steady, go,
       postrec, retrig, continue
- %Rw  recording warnings in hex, empty while there are none
- %Ri  recording source by name; as a conditional the same on every
       target: mic, line in, digital, FM radio
- %Rg  gain of the source in dB, the left channel's for line in and FM

The manual gets a Recording section covering these and the recording
tags that were there, none of which it described.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I889f95fd472c472c2589393a6c77eff4a4fd5c8f
2026-10-07 20:12:49 -04:00
Solomon Peachy
7ba413d87d checkwps: Delete incorrect dependency
It was accidently left in from an older iteration of 9d3d2c6e6

Change-Id: Ie911b2eb088eb967a73c39585cb4d8b69eef9203
2026-10-07 17:34:22 -04:00
Solomon Peachy
60935805a9 build: Only simulator builds get a 'make install' target
Change-Id: I7ab3f698fba4950cf20c9448ca0f5cca00c8c100
2026-10-07 17:34:22 -04:00
Marcin Bukat
da4c218046 skin_engine: fix the text of five recording tags
- %Rf printed the sample rate with "%lu.%1lu", a field width rather
  than a precision: 44100 Hz came out as "44.100", 22050 Hz as "22.50".
  It is now in kHz without trailing zeros: "44.1", "22.05", "48".
- %Re switched on the format setting plus one, so its text named the
  next format: "aiff" for WAV up to nothing for MP3. It now names the
  format set, MP3 as "mp3" like the others.
- %Rb printed the index of the MP3 bitrate setting, "12" for 128 kbps.
  It now prints the bitrate in kbps. Dead code from 2009 goes.
- %Rm was true for stereo: rec_channels is 0 for stereo. The classic
  status bar has shown the mono icon for stereo recordings since 2009.
- %Rn counted the minutes on past 59, so %Rh:%Rn:%Rs showed 01:61:05
  after an hour. It now counts within the hour, as %Rs does.

As conditionals %Rf, %Re and %Rb keep their values, so the classic
status bar's sample rate, format and bitrate icons are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Id9fb5c0b71fc0a44c5c7b3239a74397cb71090bf
2026-10-07 16:38:16 -04:00
Marcin Bukat
776fe2ad9c rk27xx: boot Rockbox on USB wake when it can write the NAND
The bootloader starts the OF when the player is woken by plugging in
USB, because the OF was the only firmware that could give the host
write access to the internal flash. A build with FTL_ALLOW_WRITE
can do that itself, so boot Rockbox there and leave the OF to a held
key. Builds without it keep the old behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I74af2f8c56436a3f7c9ad97d1b1638469807f0dc
2026-10-07 22:20:18 +02:00
Marcin Bukat
1669e49cd7 rk27xx: debug menu switch to show NAND SYS over USB
The NAND's SYS volume holds the bootloader's firmware and is hidden
unless a build defines HAVE_RK27XX_NAND_SYS. Getting at it, e.g. to
replace BASE.RKW, needed a special build.

Add "NAND SYS on next USB" to the debug menu. While it is set, the
next USB connection shows SYS through the USER drive, as "NAND SYS",
and the switch clears when that connection ends. It is decided once
per connection, so setting it while connected waits for the next
one, and it is held in RAM only. Writes still need FTL_ALLOW_WRITE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Idda3f1c5e00bea511618021397a6cb3bb0be7361
2026-10-07 18:33:01 +02:00
Marcin Bukat
406a28cc6a rk27xx: boot the OF when it asked for a reboot
Before the original firmware resets itself, e.g. at the end of a
firmware update, it stores a boot mode in an undocumented GPIO1
register that keeps its value over the reset. The NAND bootloader
reads it to decide whether to boot at all, then starts our
bootloader, which chose Rockbox or the OF from the buttons alone, so
an OF that restarted itself came back up as Rockbox.

Rockbox never writes the register and it reads 0 after power-on, so
boot the OF whenever it is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I22a98cafad48f66302a8401c9df847a073675a58
2026-10-07 18:09:15 +02:00
Marcin Bukat
c3a62cee96 rk27xx: pass the NAND bootloader's handoff words to the OF
Before it jumps to an image, the rk27xx NAND bootloader writes three
words at the address in RKW header field 0x14: a magic, its version
and which copy of the image it loaded. The original firmware never
initialises them: it reports the version over USB and counts its own
reboots in the third word, and once the count reaches 5 it reboots
into a mode the NAND bootloader will not boot. When our bootloader
started the OF, nothing wrote them, so the OF ran with whatever was
left in DRAM.

Field 0x14 of our own images held a constant taken from some other
image. Point it at the last 12 bytes of DRAM, which nothing uses
before our bootloader runs. load_rkw() in the bootloader now reads
the words there before loading an image, and writes them at the
address in the loaded image's header when that lies between the
image and the bootloader. Started over USB, it passes version 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ife1d63e361a780c28a9f0626d110552b98bb6b7f
2026-10-07 18:09:15 +02:00
Marcin Bukat
e61aa0cd37 usb_storage: allocate the buffer at the storage handover
The host configures the device before Rockbox has handed the storage
over, and mass storage allocated its buffer right then. On the
recording screen the recording buffer still holds all free memory at
that point and cannot shrink, so plugging in USB there panicked with
"usb_storage_init_connection(): OOM". Recording closes, and frees its
buffer, only on SYS_USB_CONNECTED, which the handover broadcasts.
Playback does not hit this: its buffer gives memory up on request.

The buffer is only needed to run commands, and commands already wait
for the handover. So it is allocated, and the endpoint primed for the
first command, in the notify event that both handover paths in usb.c
send, or at once when the storage is already handed over. Until then
the host's first command waits at the endpoint, NAKed. GET_MAX_LUN,
which comes before the handover, is answered from the core's control
buffer instead of the transfer buffer. Targets with static USB buffers
are unchanged.

Tested on a Samsung YP-CP3: USB plugged in on the recording screen,
at boot and from the main menu, mounts, and files copied both ways
keep their checksums.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icadc24fd52a07d03125234e492dab0ede9e339c9
2026-10-07 09:30:49 -04:00
Marcin Bukat
136e6b9953 recording: stop monitoring the input before USB mode
Plugging in USB on the recording screen closes recording, but the input
stays selected: the screen switches it back to playback only when it
ends, which is after USB mode, at the unplug. On a Samsung YP-CP3,
whose codec passes the microphone or FM radio through to the
headphones while recording, the input could be heard all through USB
mode. Switch to playback before entering it.

Tested on a YP-CP3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I194d5e120cc39685aa43cea4741e826a86ed2c65
2026-10-07 07:56:53 -04:00
Marcin Bukat
9ce3fca4cb rk27xx: no whole-cache invalidate at run time
commit_discard_idcache() invalidated both cache ways at run time, at
every codec and plugin load and every USB connect. With the cache on,
the CPU crashed in the poll loop when that loop started a cache line of
its own; with the cache off around it, since 0e1952a4d1, code fetched
again afterwards could come back wrong. On a Samsung YP-CP3 a build
with a few changes elsewhere took an undefined instruction exception
in the USB interrupt handler at every boot into USB mode, always at
the same instruction, wherever the linker put it. The same build with
only the invalidate at USB connect replaced by a nop booted and
worked.

The original firmware invalidates the ways only once, at power-on with
the cache off, as crt0.S does, and after that only single lines. None
is needed at run time: the cache is unified and write-through, so what
the CPU writes, code included, is in memory and in any cached copy, and
every DMA into memory - SD reads, USB receives, recording - discards
the lines of its own buffer first. NAND is read by the CPU. So
commit_discard_idcache(), and commit_discard_dcache() with it, now do
nothing.

Tested on a YP-CP3, with the build that crashed: it boots into USB
mode, copies files with matching checksums, plays several formats,
runs plugins (fft with playback, bubbles), and records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I14301a0c05736e28e53a3365c838f4a4a73052b3
2026-10-07 08:20:44 +02:00
Solomon Peachy
9d3d2c6e67 checkwps: validate themes against builtin cabbiev2 assets
Themes for a given device often reference the assets of the default
cabbiev2 theme.  Record a list of what is bundled, and check anything
not included with the theme against that list.

If something is still missing, then that's an error.

This applies to fonts, wps files, backdrops, icons, and so forth.

Change-Id: I5ff7feaae892c8b9a81c2cb3035879672f3e6c29
2026-10-06 23:24:34 -04:00
Solomon Peachy
31fac4c400 configure: drop the 'bootloader only' from the ipodnano3g
This allows the themesite to validate this target

Change-Id: I77407098ec0c92c845e5e14f4d6bfcd3fb230ebb
2026-10-06 22:50:10 -04:00
Michael Giacomelli
4c08906670 test_codec: set the DSP's output samplerate
The DSP gets its output samplerate from the codec thread when a
track is played. test_codec did not set it, so its runs with the
DSP used the rate of the last track played, or the default if
there was none: after "Playback frequency" was changed, they
resampled to the old rate until something had been played.

Set it from the mixer for each file, as the codec thread does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:25:09 -04:00
Michael Giacomelli
e27fb9a9f0 test_codec: add checksum runs with the DSP
"Checksum with DSP" and "Checksum folder with DSP" give the CRC32
of the DSP's 16-bit output for a file or a folder, as "Checksum"
does for the codec's output. That lets the DSP of a device, with
its settings, be checked against a reference for several files in
one run; "Write WAV with DSP" writes one file, /test.wav.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:25:09 -04:00
Michael Giacomelli
fc1d445d69 atrac3: keep the second window table out of small IRAM
b7170e03c4 added window_lookup_mid, 512 bytes, to IRAM on every
target. On the targets with 48KB of IRAM for a codec that left
the ATRAC3 codecs 48 bytes too large to link (iPod Color and the
other PP502x players with 96KB of IRAM).

Put the table in IRAM only on the targets with more, like the
decoder's other large tables. Nothing changes there.

Built for iPod Color, where the two codecs now have 476 bytes of
IRAM to spare, and for the iriver H120.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:18:33 -04:00
Michael Giacomelli
02d5520c5f atrac3: output samples with 28 fractional bits
The decoder gave the DSP samples with two fractional bits (sample
depth 17), far fewer than any other codec. The DSP's filters are
only as precise as the samples they are given: with an equalizer
band at a low frequency the output was noise, or held at full
scale.

Scale the last stage of the synthesis filter up by 11 bits and
set the sample depth to 28. On ARMv4 the bits are taken from the
64-bit sums of the dewindowing, which had them. Elsewhere the
input of that stage is scaled, in its matrixing step; that is not
done on ARMv4 because its multiplier takes longer for the larger
operands (51.97 against 48.85 MHz below). The Coldfire path uses
the C matrixing and has not been run.

Seven files decoded under perfsim for the Sansa e200v1 and Clip+:
shifted down again, the e200v1 output is within one step of the
old output, and the Clip+ output no further from the e200v1's
than before. Through a ten band equalizer with bands at 32 and
64 Hz (the filter fix of the DSP included), the noise of
atrac3_lp2_132.oma falls from -64 dB to -101 dB relative to the
signal.

Estimated with perfsim (not measured on a device),
atrac3_lp2_132.oma: e200v1 48.81 -> 48.85 MHz, Clip+ 26.91 ->
26.96 MHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
3966b420a3 atrac3: use the QMF window's symmetry in the ARMv4 filter
The dewindowing loop is 72% of ATRAC3 decoding on ARM7TDMI. The
window is symmetric, so read only its first half: each pair of
coefficients serves two inputs from the front of the 48 and, with
the two swapped, two from the back. That halves the coefficient
loads and lets them be done four at a time.

The sums are kept in 64 bits, so the new order of the additions
does not change the result: the PCM output of seven files is
byte-identical.

Estimated with perfsim for the Sansa e200v1 (not measured on the
device), atrac3_lp2_132.oma: 53.74 -> 48.81 MHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
b8861e3f09 atrac3: remove the rounding bias of the ARMv5E QMF filter
The ARMv5E dewindowing routine takes 16 of each product's bits,
rounded down, so each sum of 24 products came out about 12 units
low. Through the filter bank that put a dc offset of about 12 LSB
(of 16 bits) on the output and, from the high band, a tone at half
the sample rate; together they were four fifths of the decoder's
error on these targets.

Start the sums 12 up. Also correct a comment in both ARM files.

Checked with perfsim (Sansa Clip+ build) against ffmpeg's decode of
seven files: the dc offset goes from 11 to 12 LSB down to under
1 LSB, and the error from about -66 dBFS to -69 to -72 dBFS, the
same as the ARMv4 routine gives. Decoding is 0.7% slower (26.73 to
26.91 MHz for atrac3_lp2_132.oma). The ARMv4 output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
8667af5cd7 atrac3: fix gain compensation at adjacent gain points
applyVariableGain() applied the constant gain before a gain point
in a do-while loop, so at least eight samples of it, even when
there are none: when the point is at the start of the block or
follows straight after the previous one. Every later gain point in
the block was then eight samples late. This came in with the loop
unrolling of 51a8be1a0f.

Use a while loop, as ffmpeg does.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode.
Over the whole of atrac3_lp2_132.oma the SNR goes from 45 to 51 dB
and the worst frame from 14 to 21 dB; the right channel of a joint
stereo RM file goes from 37 to 54 dB. What is left is a noise floor
near -72 dBFS in every frame, from the 2 fractional bits the
decoder works with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
b7170e03c4 atrac3: apply the whole mdct window
Only the first and last 128 points of the 512 point window were
applied, and the 256 between were taken to be one. They are not:
the window rises to 1.207 there. Every block was therefore up to
1.6 dB low over half its length, which left the output 0.7 dB low
on average and an error about 23 dB below the signal at all
frequencies.

Add the missing half of the table and apply it.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode,
with the two previous fixes applied before and after:

                               level     SNR
  five plain stereo RM files   -0.7 dB   23 dB  ->  0.0 dB   54-57 dB
  joint stereo RM file         -0.7 dB   23 dB  ->  0.0 dB   37-45 dB
  joint stereo OMA file        -0.7 dB   23 dB  ->  0.0 dB   45 dB

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
1bae7e0e0a atrac3: reverse the whole of an odd band's spectrum
The spectra of the odd QMF bands are stored reversed. 8722c6f2bb
moved that reversal out of IMLT() and into the decoding of the
coefficients, but did it within each scale factor band, one place
off, and not at all for tonal components. The odd bands, which is
5.5 to 11 kHz and above 16.5 kHz at 44.1 kHz, have been decoded
wrongly since: against ffmpeg's decode that range was uncorrelated.

Reverse the whole band in IMLT() again, as ffmpeg does. The old
commit measured the saving it is giving up as 0.11 MHz on PP5024.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode
of an OMA file and six RM files: the coherence of the 5.7 to 10.7
kHz range goes from under 0.01 to 0.99, the same as other bands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
f1d7ae3bbf atrac3: give the decoder its frame size for RM files
Since 2e314093c8 the ATRAC3 decoder takes its frame size from
id3->bytesperframe, which the RM metadata parser does not set. With
a frame size of zero, a file in plain stereo mode had its second
channel decoded from the first channel's data: both outputs were
the left channel.

Set it from the RM sub packet size before the decoder is opened.

Checked with perfsim (Sansa e200v1 build) on six ATRAC3 .rm files:
the two channels were identical sample for sample before, and each
now follows its own channel of ffmpeg's decode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:56:47 -04:00
Michael Giacomelli
dcee3ce62f jpeg: decode 16-bit quantization tables (SOF1)
Both decoders rejected extended sequential JPEGs (SOF1), which are
baseline files in all but name when they have 8-bit samples, and
16-bit quantization tables, which libjpeg writes for very low
quality settings unless told to force baseline.

Accept SOF1 with 8-bit samples, and read 16-bit table entries. The
core loader keeps its tables in 16 bits and scales them for the IDCT,
so it rejects entries over 8191; libjpeg's largest at quality 1 is
4950. Files using more than two Huffman tables are still rejected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I705a61402b4a0ab252995f8559b7ff2f68b05d72
2026-10-06 00:44:06 -04:00
Michael Giacomelli
2890d4a40d vorbis: don't buffer the comment packet
The comment packet is not used by the codec, but skipping it grew
the stream's packet buffer to the packet's full size. Heap use rose
with the size of the tags: a file with 300 KB of embedded album art
needed 617 KB of heap, and one with 940 KB of art could not be
opened even with a 992 KB codec buffer.

Skip the packet without storing it: forget the part already
buffered and let ogg_stream_pagein() drop the rest, as it does for
a packet whose start was lost.

Seeking to the start of a file ran the headers through the stream
again, which buffered the whole comment packet by the normal path.
Start the seek search at the first audio page instead, as libvorbis
does, and handle a target inside that page.

With both, heap use no longer depends on the tags. Large tags cost
at most about 90 KB over an untagged file, from the two Ogg buffers
growing to hold one full page each.

Checked with perfsim on the Sansa Clip+ model, not on a device,
with the heap limited to what a 512 KB codec buffer leaves:
files with up to 1.5 MB of art or 240 KB of text decode to the
same PCM hashes as the untagged audio, and 15 seeks per file,
including to 0, 30 and 150 ms, give the same positions and PCM
as the unmodified codec on ten files. Chained streams were not
tested.

Change-Id: Id27ebf98b436d813cfa3179951eee5bd7b2c0cd2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 22:41:43 -04:00
Michael Giacomelli
34666474b8 wma: fix dropouts in files with LSP exponents
Where the LSP curve peaks, the value its -1/4 power is taken of is
far below one step of 16.16 and was rounded to zero. pow_m1_4() of
zero is enormous, the block's levels are taken relative to that
maximum, and so the whole block came out about 46 dB down. In
StutteringFile.wma (22 kHz mono, 20 kbps) this muted a block here
and there, which is heard as a stutter.

Pass the value to pow_m1_4() with all of its 38 fractional bits.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode
of that file, in blocks of 46 ms: 11 of 841 were off by more than
1 dB, the worst by 10.6 dB; none are now, the worst by 0.4 dB.
Four other files with LSP exponents are unchanged or closer, and
files without them are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 21:21:27 -04:00
Michael Giacomelli
d084219b9a vorbis: refuse streams with more than two channels
Tremor is built here with CHANNELS 2: synthesis produces no PCM
for a stream with more channels, so a 5.1 file "played" as nothing
at all. It was still set up in full first, which took 416 to 880 KB
of the codec heap for the 5.1 and 7.1 files tried, and wrote one
entry per channel into arrays sized for two.

Refuse such a stream when its identification header is read. The
codec then returns an error at once, with under 8 KB of heap used.

Checked with perfsim on the Sansa Clip+ model, not on a device:
six 5.1 and 7.1 files are refused, and 13 stereo and mono files
give the same PCM hashes as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:47:12 -04:00
Michael Giacomelli
9477e371a3 wma: fix overflow in the LSP exponent curve
Very low bitrate WMA files send their spectral envelope as LSP
coefficients. The curve computed from them squares two products
whose value can reach about a million, in 16.16 fixed point. Where
the envelope should be lowest the squares overflowed, and those
points came out near the curve's maximum instead: bands at the top
of the spectrum up to 17 dB too loud, and everything else a few dB
low, as levels are taken relative to the maximum.

Sum the squares in 64 bits, and let pow_m1_4() take a value above
the 16.16 range.

Checked with perfsim (Sansa e200v1 build). The curve is within
0.2 dB of ffmpeg's formula at all 57600 points of 80 blocks traced
from two files. Against ffmpeg's decode, as the mean difference in
band levels over the whole file, with the noise coding fix applied
before and after:

  StutteringFile (22 kHz mono, 20 kbps)    1.6 dB -> 0.3 dB
  shoujicomcn2117164 (same)                0.2 dB -> 0.1 dB
  terra8k (11 kHz mono, 8 kbps)            0.3 dB -> 0.0 dB

Only the five files that use LSP exponents change; the other 23
are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:47:12 -04:00
Michael Giacomelli
11b12894ad wma: fix the level of noise coded high bands
Low bitrate WMA files code their upper bands as noise at a given
power. Those bands came out 12 to 25 dB too quiet, which dulled
the treble. Two things were wrong.

The exponent pointer was moved too far in blocks whose exponents
have another resolution. This is ffmpeg's r20756 (f78501b264,
"Fix apparent 10l typos introduced in r8627"); r8627 was merged
here in 2f1da8d24a but the fix for it never was.

The fixed point gain of a band lost nearly all its precision: it
was zero for three bands in four in the file traced, and the sum
for a band's power overflowed in a quarter of them. Compute the
gain in 64 bits, once a band. The factor for the noise below the
first coded coefficient (WMA v1 only) was 16 bits too large and is
corrected by the same reasoning; no file here exercises it.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode,
as the mean difference in band levels over the whole file:

  01 - Jane Austen (44.1 kHz, 32 kbps)   5.5 dB -> 0.0 dB
  07.Devil.In.My.Mind (same)             6.2 dB -> 0.0 dB
  beyonthepain907z (same)                6.8 dB -> 0.0 dB
  moshimoashitaga (same)                 2.8 dB -> 0.0 dB

Four more noise coded files, already within 0.3 dB, now match too.
The output of the 15 files without noise coding is byte-identical.
Files that use LSP exponents still differ in their top bands; that
is a separate fault in the LSP curve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:47:12 -04:00
Michael Giacomelli
fb2ee6c08f wma: decode every codec packet in an ASF payload
A payload normally holds one codec packet of blockalign bytes, but
some files put several in each. Only the first was decoded, so such
a file played one packet in every 8 or 15, as a few seconds of
broken sound, and then ended.

Step through the payload in blockalign sized packets.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode:

  doors-test.wma (WMA v2, 11 kHz mono), 15 packets a payload:
    80896 of 1205760 samples before, all of them after, 86 dB SNR
  test.wma (WMA v1, 44.1 kHz stereo), 8 packets a payload:
    1294336 of 10346496 samples before, all after, 114 dB SNR

The output of 26 other WMA files is byte-identical before and after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:47:12 -04:00
Michael Giacomelli
6ffbee954b wmapro: fix the level and accuracy of 16 bit streams
A WMA Professional stream of 16 bits per sample played about 48 dB
too quiet and with a noise floor near -70 dBFS.

ffmpeg scales the transform's output by the stream's sample size.
That was dropped when the decoder was converted to fixed point
(d884af2b99, 16284ae8ae), and the output is passed to the DSP as if
every stream had 24 bits. A stream of fewer bits has a lower
quantization step to match, so it came out low by the difference,
and it used the bottom of the integer quantization table, where the
factors have only a few significant bits.

Decode a 16 or 20 bit stream at the level of a 24 bit stream: use
that stream's quantization step, and scale each band's factor by
the ratio that is left. 24 bit streams are not affected.

Checked with perfsim (Sansa e200v1 build) against ffmpeg's decode
of a 16 bit, 192 kbps stereo file, the only such file to hand:

            level      SNR vs ffmpeg   noise
  before    -48 dB     37 dB           -70 dBFS
  after     correct    85 dB           -117 dBFS

A 24 bit file's output is byte-identical before and after. The
20 bit case follows the same rule but is untested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:13:48 -04:00
neofright
4b8ea97fa9 Support ROCKBOX_DIR in HibyOS hosted bootloader
Currently if you want to compile a bootloader like so:

$ ../tools/configure --rbdir=/.rockbox.aigoerosq --target=aigoerosq --type=b

This won't work because the bootloader is hard coded to look for the rockbox Linux binary in /.rockbox/rockbox.erosq regardless of what is specified by '--rbdir'.

This commit adds support for ROCKBOX_DIR in the hosted HibyOS bootloaders.

After compiling, they can be installed with adb via (for example):

adb push build/bootloader.erosq /usr/bin/bootloader.erosq

Change-Id: Ic4ceb75107f608beacfd497998a11a5874e87ac1
2026-10-05 19:42:58 -04:00
Michael Giacomelli
4502948cd8 wmapro: add a two-channel path to the channel transform
inverse_channel_transform() ran its general N-channel matrix loop
for every sample of a stereo stream, where the matrix is exactly
+-1.0. That loop was a quarter to a third of the whole decode, and
GCC 9.5.0 compiles it worse than 4.9.4 did, which made the codec
6-9% slower on ARM7TDMI after the toolchain update.

Handle a group of two channels separately: add and subtract when
the matrix is +-1.0, and a plain four multiply loop otherwise. More
than two channels still use the general loop.

This reverses the regression from the GCC 9.5.0 update and goes
well past it. The loop the newer compiler handled badly is no longer
used for stereo, so the two compilers now give the same speed to
within 1%, about 25% faster than the codec was with GCC 4.9.4
(estimated with perfsim, e200v1, wmapro_141k: 25.81 MHz with 4.9.4
before this change, 19.5 MHz with either compiler after it).

Output is bit-identical: whole-file PCM hashes match before and
after for five stereo files at 55-271 kbps, built with GCC 9.5.0
and with 4.9.4, and also with the multiply path forced on.

Measured with test_codec, wmapro_141k.wma, MHz for real time:

  Sansa e200v1  27.99 -> 19.70
  Sansa Clip+   21.78 -> 15.80

Estimated with perfsim for the other files (e200v1 / Clip+):

  wmapro_55k   25.21 -> 17.06 / 20.02 -> 13.71
  wmapro_80k   26.17 -> 18.01 / 20.75 -> 14.44
  wmapro_173k  28.52 -> 20.29 / 22.55 -> 16.25
  wmapro_271k  30.85 -> 22.52 / 24.34 -> 18.04

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:29:19 -04:00
Marcin Bukat
361913fdd2 YP-CP3: make the WM8750 the I2S master
The WM8750 frames its ADC on ADCLRC and its DAC on DACLRC. As the I2S
slave both are inputs, and the rk27xx has a single LRCK, which on the
YP-CP3 evidently does not reach ADCLRC: with the rk27xx as the master,
the recording DMA got exact zeros on most visits to the recording
screen and real samples only by chance, sometimes changing partway
through a visit. That stays so with the codec PLL no longer glitching
the I2S clocks (previous commit); with the codec as the master it does
not happen.

The codec now drives BCLK and both LRCKs itself, and the rk27xx I2S
transmitter and receiver are slaves. The codec's MCLK still comes from
the rk27xx codec PLL at 256 fs, so sample rates stay exact. The
original firmware runs the codec as master too, but off a fixed 12 MHz
in USB mode.

RK27XX_I2S_MCLK says the rk27xx makes the codec's MCLK, apart from
CODEC_SLAVE, which also makes it the I2S master. The YP-CP3 drops
CODEC_SLAVE for RK27XX_I2S_MCLK, and the WM8750 driver sets its master
bit as it does for any codec that is not a slave.

Tested on a YP-CP3: playback, FM radio, the recording screen's peak
meter on every visit, recording from the microphone and from FM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0dc93d3b2c45fddf61649702a931d11806e78cbf
2026-10-05 22:35:12 +02:00
Marcin Bukat
ce968e7da1 rk27xx: don't reprogram the codec PLL for an unchanged rate
Every start of recording applies the sample rate again, mostly
unchanged, and set_codec_freq() reprogrammed the codec PLL each time.
That glitches MCLK and every I2S clock made of it, right before the
recording receiver is reset and starts on them. Whether it then kept
its framing came down to code timing: on the Samsung YP-CP3 the
recording screen's peak meter showed one channel empty and the other
saturated on some visits, and builds with debug code added never did.

The PLL is now left alone when it already runs the rate asked for. On a
real change the lock bit, which may still show the old lock at first,
is polled only after the 0.3 ms the datasheet gives for locking, with a
timeout of at least a full tick, and the clocks get 1 ms more to settle
before anything starts on them.

Tested on a YP-CP3, together with the codec as I2S master: the peak
meter on every one of many visits to the recording screen, with the
microphone and FM, and at 22 kHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2267e17c5bc7d8044715e762dbb6b92f45af4dcc
2026-10-05 22:35:12 +02:00
Solomon Peachy
59f15ca630 cabbiev2: Don't generate certain entries for monochrome devices
Namely, 'backdrop' and 'filetype colours'

Change-Id: Ied0b87d9b43d010a37ebb1e33488463c6ad1ebcc
2026-10-05 14:11:34 -04:00