Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Avoid Ogg symbol conflicts; rename m4a stream APIs

+84 -34
+2 -1
CLAUDE.md
··· 75 75 ``` 76 76 77 77 ### Zig 0.16.0 build.zig API notes 78 - - **Linker args**: use `exe.root_module.addLinkerArg("...")` — `exe.addLinkerArg` does **not** exist on `Build.Step.Compile` in 0.16.0; the method lives on `Build.Module`. 78 + - **Linker args (raw flags)**: **There is no `addLinkerArg` method** anywhere in Zig 0.16.0 — not on `Build.Step.Compile` and not on `Build.Module`. Passing raw flags like `--allow-multiple-definition` through `build.zig` is not possible. Solve linker conflicts at the source level instead (e.g. `objcopy --redefine-sym` in the build script). 79 79 - **Library/include paths**: `exe.root_module.addLibraryPath(...)`, `exe.root_module.addIncludePath(...)`. 80 80 - **System libraries**: `exe.root_module.linkSystemLibrary("name", .{})`. 81 81 - **Frameworks (macOS)**: `exe.root_module.linkFramework("Name", .{})`. 82 82 - **Object/archive files**: `exe.root_module.addObjectFile(b.path("..."))` — used for both `.o` and `.a`. 83 + - **Conditional linking**: use `if (condition) { ... }` around the `addObjectFile` / `linkSystemLibrary` calls directly in `build()` — there is no per-target feature-flag mechanism. 83 84 84 85 ## Runtime configuration 85 86
+5 -5
lib/rbcodec/codecs/libm4a/m4a.c
··· 49 49 if (stream->ci->curpos >= stream->ci->filesize) { stream->eof=1; } 50 50 } 51 51 52 - int32_t m4a_stream_read_int32(stream_t *stream) 52 + int32_t stream_read_int32(stream_t *stream) 53 53 { 54 54 int32_t v; 55 55 m4a_stream_read(stream, 4, &v); ··· 64 64 return stream->ci->curpos; 65 65 } 66 66 67 - uint32_t m4a_stream_read_uint32(stream_t *stream) 67 + uint32_t stream_read_uint32(stream_t *stream) 68 68 { 69 69 uint32_t v; 70 70 m4a_stream_read(stream, 4, &v); ··· 74 74 return v; 75 75 } 76 76 77 - uint16_t m4a_stream_read_uint16(stream_t *stream) 77 + uint16_t stream_read_uint16(stream_t *stream) 78 78 { 79 79 uint16_t v; 80 80 m4a_stream_read(stream, 2, &v); ··· 84 84 return v; 85 85 } 86 86 87 - uint8_t m4a_stream_read_uint8(stream_t *stream) 87 + uint8_t stream_read_uint8(stream_t *stream) 88 88 { 89 89 uint8_t v; 90 90 m4a_stream_read(stream, 1, &v); ··· 229 229 time_dur = tts_tab[time].sample_duration; 230 230 } 231 231 232 - offset += tsz_tab ? tsz_tab[i] : m4a_stream_read_uint32(stream); 232 + offset += tsz_tab ? tsz_tab[i] : stream_read_uint32(stream); 233 233 sound_sample_i += time_dur; 234 234 time_cnt--; 235 235 }
+47
scripts/build-headless.sh
··· 126 126 done 127 127 echo " Done — codec objects in $OBJECTS_DIR" 128 128 129 + echo "==> Step 2.6: Namespace ogg_* symbols in libopus.a to avoid ABI conflict with libtremor" 130 + # libopus bundles Ogg framing with a different ABI than libtremor (different 131 + # ogg_stream_state layout, extra copy_body arg on ogg_stream_pagein). 132 + # Rename every ogg_* symbol in libopus.a and in opus.o to libopus_ogg_* so 133 + # the Opus codec gets its own Ogg implementation and the Vorbis codec keeps 134 + # libtremor's — both correct, no lld duplicate-symbol errors on Linux. 135 + OBJCOPY="${LLVM_OBJCOPY:-objcopy}" 136 + LIBOPUS_A="$CODEC_DIR/libopus.a" 137 + OPUS_O="$OBJECTS_DIR/opus/opus.o" 138 + 139 + OGG_SYMS=( 140 + ogg_packet_clear 141 + ogg_page_bos ogg_page_checksum_set ogg_page_continued ogg_page_eos 142 + ogg_page_granulepos ogg_page_packets ogg_page_pageno ogg_page_serialno 143 + ogg_page_version 144 + ogg_stream_check ogg_stream_clear ogg_stream_destroy ogg_stream_eos 145 + ogg_stream_flush ogg_stream_flush_fill ogg_stream_init ogg_stream_iovecin 146 + ogg_stream_packetin ogg_stream_packetout ogg_stream_packetpeek 147 + ogg_stream_pagein ogg_stream_pageout ogg_stream_pageout_fill 148 + ogg_stream_reset ogg_stream_reset_serialno 149 + ogg_sync_buffer ogg_sync_check ogg_sync_clear ogg_sync_destroy 150 + ogg_sync_init ogg_sync_pageout ogg_sync_pageseek ogg_sync_reset 151 + ogg_sync_wrote 152 + ) 153 + 154 + REDEFINE_ARGS=() 155 + for sym in "${OGG_SYMS[@]}"; do 156 + REDEFINE_ARGS+=(--redefine-sym "${sym}=libopus_${sym}") 157 + # Mach-O symbols have a leading underscore; pass both forms. 158 + REDEFINE_ARGS+=(--redefine-sym "_${sym}=_libopus_${sym}") 159 + done 160 + 161 + # Extract all objects from libopus.a, rename, re-archive. 162 + TMP_LIBOPUS="$(mktemp -d)" 163 + (cd "$TMP_LIBOPUS" && ar x "$LIBOPUS_A") 164 + for obj in "$TMP_LIBOPUS"/*.o; do 165 + [ -f "$obj" ] && "$OBJCOPY" "${REDEFINE_ARGS[@]}" "$obj" 2>/dev/null || true 166 + done 167 + rm -f "$LIBOPUS_A" 168 + ar rcs "$LIBOPUS_A" "$TMP_LIBOPUS"/*.o 169 + rm -rf "$TMP_LIBOPUS" 170 + 171 + # Apply the same renames to the opus codec object so it calls libopus_ogg_*. 172 + [ -f "$OPUS_O" ] && "$OBJCOPY" "${REDEFINE_ARGS[@]}" "$OPUS_O" 2>/dev/null || true 173 + 174 + echo " Done — ${#OGG_SYMS[@]} ogg_* symbols namespaced in libopus.a + opus.o" 175 + 129 176 echo "==> Step 3: Build Rust crates (features: cpal-sink)" 130 177 cargo build $CARGO_FLAG --features cpal-sink -p rockbox-cli 131 178 cargo build $CARGO_FLAG -p rockbox-server
+30 -28
zig/build.zig
··· 116 116 if (headless) { 117 117 // cpal uses ALSA on Linux by default. 118 118 exe.root_module.linkSystemLibrary("asound", .{}); 119 - // libopus and libtremor both bundle identical Ogg framing symbols; 120 - // libspeex and libspeex-voice share bits.c symbols. lld errors on 121 - // duplicates while macOS ld64 silently picks first. 122 - exe.root_module.addLinkerArg("--allow-multiple-definition"); 123 119 } 124 120 } 125 121 126 122 const fw_dir = if (headless) "../build-headless" else "../build-lib"; 127 123 128 - const librockbox = b.path(b.pathJoin(&.{ fw_dir, "librockbox.a" })); 129 - const libfirmware = b.path(b.pathJoin(&.{ fw_dir, "firmware/libfirmware.a" })); 124 + const librockbox = b.path(b.pathJoin(&.{ fw_dir, "librockbox.a" })); 125 + const libfirmware = b.path(b.pathJoin(&.{ fw_dir, "firmware/libfirmware.a" })); 130 126 const libfixedpoint = b.path(b.pathJoin(&.{ fw_dir, "lib/libfixedpoint.a" })); 131 - const librbcodec = b.path(b.pathJoin(&.{ fw_dir, "lib/librbcodec.a" })); 127 + const librbcodec = b.path(b.pathJoin(&.{ fw_dir, "lib/librbcodec.a" })); 132 128 const libskin_parser = b.path(b.pathJoin(&.{ fw_dir, "lib/libskin_parser.a" })); 133 - const libtlsf = b.path(b.pathJoin(&.{ fw_dir, "lib/libtlsf.a" })); 134 - const libspeex_voice = b.path(b.pathJoin(&.{ fw_dir, "lib/rbcodec/codecs/libspeex-voice.a" })); 135 - const librockbox_cli = b.path("../target/release/librockbox_cli.a"); 129 + const libtlsf = b.path(b.pathJoin(&.{ fw_dir, "lib/libtlsf.a" })); 130 + const librockbox_cli = b.path("../target/release/librockbox_cli.a"); 136 131 const librockbox_server = b.path("../target/release/librockbox_server.a"); 137 132 138 133 exe.root_module.addObjectFile(librockbox); ··· 141 136 exe.root_module.addObjectFile(libskin_parser); 142 137 exe.root_module.addObjectFile(librbcodec); 143 138 exe.root_module.addObjectFile(libtlsf); 144 - exe.root_module.addObjectFile(libspeex_voice); 139 + // libspeex-voice is only needed for SDL (voice/TTS UI); the headless build 140 + // uses libspeex.a via lib_names instead — linking both causes duplicate symbols. 141 + if (!headless) { 142 + const libspeex_voice = b.path(b.pathJoin(&.{ fw_dir, "lib/rbcodec/codecs/libspeex-voice.a" })); 143 + exe.root_module.addObjectFile(libspeex_voice); 144 + } 145 145 exe.root_module.addObjectFile(librockbox_cli); 146 146 exe.root_module.addObjectFile(librockbox_server); 147 147 ··· 158 158 // scripts/build-headless.sh Step 2.5 extracts these files into 159 159 // <fw_dir>/lib/rbcodec/codecs/codec-objects/<name>/ 160 160 const codec_names = [_][]const u8{ 161 - "a52", "a52_rm", "aac", "aac_bsf", 162 - "adx", "aiff", "alac", "ape", 163 - "atrac3_oma", "atrac3_rm", "au", "cook", 164 - "flac", "mod", "mpa", "mpc", 165 - "opus", "raac", "shorten", "smaf", 166 - "speex", "tta", "vorbis", "vox", 167 - "wav", "wav64", "wavpack", "wma", 161 + "a52", "a52_rm", "aac", "aac_bsf", 162 + "adx", "aiff", "alac", "ape", 163 + "atrac3_oma", "atrac3_rm", "au", "cook", 164 + "flac", "mod", "mpa", "mpc", 165 + "opus", "raac", "shorten", "smaf", 166 + "speex", "tta", "vorbis", "vox", 167 + "wav", "wav64", "wavpack", "wma", 168 168 "wmapro", 169 169 }; 170 170 const codec_dir = b.pathJoin(&.{ fw_dir, "lib/rbcodec/codecs" }); 171 - const obj_base = b.pathJoin(&.{ codec_dir, "codec-objects" }); 171 + const obj_base = b.pathJoin(&.{ codec_dir, "codec-objects" }); 172 172 for (codec_names) |name| { 173 173 const dir = b.pathJoin(&.{ obj_base, name }); 174 - exe.root_module.addObjectFile(b.path(b.pathJoin(&.{ dir, b.fmt("{s}.o", .{name}) }))); 174 + exe.root_module.addObjectFile(b.path(b.pathJoin(&.{ dir, b.fmt("{s}.o", .{name}) }))); 175 175 exe.root_module.addObjectFile(b.path(b.pathJoin(&.{ dir, b.fmt("{s}-crt0.o", .{name}) }))); 176 176 } 177 177 ··· 184 184 // Passed as archives (lazy scanning) because code references from the 185 185 // directly-linked codec .o files drive archive member inclusion correctly. 186 186 const lib_names = [_][]const u8{ 187 - "liba52", "libalac", "libasap", "libasf", 188 - "libatrac", "libcook", "libdemac", "libfaad", 189 - "libffmpegFLAC", "libm4a", "libmad", "libmusepack", 190 - "libopus", "libpcm", "librm", "libspc", 191 - "libspeex", "libtremor", "libtta", "libwavpack", 192 - "libwma", "libwmapro", 187 + "liba52", "libalac", "libasap", "libasf", 188 + "libatrac", "libcook", "libdemac", "libfaad", 189 + "libffmpegFLAC", "libm4a", "libmad", "libmusepack", 190 + "libopus", "libpcm", "librm", "libspc", 191 + "libspeex", "libtremor", "libtta", "libwavpack", 192 + "libwma", "libwmapro", 193 193 }; 194 194 for (lib_names) |lib| { 195 195 exe.root_module.addObjectFile(b.path(b.pathJoin(&.{ codec_dir, b.fmt("{s}.a", .{lib}) }))); 196 196 } 197 - // libogg symbols are compiled with -fvisibility=hidden; they become 198 - // private-external in Mach-O and do not conflict across archives. 197 + // libopus and libtremor both bundle Ogg framing but with incompatible ABIs 198 + // (different ogg_stream_state layout, different ogg_stream_pagein signature). 199 + // build-headless.sh Step 2.6 renames all ogg_* symbols in libopus.a and 200 + // opus.o to libopus_ogg_* so each codec gets its own implementation. 199 201 } else { 200 202 exe.root_module.linkSystemLibrary("SDL2", .{}); 201 203 }