apply_volume() corrupts 16- and 32-bit PCM on big-endian hosts, against its own little-endian contract
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 78/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- cpp
調査の方向性
Start in src/pcm_volume.cpp at apply_volume() and read the little-endian contract in src/pcm_volume.h, then run the standalone harness described against the 16- and 32-bit paths on a big-endian target. Done means S16_LE and S32_LE produce the same scaled little-endian samples as the reference while the existing 24-bit behavior remains correct.
索引モデルが issue の本文から書いたものです。
説明
src/pcm_volume.h states the contract in its own words:
Software volume for interleaved signed little-endian PCM, in Q32 fixed point.
and again on the function:
Scales
lenbytes of signed little-endian PCM in place by a Q32 gain of at mostQ32_ONE.
Two of the three sample widths do not honour that on a big-endian host.
The code
src/pcm_volume.cpp, in apply_volume(). The 24-bit path reads and writes byte by byte, which is correct anywhere:
case 3: {
int32_t sample = static_cast<int32_t>(p[0] | (p[1] << 8) | (p[2] << 16));
...
p[0] = static_cast<uint8_t>(out & 0xFF);
p[1] = static_cast<uint8_t>((out >> 8) & 0xFF);
p[2] = static_cast<uint8_t>((out >> 16) & 0xFF);
}
The 16- and 32-bit paths reinterpret the buffer at native width instead:
case 2: {
auto* samples = reinterpret_cast<int16_t*>(data);
...
}
case 4: {
auto* samples = reinterpret_cast<int32_t*>(data);
...
}
On a little-endian host those agree. On a big-endian one they read each sample with its bytes reversed, scale the wrong number, and store it reversed again.
Reproducing it
pcm_volume.{h,cpp} are self-contained — <cmath>, <cstddef>, <cstdint> and nothing else — so they build standalone. Compiled unchanged from 0.3.0 for MIPS big-endian and run under qemu-mips-static, against a little-endian sine scaled by Q32_ONE / 2 (-6 dB) and read back as little-endian:
──────── x86_64 (little-endian) ────────
16-bit (S16_LE) ok (0/64 samples wrong)
24-bit (S24_3LE) ok (0/64 samples wrong)
32-bit (S32_LE) ok (0/64 samples wrong)
=> contract holds
──────── MIPS big-endian under QEMU ────────
16-bit (S16_LE) FAIL (57/64 samples wrong)
24-bit (S24_3LE) ok (0/64 samples wrong)
32-bit (S32_LE) FAIL (60/64 samples wrong)
=> contract violated
The harness builds a sine in little-endian PCM, calls apply_volume(buf, len, bytes_per_sample, Q32_ONE/2), reads the result back as little-endian and compares against sample * gain >> 32. Glad to attach it, or to open a pull request with it as a test.
That the 24-bit path is right while the other two are not is why this reads as an oversight rather than a design choice.
Why it matters in practice
The ALSA sink opens SND_PCM_FORMAT_S16_LE / S24_3LE / S32_LE, so the bytes handed to the device are little-endian by construction and the 16- and 32-bit volume paths corrupt them on a big-endian build. Anything other than unity gain plays as noise.
I hit this packaging sendspin-cli for OpenWrt, where ath79 — MIPS 24Kc, big-endian, still on kernel 6.18 — is a common "old router with a USB port" host. My package carries @!BIG_ENDIAN for now so it is not offered there.
A related one, read-confirmed but not reproduced: opus_decode() in sendspin-cpp src/decoder.cpp fills its output through an (int16_t*) cast, and libopus writes native-endian opus_int16, so the Opus path looks like it has the same problem one layer down. The same goes for micro-flac's write_samples() fast paths. I am reporting those separately.
Environment
| Source | sendspin-cli 0.3.0, src/pcm_volume.{h,cpp} unchanged |
| Toolchain | mips-linux-gnu-g++ (Debian), -O2 -static |
| Runner | qemu-mips-static |
| Reference | same harness on x86_64, which passes all three |
- 主要言語
- C++
- スター
- 5
- フォーク
- 3
- 平均マージ
- 7時間 6分
- マージ済み PR(30日)
- 20
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Sendspin/sendspin-cpp-cli のほかの issue
-
Recovering from a device outage reports a sync error and plays silence for the swallowed lookaheadオープンenhancement
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
Sendspin/sendspin-cpp-cli#63 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
Sendspin/sendspin-cpp-cli#32 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 38/100
Sendspin/sendspin-cpp-cli#30 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
Sendspin/sendspin-cpp-cli の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
ROCm/rocm-systems#12638 ·
メンテナーはふだん 2 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
Ryan-Millard/Img2Num#652 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
lxqt/qtermwidget#688 ·
メンテナーはふだん 1 日以内に返信
-
CI enhancement tests
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
lfortran/lfortran#13885 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信