Remove demo signal generator

Audio capture only — shows NO SIGNAL when libav can't connect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
rolandnsharp
2026-04-05 19:39:44 +10:00
parent fb3a0cb77b
commit 43a7997944
2 changed files with 44 additions and 87 deletions

View File

@@ -66,8 +66,6 @@ proc main() =
of gsGrid: gsCross of gsGrid: gsCross
of gsCross: gsOff of gsCross: gsOff
of gsOff: gsGrid of gsOff: gsGrid
of Key.D:
audio.cyclePreset()
else: else:
discard discard

View File

@@ -1,7 +1,6 @@
## Audio capture via libavdevice/libavformat (dlopen at runtime), ## Audio capture via libavdevice/libavformat (dlopen at runtime).
## with fallback to demo signal.
import osproc, strutils, math import osproc, strutils
import scope import scope
# ── libav C helper bindings ────────────────────────────────────────── # ── libav C helper bindings ──────────────────────────────────────────
@@ -10,8 +9,8 @@ import scope
{.passL: "-ldl".} {.passL: "-ldl".}
type type
AVFormatContext = object # opaque, only used as pointer AVFormatContext = object
AVPacket = object # opaque, only used as pointer AVPacket = object
proc av_helper_init(): cint {.importc, cdecl.} proc av_helper_init(): cint {.importc, cdecl.}
proc av_helper_open_pulse(ctx: ptr ptr AVFormatContext, proc av_helper_open_pulse(ctx: ptr ptr AVFormatContext,
@@ -46,102 +45,62 @@ proc findMonitorSource(): string =
except: discard except: discard
"" ""
# ── Audio capture types ────────────────────────────────────────────── # ── Audio capture ────────────────────────────────────────────────────
type type
AudioMode* = enum
amLive ## Direct libav capture
amDemo ## Built-in synthesized waveforms
AudioCapture* = object AudioCapture* = object
mode*: AudioMode
fmtCtx: ptr AVFormatContext fmtCtx: ptr AVFormatContext
packet: ptr AVPacket packet: ptr AVPacket
streamIdx: cint streamIdx: cint
phase: float live*: bool
demoFreqL*, demoFreqR*: float
demoPreset*: int
# ── Start / stop ─────────────────────────────────────────────────────
proc startAudio*(): AudioCapture = proc startAudio*(): AudioCapture =
let monitor = findMonitorSource() let monitor = findMonitorSource()
if monitor.len > 0: if monitor.len == 0: return
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)
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) = 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.packet != nil: av_helper_packet_free(addr cap.packet)
if cap.fmtCtx != nil: av_helper_close(addr cap.fmtCtx) if cap.fmtCtx != nil: av_helper_close(addr cap.fmtCtx)
proc sourceLabel*(cap: AudioCapture): string = proc sourceLabel*(cap: AudioCapture): string =
if cap.mode == amLive: "LIVE" else: "DEMO" if cap.live: "LIVE" else: "NO SIGNAL"
# ── 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 ───────────────────────────────────────────────────
proc readSamples*(cap: var AudioCapture, scope: var Scope) = proc readSamples*(cap: var AudioCapture, scope: var Scope) =
case cap.mode if not cap.live: return
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..<frames:
if totalSamples >= 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
of amDemo: const frameSize = 4 # 2ch × 16-bit
scope.sampleCount = scope.samplesL.len var total = 0
let cycles = 3.0 / scope.timeDiv while total < scope.samplesL.len:
let drift = sin(cap.phase * 0.3) * 0.1 let ret = av_helper_read_frame(cap.fmtCtx, cap.packet)
let rL = cap.demoFreqL / 440.0 if ret < 0: break
let rR = cap.demoFreqR / 440.0 if av_helper_packet_stream(cap.packet) == cap.streamIdx:
for i in 0..<scope.sampleCount: let data = av_helper_packet_data(cap.packet)
let t = cap.phase + (i.float / scope.sampleCount.float) * cycles * 2.0 * PI let size = av_helper_packet_size(cap.packet)
scope.samplesL[i] = sin(t * rL) * 0.7 + for i in 0..<(size div frameSize):
sin(t * rL * 2.0) * 0.15 + if total >= scope.samplesL.len: break
sin(t * rL * 3.0 + drift) * 0.08 let off = i * frameSize
scope.samplesR[i] = sin(t * rR + 0.5) * 0.7 + let left = cast[int16]((data[off + 1].uint16 shl 8) or data[off].uint16)
sin(t * rR * 2.0 + 0.3) * 0.2 let right = cast[int16]((data[off + 3].uint16 shl 8) or data[off + 2].uint16)
cap.phase += 0.05 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