From 43a7997944b61ae87c383cae2b95f63f54b3f5b6 Mon Sep 17 00:00:00 2001 From: rolandnsharp Date: Sun, 5 Apr 2026 19:39:44 +1000 Subject: [PATCH] Remove demo signal generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audio capture only — shows NO SIGNAL when libav can't connect. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/osc.nim | 2 - src/osc/audio.nim | 129 ++++++++++++++++------------------------------ 2 files changed, 44 insertions(+), 87 deletions(-) diff --git a/src/osc.nim b/src/osc.nim index 43506f9..f66bcbb 100644 --- a/src/osc.nim +++ b/src/osc.nim @@ -66,8 +66,6 @@ proc main() = of gsGrid: gsCross of gsCross: gsOff of gsOff: gsGrid - of Key.D: - audio.cyclePreset() else: discard diff --git a/src/osc/audio.nim b/src/osc/audio.nim index bd3a5b5..86a03d8 100644 --- a/src/osc/audio.nim +++ b/src/osc/audio.nim @@ -1,7 +1,6 @@ -## Audio capture via libavdevice/libavformat (dlopen at runtime), -## with fallback to demo signal. +## Audio capture via libavdevice/libavformat (dlopen at runtime). -import osproc, strutils, math +import osproc, strutils import scope # ── libav C helper bindings ────────────────────────────────────────── @@ -10,8 +9,8 @@ import scope {.passL: "-ldl".} type - AVFormatContext = object # opaque, only used as pointer - AVPacket = object # opaque, only used as pointer + AVFormatContext = object + AVPacket = object proc av_helper_init(): cint {.importc, cdecl.} proc av_helper_open_pulse(ctx: ptr ptr AVFormatContext, @@ -46,102 +45,62 @@ proc findMonitorSource(): string = except: discard "" -# ── Audio capture types ────────────────────────────────────────────── +# ── Audio capture ──────────────────────────────────────────────────── type - AudioMode* = enum - amLive ## Direct libav capture - amDemo ## Built-in synthesized waveforms - AudioCapture* = object - mode*: AudioMode fmtCtx: ptr AVFormatContext packet: ptr AVPacket streamIdx: cint - phase: float - demoFreqL*, demoFreqR*: float - demoPreset*: int - -# ── Start / stop ───────────────────────────────────────────────────── + live*: bool proc startAudio*(): AudioCapture = let monitor = findMonitorSource() - if monitor.len > 0: - block libav: - if av_helper_init() < 0: break libav - var ctx: ptr AVFormatContext = nil - if av_helper_open_pulse(addr ctx, monitor.cstring) < 0: break libav - if av_helper_find_stream_info(ctx) < 0: - av_helper_close(addr ctx) - break libav - let idx = av_helper_find_audio_stream(ctx) - let pkt = av_helper_packet_alloc() - if pkt != nil: - return AudioCapture( - mode: amLive, fmtCtx: ctx, packet: pkt, - streamIdx: idx.cint, - demoFreqL: 440.0, demoFreqR: 330.0) - av_helper_close(addr ctx) + if monitor.len == 0: return - AudioCapture(mode: amDemo, demoFreqL: 440.0, demoFreqR: 330.0) + if av_helper_init() < 0: return + + var ctx: ptr AVFormatContext = nil + if av_helper_open_pulse(addr ctx, monitor.cstring) < 0: return + if av_helper_find_stream_info(ctx) < 0: + av_helper_close(addr ctx) + return + + let idx = av_helper_find_audio_stream(ctx) + let pkt = av_helper_packet_alloc() + if pkt == nil: + av_helper_close(addr ctx) + return + + AudioCapture(fmtCtx: ctx, packet: pkt, streamIdx: idx.cint, live: true) proc stop*(cap: var AudioCapture) = - if cap.mode == amLive: + if cap.live: if cap.packet != nil: av_helper_packet_free(addr cap.packet) if cap.fmtCtx != nil: av_helper_close(addr cap.fmtCtx) proc sourceLabel*(cap: AudioCapture): string = - if cap.mode == amLive: "LIVE" else: "DEMO" - -# ── Preset cycling ─────────────────────────────────────────────────── - -proc cyclePreset*(cap: var AudioCapture) = - if cap.mode != amDemo: return - cap.demoPreset = (cap.demoPreset + 1) mod 4 - case cap.demoPreset - of 0: cap.demoFreqL = 440.0; cap.demoFreqR = 330.0 # 4:3 - of 1: cap.demoFreqL = 440.0; cap.demoFreqR = 440.0 # 1:1 - of 2: cap.demoFreqL = 440.0; cap.demoFreqR = 220.0 # 2:1 - of 3: cap.demoFreqL = 440.0; cap.demoFreqR = 293.3 # 3:2 - else: discard - -# ── Sample reading ─────────────────────────────────────────────────── + if cap.live: "LIVE" else: "NO SIGNAL" proc readSamples*(cap: var AudioCapture, scope: var Scope) = - case cap.mode - of amLive: - const frameSize = 4 # 2ch × 16-bit - var totalSamples = 0 - while totalSamples < scope.samplesL.len: - let ret = av_helper_read_frame(cap.fmtCtx, cap.packet) - if ret < 0: break - if av_helper_packet_stream(cap.packet) == cap.streamIdx: - let data = av_helper_packet_data(cap.packet) - let size = av_helper_packet_size(cap.packet) - let frames = size div frameSize - for i in 0..= scope.samplesL.len: break - let off = i * frameSize - let left = cast[int16]((data[off + 1].uint16 shl 8) or data[off].uint16) - let right = cast[int16]((data[off + 3].uint16 shl 8) or data[off + 2].uint16) - scope.samplesL[totalSamples] = left.float / 32768.0 - scope.samplesR[totalSamples] = right.float / 32768.0 - totalSamples += 1 - av_helper_packet_unref(cap.packet) - if totalSamples > 0: break - scope.sampleCount = totalSamples + if not cap.live: return - of amDemo: - scope.sampleCount = scope.samplesL.len - let cycles = 3.0 / scope.timeDiv - let drift = sin(cap.phase * 0.3) * 0.1 - let rL = cap.demoFreqL / 440.0 - let rR = cap.demoFreqR / 440.0 - for i in 0..= scope.samplesL.len: break + let off = i * frameSize + let left = cast[int16]((data[off + 1].uint16 shl 8) or data[off].uint16) + let right = cast[int16]((data[off + 3].uint16 shl 8) or data[off + 2].uint16) + scope.samplesL[total] = left.float / 32768.0 + scope.samplesR[total] = right.float / 32768.0 + total += 1 + av_helper_packet_unref(cap.packet) + if total > 0: break + scope.sampleCount = total