You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
DoP (DSD over PCM) output mode (--dop CLI flag, issue #80 requested by yama3kzh). For DACs that receive Diretta PCM but decode DoP natively (e.g., connected via I2S or S/PDIF passthrough at the target), --dop causes DSD streams to be transmitted over the Diretta PCM channel as standard 24-bit DoP frames instead of native Diretta DSD. Encoding: 2 DSD bytes per channel packed into a 24-bit PCM word with alternating 0x05/0xFA markers in the MSB byte (standard DoP v1.1 format). Sample rate mapping: DSD64→176.4kHz, DSD128→352.8kHz, DSD256→705.6kHz, DSD512→1.4MHz. Ring buffer operates in PCM mode (24-bit packed, silence=0x00); DoP marker state resets cleanly on each track start via clear(). No change to PCM or native DSD paths; the flag is a no-op when playing PCM. Format transition logic (DSD64→DSD128, DSD→PCM) uses the existing full close/reopen sequence since the underlying source format is still DSD.
--dop-msb flag (issue #80). For DACs whose DoP decoder expects MSB-first DSD payload in 24-bit DoP frames (rather than the LSB-first default from DSF files), --dop-msb enables intra-byte bit reversal of each DSD byte before packing into DoP frames. The reversal uses a static 256-entry lookup table in DirettaRingBuffer — zero branches and zero heap allocations in the audio hot path. --dop-msb implies --dop. Verbose mode prints the first few DoP pushes with raw DSD bytes for diagnosis. Also accessible as DOP=msb in the config file and as a third option in the web UI DSD Output Mode selector.
Fixed
DoP: silence during stabilisation uses plain PCM 0x00 for all modes (issue #80). getNewStream() fills all silence periods (prefill, stabilisation, underrun) with memset(0x00) in all modes, including DoP. The working reference path (MinimServer/Asset UPnP pre-encode DSF to DoP at the source, sending 176.4kHz PCM to DRUP with isDoPMode=false; DRUP sends PCM 0x00 silence and the DAC locks on the first real audio frame) uses no DoP markers during silence. A DoP-specific silence generator ([0x69, 0x69, marker] per channel with alternating markers, tracked by m_doPSilenceMarkerState) was tried in an earlier revision but did not resolve "music + constant hiss" on the SFORZATO DAC-01 or HOLO Audio targets — the pre-audio marker sequence appears to disturb these DACs' DSD demodulators before real audio starts. Reverted to plain memset(0x00) — m_doPSilenceMarkerState and its reset in configureRingPCM() have been removed. Verbose mode (--verbose) now logs the first 5 ring pops with hex-decoded marker and DSD bytes ([DoP POP #N]) for field diagnosis of DoP lock issues.
DoP: stabilisation silence count inflated 30× by wrong cycle-time formula (issue #80 — secondary contributor). The stabilisation in getNewStream() computed cycle time as efficientMTU / bytesPerSecond. For DoP at 176.4kHz (1056-byte buffers, MTU=1500): 1497 / 1,058,400 × 1e6 = 1.4 µs instead of ~1 ms, inflating the required buffer count to 70,000+ (capped at 3000) → ~3 s of silence instead of 100 ms. Fixed (commit 912a177) to use currentBytesPerBuffer. Same fix applied to the DSD branch.
DoP: even-frame invariant for ring pops and drift compensation (issue #80). Two defensive changes ensure the ring read position is always at a 0x05-aligned (even-indexed) DoP frame so any silence→audio transition resumes with a clean 0x05 marker: (1) pushDSDToDoP even-frame guard (if (pcmFrames % 2 != 0) pcmFrames--;, commit 209365f) — every push writes an even number of PCM frames, leaving m_dopMarkerState at false (0x05) after each push and keeping ring content always at even × 6 bytes; (2) 44.1k drift corrector in DoP mode — at 176.4kHz the accumulator receives 400 units/call (176400 % 1000 = 400) and fires at threshold to add an extra frame compensating for the non-integer 176.4 frames/ms; in DoP mode the threshold changes 1000→2000 and the bonus 1→2 frames, so every corrector firing adds 178 frames (even) instead of 177 (odd); average buffer rate is unchanged (2 frames / 2000 units = 1 frame / 1000 units). Together, all pops from the ring to the Diretta stream are even-frame counts, keeping the first real audio frame after any silence period at a predictable 0x05 marker. Unit test test_pushDSD_dop_marker_phase_invariant covers the push-side invariant.
DSD / DoP stabilisation divide-by-zero (issue #80). configureRingDSD() never stored m_sampleRate or m_bytesPerSample for DSD mode. When the first track is DSD, both were 0, causing a divide-by-zero in the DSD-branch cycle-time formula inside getNewStream(). Fixed by storing byteRate * 8 and 1 respectively at the end of configureRingDSD().
DoP: DSD byte pair packed in wrong order — root cause of "music + constant hiss" (commit 233b520, issue #80, confirmed by Dominique on HOLO Audio Spring 3 / DDC-0 and by yama3kzh on SFORZATO DAC-01 / Volumio Preciso). pushDSDToDoP() was writing each DoP 24-bit word as [DSD_byte_N, DSD_byte_N+1, marker] instead of the correct [DSD_byte_N+1, DSD_byte_N, marker]. Per DoP v1.1 the 24-bit PCM word carries bits[23:16]=marker, bits[15:8]=DSD_byte_N, bits[7:0]=DSD_byte_N+1; in little-endian memory this means byte[0]=DSD_N+1, byte[1]=DSD_N, byte[2]=marker — the opposite order from what was written. The framing was correct (DAC showed DSD2.8MHz / DoP lock detected), so the DAC accepted the stream; however the two DSD bytes within every frame were transposed, breaking the PDM correlation at byte boundaries and producing the characteristic "music audible + constant high-frequency hiss" symptom. Fix: a two-line swap in the pushDSDToDoP() output loop. This matches the MinimServer / Asset UPnP reference implementations. Note: after long sessions with the incorrect encoding, a Diretta target may enter a stale lock state; switching modes or restarting the target resets it.
CPU affinity: cores silently rejected as invalid on AMD Ryzen with HT/SMT disabled (reported by Kiran on a Ryzen 7730U running AudioLinux). sysconf(_SC_NPROCESSORS_ONLN) returns the count of online CPUs (8 on a Ryzen 7730U with HT off), which DRUP incorrectly used as the maximum valid CPU ID. With SMT disabled, Linux takes the odd-numbered logical CPUs offline and keeps the even-numbered ones active (0, 2, 4, ..., 14) — so cores 10, 12, 14 are perfectly valid but were rejected as >= 8. The validation silently cleared config.cpuAudio/Decode/Other, disabling DRUP's per-thread pthread_setaffinity_np calls entirely. Fix: new getOnlineCpus() helper reads /sys/devices/system/cpu/online and validates against actual set membership. Falls back to 0..N-1 if the file is unreadable. Same fix applied to slim2Diretta v1.4.10.
install.sh: FFmpeg check falsely reported [MISSING] mov — ffmpeg -demuxers in FFmpeg 8.x lists the QuickTime demuxer as mov,mp4,m4a,3gp,3g2,mj2 (alias group); the post-build check searched for " mov " (space on both sides) which never matches because the alias list is comma-separated. Fixed to grep -qE " ${dem}[ ,]". The demuxer was always compiled in; only the check message was wrong.
install.sh: FFmpeg decode test failed on every minimal build — the test used -f lavfi -i "sine=..." which requires the lavfi demuxer and sine source filter, neither of which is included in the minimal audio-only configure profile. Every minimal build would print a false-positive [WARNING] FFmpeg decode test failed. Replaced with a silent s16le byte-pipe that works with the minimal profile.
install.sh: WebUI profiles not refreshed after --build / option 3 — build_renderer() now detects whether the WebUI is already installed and copies the updated profile JSON files after each build.