Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bee679d3e1 | ||
|
|
0660130653 | ||
| ff639015c2 | |||
| 5d969888fc |
34
README.md
34
README.md
@@ -57,22 +57,30 @@ sudo ln -s $(pwd)/osc /usr/local/bin/osc
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit the constants at the top of `src/osc.nim` or `src/osc_braille.nim`:
|
||||
Runtime options can be passed via CLI flags:
|
||||
|
||||
```nim
|
||||
const
|
||||
# Phosphor physics
|
||||
Decay = 0.85 # persistence per frame (0.0–1.0)
|
||||
Beam = 0.4 # intensity at beam impact
|
||||
Bloom = 0.08 # horizontal glow spread
|
||||
```bash
|
||||
./osc --palette:amber --decay:0.92 --beam:0.6 --bloom:0.12
|
||||
./osc_braille --palette:green --hot:0.8 --warm:0.45 --cool:0.12
|
||||
```
|
||||
|
||||
# Phosphor glow thresholds
|
||||
HotGlow = 0.7 # white-hot beam core
|
||||
WarmGlow = 0.4 # bright phosphor
|
||||
CoolGlow = 0.15 # dim persistence trail
|
||||
Available options:
|
||||
|
||||
# Palette: green, amber, cyan, blue, white, red
|
||||
Palette = "green"
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `-p`, `--palette:NAME` | Palette name | `green` |
|
||||
| `-d`, `--decay:FLOAT` | Phosphor persistence per frame | `0.85` |
|
||||
| `--beam:FLOAT` | Beam impact intensity | `0.4` |
|
||||
| `--bloom:FLOAT` | Horizontal glow spread | `0.08` |
|
||||
| `--hot:FLOAT` | White-hot beam core threshold | `0.7` |
|
||||
| `--warm:FLOAT` | Bright phosphor threshold | `0.4` |
|
||||
| `--cool:FLOAT` | Dim persistence trail threshold | `0.15` |
|
||||
|
||||
Show help:
|
||||
|
||||
```bash
|
||||
./osc --help
|
||||
./osc_braille --help
|
||||
```
|
||||
|
||||
### Palettes
|
||||
|
||||
228
src/osc.nim
228
src/osc.nim
@@ -1,28 +1,83 @@
|
||||
## Terminal oscilloscope with CRT phosphor physics.
|
||||
## Zero dependencies beyond Nim stdlib + libav (dlopen at runtime).
|
||||
|
||||
import os, strutils
|
||||
import os, strutils, parseopt
|
||||
import posix/termios as ptermios
|
||||
from posix import read
|
||||
import osc/canvas/[term, effects]
|
||||
import osc/[scope, audio]
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────
|
||||
# Edit these to tune the look.
|
||||
|
||||
const
|
||||
# Phosphor physics
|
||||
Decay = 0.85 # persistence per frame (0.0–1.0)
|
||||
Beam = 0.4 # intensity at beam impact
|
||||
Bloom = 0.08 # horizontal glow spread
|
||||
type Config = object
|
||||
decay: float
|
||||
beam: float
|
||||
bloom: float
|
||||
hotGlow: float
|
||||
warmGlow: float
|
||||
coolGlow: float
|
||||
palette: string
|
||||
|
||||
# Phosphor glow thresholds
|
||||
HotGlow = 0.7 # white-hot beam core
|
||||
WarmGlow = 0.4 # bright phosphor
|
||||
CoolGlow = 0.15 # dim persistence trail
|
||||
proc defaultConfig(): Config =
|
||||
Config(
|
||||
decay: 0.85,
|
||||
beam: 0.4,
|
||||
bloom: 0.08,
|
||||
hotGlow: 0.7,
|
||||
warmGlow: 0.4,
|
||||
coolGlow: 0.15,
|
||||
palette: "green"
|
||||
)
|
||||
|
||||
# Palette: green, amber, cyan, blue, white, red
|
||||
Palette = "green"
|
||||
proc usage() =
|
||||
echo """
|
||||
Terminal oscilloscope
|
||||
|
||||
Options:
|
||||
-p, --palette:NAME Palette name, default: green
|
||||
-d, --decay:FLOAT Phosphor decay, default: 0.85
|
||||
--beam:FLOAT Beam intensity, default: 0.4
|
||||
--bloom:FLOAT Bloom intensity, default: 0.08
|
||||
--hot:FLOAT Hot glow, default: 0.7
|
||||
--warm:FLOAT Warm glow, default: 0.4
|
||||
--cool:FLOAT Cool glow, default: 0.15
|
||||
-h, --help Show this help
|
||||
|
||||
Palettes:
|
||||
green, amber, cyan, blue, white, red
|
||||
|
||||
Example:
|
||||
./osc --palette:amber --decay:0.92 --beam:0.6 --bloom:0.12
|
||||
"""
|
||||
|
||||
proc parseConfig(): Config =
|
||||
result = defaultConfig()
|
||||
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "h", "help":
|
||||
usage()
|
||||
quit 0
|
||||
of "palette", "p":
|
||||
result.palette = val
|
||||
of "decay", "d":
|
||||
result.decay = parseFloat(val)
|
||||
of "beam":
|
||||
result.beam = parseFloat(val)
|
||||
of "bloom":
|
||||
result.bloom = parseFloat(val)
|
||||
of "hot":
|
||||
result.hotGlow = parseFloat(val)
|
||||
of "warm":
|
||||
result.warmGlow = parseFloat(val)
|
||||
of "cool":
|
||||
result.coolGlow = parseFloat(val)
|
||||
else:
|
||||
quit "Unknown option: " & key
|
||||
else:
|
||||
discard
|
||||
|
||||
# ── Audio thread via Channel ─────────────────────────────────────────
|
||||
|
||||
@@ -66,94 +121,171 @@ proc readKey(): char =
|
||||
|
||||
# ── Phosphor ─────────────────────────────────────────────────────────
|
||||
|
||||
proc plotDot(c: var Canvas, fx, fy: float) =
|
||||
let x = int(fx); let y = int(fy)
|
||||
c.addPixel(x, y, Beam)
|
||||
c.addPixel(x - 1, y, Bloom)
|
||||
c.addPixel(x + 1, y, Bloom)
|
||||
proc plotDot(c: var Canvas, fx, fy: float, cfg: Config) =
|
||||
let x = int(fx)
|
||||
let y = int(fy)
|
||||
|
||||
proc plotLine(c: var Canvas, x0, y0, x1, y1: float) =
|
||||
c.addPixel(x, y, cfg.beam)
|
||||
c.addPixel(x - 1, y, cfg.bloom)
|
||||
c.addPixel(x + 1, y, cfg.bloom)
|
||||
|
||||
proc plotLine(
|
||||
c: var Canvas,
|
||||
x0, y0, x1, y1: float,
|
||||
cfg: Config
|
||||
) =
|
||||
let steps = max(int(max(abs(x1 - x0), abs(y1 - y0))), 1)
|
||||
|
||||
for i in 0..steps:
|
||||
let t = i.float / steps.float
|
||||
c.plotDot(x0 + (x1 - x0) * t, y0 + (y1 - y0) * t)
|
||||
|
||||
proc renderTrace(c: var Canvas, scope: Scope) =
|
||||
if scope.sampleCount < 2: return
|
||||
let w = c.pixW; let h = c.pixH
|
||||
let cy = h.float / 2.0; let gain = scope.gain
|
||||
c.plotDot(
|
||||
x0 + (x1 - x0) * t,
|
||||
y0 + (y1 - y0) * t,
|
||||
cfg
|
||||
)
|
||||
|
||||
proc renderTrace(c: var Canvas, scope: Scope, cfg: Config) =
|
||||
if scope.sampleCount < 2:
|
||||
return
|
||||
|
||||
let w = c.pixW
|
||||
let h = c.pixH
|
||||
let cy = h.float / 2.0
|
||||
let gain = scope.gain
|
||||
|
||||
case scope.mode
|
||||
of ModeYT:
|
||||
let visible = max(int(scope.sampleCount.float / scope.timeDiv), 2)
|
||||
var px, py: float; var first = true
|
||||
|
||||
var px, py: float
|
||||
var first = true
|
||||
|
||||
for col in 0..<w:
|
||||
let s = min((col * visible) div w, scope.sampleCount - 1)
|
||||
let x = col.float; let y = cy - scope.samplesL[s] * gain * cy * 0.5
|
||||
if first: c.plotDot(x, y) else: c.plotLine(px, py, x, y)
|
||||
first = false; px = x; py = y
|
||||
let x = col.float
|
||||
let y = cy - scope.samplesL[s] * gain * cy * 0.5
|
||||
|
||||
if first:
|
||||
c.plotDot(x, y, cfg)
|
||||
else:
|
||||
c.plotLine(px, py, x, y, cfg)
|
||||
|
||||
first = false
|
||||
px = x
|
||||
py = y
|
||||
|
||||
of ModeXY:
|
||||
var px, py: float; var first = true
|
||||
var px, py: float
|
||||
var first = true
|
||||
|
||||
let step = max(scope.sampleCount div 1024, 1)
|
||||
|
||||
for i in countup(0, scope.sampleCount - 1, step):
|
||||
let x = (1.0 + scope.samplesL[i] * gain * 0.5) * w.float / 2.0
|
||||
let y = (1.0 - scope.samplesR[i] * gain * 0.5) * h.float / 2.0
|
||||
if first: c.plotDot(x, y) else: c.plotLine(px, py, x, y)
|
||||
first = false; px = x; py = y
|
||||
|
||||
if first:
|
||||
c.plotDot(x, y, cfg)
|
||||
else:
|
||||
c.plotLine(px, py, x, y, cfg)
|
||||
|
||||
first = false
|
||||
px = x
|
||||
py = y
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────
|
||||
|
||||
proc main() =
|
||||
let cfg = parseConfig()
|
||||
|
||||
initTerm()
|
||||
setRawMode()
|
||||
|
||||
var w = termWidth(); var h = termHeight()
|
||||
var c = newCanvas(w, h, Palette, [HotGlow, WarmGlow, CoolGlow])
|
||||
var w = termWidth()
|
||||
var h = termHeight()
|
||||
|
||||
var c = newCanvas(
|
||||
w,
|
||||
h,
|
||||
cfg.palette,
|
||||
[cfg.hotGlow, cfg.warmGlow, cfg.coolGlow]
|
||||
)
|
||||
|
||||
crtTurnOn(c)
|
||||
|
||||
var scope = initScope(w, h)
|
||||
var aud = startAudio()
|
||||
var running = true
|
||||
|
||||
audioChan.open()
|
||||
audioRunning = true
|
||||
|
||||
var aThread: Thread[ptr AudioCapture]
|
||||
createThread(aThread, audioThread, addr aud)
|
||||
|
||||
while running:
|
||||
let nw = termWidth(); let nh = termHeight()
|
||||
let nw = termWidth()
|
||||
let nh = termHeight()
|
||||
|
||||
if nw != w or nh != h:
|
||||
w = nw; h = nh; c.resize(w, h); scope.resize(w, h)
|
||||
w = nw
|
||||
h = nh
|
||||
c.resize(w, h)
|
||||
scope.resize(w, h)
|
||||
|
||||
let got = audioChan.tryRecv()
|
||||
|
||||
if got.dataAvailable:
|
||||
scope.sampleCount = got.msg.count
|
||||
|
||||
for i in 0..<got.msg.count:
|
||||
scope.samplesL[i] = got.msg.samples[i][0]
|
||||
scope.samplesR[i] = got.msg.samples[i][1]
|
||||
|
||||
c.decayPixels(Decay)
|
||||
c.renderTrace(scope)
|
||||
c.decayPixels(cfg.decay)
|
||||
c.renderTrace(scope, cfg)
|
||||
|
||||
let hud =
|
||||
" " &
|
||||
(if scope.mode == ModeYT: "Y-T" else: "X-Y") &
|
||||
" G:" &
|
||||
scope.gain.formatFloat(ffDecimal, 1) &
|
||||
" "
|
||||
|
||||
let hud = " " & (if scope.mode == ModeYT: "Y-T" else: "X-Y") &
|
||||
" G:" & scope.gain.formatFloat(ffDecimal, 1) & " "
|
||||
let help = " m:mode +/-:gain [/]:time q:quit "
|
||||
c.flush([(1, 0, tNormal, hud),
|
||||
(w - help.len - 1, h - 1, tDim, help)])
|
||||
|
||||
c.flush([
|
||||
(1, 0, tNormal, hud),
|
||||
(w - help.len - 1, h - 1, tDim, help)
|
||||
])
|
||||
|
||||
sleep(16)
|
||||
|
||||
case readKey()
|
||||
of 'q', '\x1b': running = false
|
||||
of 'm': scope.mode = if scope.mode == ModeYT: ModeXY else: ModeYT
|
||||
of '+', '=': scope.gain = min(scope.gain * 1.3, 20.0)
|
||||
of '-': scope.gain = max(scope.gain / 1.3, 0.5)
|
||||
of ']': scope.timeDiv = min(scope.timeDiv * 1.5, 16.0)
|
||||
of '[': scope.timeDiv = max(scope.timeDiv / 1.5, 0.25)
|
||||
else: discard
|
||||
of 'q', '\x1b':
|
||||
running = false
|
||||
of 'm':
|
||||
scope.mode =
|
||||
if scope.mode == ModeYT:
|
||||
ModeXY
|
||||
else:
|
||||
ModeYT
|
||||
of '+', '=':
|
||||
scope.gain = min(scope.gain * 1.3, 20.0)
|
||||
of '-':
|
||||
scope.gain = max(scope.gain / 1.3, 0.5)
|
||||
of ']':
|
||||
scope.timeDiv = min(scope.timeDiv * 1.5, 16.0)
|
||||
of '[':
|
||||
scope.timeDiv = max(scope.timeDiv / 1.5, 0.25)
|
||||
else:
|
||||
discard
|
||||
|
||||
audioRunning = false
|
||||
joinThread(aThread)
|
||||
audioChan.close()
|
||||
|
||||
aud.stop()
|
||||
crtTurnOff(c)
|
||||
restoreMode()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Terminal oscilloscope — braille dot rendering (4× resolution).
|
||||
|
||||
import os, strutils
|
||||
import os, strutils, parseopt
|
||||
import posix/termios as ptermios
|
||||
from posix import read
|
||||
import osc/canvas/[braille, term, effects]
|
||||
@@ -8,14 +8,72 @@ import osc/[scope, audio]
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────
|
||||
|
||||
const
|
||||
Decay = 0.85
|
||||
Beam = 0.4
|
||||
Bloom = 0.08
|
||||
HotGlow = 0.7
|
||||
WarmGlow = 0.4
|
||||
CoolGlow = 0.15
|
||||
Palette = "green"
|
||||
type Config = object
|
||||
decay: float
|
||||
beam: float
|
||||
bloom: float
|
||||
hotGlow: float
|
||||
warmGlow: float
|
||||
coolGlow: float
|
||||
palette: string
|
||||
|
||||
proc defaultConfig(): Config =
|
||||
Config(
|
||||
decay: 0.85,
|
||||
beam: 0.4,
|
||||
bloom: 0.08,
|
||||
hotGlow: 0.7,
|
||||
warmGlow: 0.4,
|
||||
coolGlow: 0.15,
|
||||
palette: "green"
|
||||
)
|
||||
|
||||
proc usage() =
|
||||
echo """
|
||||
Terminal oscilloscope
|
||||
|
||||
Options:
|
||||
-p, --palette:NAME Palette name, default: green
|
||||
-d, --decay:FLOAT Phosphor decay, default: 0.85
|
||||
--beam:FLOAT Beam intensity, default: 0.4
|
||||
--bloom:FLOAT Bloom intensity, default: 0.08
|
||||
--hot:FLOAT Hot glow, default: 0.7
|
||||
--warm:FLOAT Warm glow, default: 0.4
|
||||
--cool:FLOAT Cool glow, default: 0.15
|
||||
-h, --help Show this help
|
||||
|
||||
Example:
|
||||
./osc_braille --palette:amber --decay:0.92 --beam:0.6 --bloom:0.12
|
||||
"""
|
||||
|
||||
proc parseConfig(): Config =
|
||||
result = defaultConfig()
|
||||
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "h", "help":
|
||||
usage()
|
||||
quit 0
|
||||
of "palette", "p":
|
||||
result.palette = val
|
||||
of "decay", "d":
|
||||
result.decay = parseFloat(val)
|
||||
of "beam":
|
||||
result.beam = parseFloat(val)
|
||||
of "bloom":
|
||||
result.bloom = parseFloat(val)
|
||||
of "hot":
|
||||
result.hotGlow = parseFloat(val)
|
||||
of "warm":
|
||||
result.warmGlow = parseFloat(val)
|
||||
of "cool":
|
||||
result.coolGlow = parseFloat(val)
|
||||
else:
|
||||
quit "Unknown option: " & key
|
||||
else:
|
||||
discard
|
||||
|
||||
# ── Audio thread ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -59,95 +117,173 @@ proc readKey(): char =
|
||||
|
||||
# ── Phosphor ─────────────────────────────────────────────────────────
|
||||
|
||||
proc plotDot(c: var BrailleCanvas, fx, fy: float) =
|
||||
let x = int(fx); let y = int(fy)
|
||||
c.addPixel(x, y, Beam)
|
||||
c.addPixel(x - 1, y, Bloom)
|
||||
c.addPixel(x + 1, y, Bloom)
|
||||
proc plotDot(c: var BrailleCanvas, fx, fy: float, cfg: Config) =
|
||||
let x = int(fx)
|
||||
let y = int(fy)
|
||||
c.addPixel(x, y, cfg.beam)
|
||||
c.addPixel(x - 1, y, cfg.bloom)
|
||||
c.addPixel(x + 1, y, cfg.bloom)
|
||||
|
||||
proc plotLine(c: var BrailleCanvas, x0, y0, x1, y1: float) =
|
||||
proc plotLine(
|
||||
c: var BrailleCanvas,
|
||||
x0, y0, x1, y1: float,
|
||||
cfg: Config
|
||||
) =
|
||||
let steps = max(int(max(abs(x1 - x0), abs(y1 - y0))), 1)
|
||||
for i in 0..steps:
|
||||
let t = i.float / steps.float
|
||||
c.plotDot(x0 + (x1 - x0) * t, y0 + (y1 - y0) * t)
|
||||
c.plotDot(
|
||||
x0 + (x1 - x0) * t,
|
||||
y0 + (y1 - y0) * t,
|
||||
cfg
|
||||
)
|
||||
|
||||
proc renderTrace(c: var BrailleCanvas, scope: Scope) =
|
||||
if scope.sampleCount < 2: return
|
||||
let w = c.pixW; let h = c.pixH
|
||||
let cy = h.float / 2.0; let gain = scope.gain
|
||||
proc renderTrace(c: var BrailleCanvas, scope: Scope, cfg: Config) =
|
||||
if scope.sampleCount < 2:
|
||||
return
|
||||
|
||||
let w = c.pixW
|
||||
let h = c.pixH
|
||||
let cy = h.float / 2.0
|
||||
let gain = scope.gain
|
||||
|
||||
case scope.mode
|
||||
of ModeYT:
|
||||
let visible = max(int(scope.sampleCount.float / scope.timeDiv), 2)
|
||||
var px, py: float; var first = true
|
||||
var px, py: float
|
||||
var first = true
|
||||
|
||||
for col in 0..<w:
|
||||
let s = min((col * visible) div w, scope.sampleCount - 1)
|
||||
let x = col.float; let y = cy - scope.samplesL[s] * gain * cy * 0.5
|
||||
if first: c.plotDot(x, y) else: c.plotLine(px, py, x, y)
|
||||
first = false; px = x; py = y
|
||||
let x = col.float
|
||||
let y = cy - scope.samplesL[s] * gain * cy * 0.5
|
||||
|
||||
if first:
|
||||
c.plotDot(x, y, cfg)
|
||||
else:
|
||||
c.plotLine(px, py, x, y, cfg)
|
||||
|
||||
first = false
|
||||
px = x
|
||||
py = y
|
||||
|
||||
of ModeXY:
|
||||
var px, py: float; var first = true
|
||||
var px, py: float
|
||||
var first = true
|
||||
let step = max(scope.sampleCount div 1024, 1)
|
||||
|
||||
for i in countup(0, scope.sampleCount - 1, step):
|
||||
let x = (1.0 + scope.samplesL[i] * gain * 0.5) * w.float / 2.0
|
||||
let y = (1.0 - scope.samplesR[i] * gain * 0.5) * h.float / 2.0
|
||||
if first: c.plotDot(x, y) else: c.plotLine(px, py, x, y)
|
||||
first = false; px = x; py = y
|
||||
|
||||
if first:
|
||||
c.plotDot(x, y, cfg)
|
||||
else:
|
||||
c.plotLine(px, py, x, y, cfg)
|
||||
|
||||
first = false
|
||||
px = x
|
||||
py = y
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────
|
||||
|
||||
proc main() =
|
||||
let cfg = parseConfig()
|
||||
|
||||
initTerm()
|
||||
setRawMode()
|
||||
|
||||
var w = termWidth(); var h = termHeight()
|
||||
var hb = newCanvas(w, h, Palette, [HotGlow, WarmGlow, CoolGlow])
|
||||
var w = termWidth()
|
||||
var h = termHeight()
|
||||
|
||||
var hb = newCanvas(
|
||||
w,
|
||||
h,
|
||||
cfg.palette,
|
||||
[cfg.hotGlow, cfg.warmGlow, cfg.coolGlow]
|
||||
)
|
||||
|
||||
crtTurnOn(hb)
|
||||
var c = newBrailleCanvas(w, h, Palette, [HotGlow, WarmGlow, CoolGlow])
|
||||
|
||||
var c = newBrailleCanvas(
|
||||
w,
|
||||
h,
|
||||
cfg.palette,
|
||||
[cfg.hotGlow, cfg.warmGlow, cfg.coolGlow]
|
||||
)
|
||||
|
||||
var scope = initScope(w, h)
|
||||
var aud = startAudio()
|
||||
var running = true
|
||||
|
||||
audioChan.open()
|
||||
audioRunning = true
|
||||
|
||||
var aThread: Thread[ptr AudioCapture]
|
||||
createThread(aThread, audioThread, addr aud)
|
||||
|
||||
while running:
|
||||
let nw = termWidth(); let nh = termHeight()
|
||||
let nw = termWidth()
|
||||
let nh = termHeight()
|
||||
|
||||
if nw != w or nh != h:
|
||||
w = nw; h = nh; c.resize(w, h); scope.resize(w, h)
|
||||
w = nw
|
||||
h = nh
|
||||
c.resize(w, h)
|
||||
scope.resize(w, h)
|
||||
|
||||
let got = audioChan.tryRecv()
|
||||
|
||||
if got.dataAvailable:
|
||||
scope.sampleCount = got.msg.count
|
||||
|
||||
for i in 0..<got.msg.count:
|
||||
scope.samplesL[i] = got.msg.samples[i][0]
|
||||
scope.samplesR[i] = got.msg.samples[i][1]
|
||||
|
||||
c.decayPixels(Decay)
|
||||
c.renderTrace(scope)
|
||||
c.decayPixels(cfg.decay)
|
||||
c.renderTrace(scope, cfg)
|
||||
|
||||
let hud =
|
||||
" " &
|
||||
(if scope.mode == ModeYT: "Y-T" else: "X-Y") &
|
||||
" G:" &
|
||||
scope.gain.formatFloat(ffDecimal, 1) &
|
||||
" "
|
||||
|
||||
let hud = " " & (if scope.mode == ModeYT: "Y-T" else: "X-Y") &
|
||||
" G:" & scope.gain.formatFloat(ffDecimal, 1) & " "
|
||||
let help = " m:mode +/-:gain [/]:time q:quit "
|
||||
c.flush([(1, 0, tNormal, hud),
|
||||
(w - help.len - 1, h - 1, tDim, help)])
|
||||
|
||||
c.flush([
|
||||
(1, 0, tNormal, hud),
|
||||
(w - help.len - 1, h - 1, tDim, help)
|
||||
])
|
||||
|
||||
sleep(16)
|
||||
|
||||
case readKey()
|
||||
of 'q', '\x1b': running = false
|
||||
of 'm': scope.mode = if scope.mode == ModeYT: ModeXY else: ModeYT
|
||||
of '+', '=': scope.gain = min(scope.gain * 1.3, 20.0)
|
||||
of '-': scope.gain = max(scope.gain / 1.3, 0.5)
|
||||
of ']': scope.timeDiv = min(scope.timeDiv * 1.5, 16.0)
|
||||
of '[': scope.timeDiv = max(scope.timeDiv / 1.5, 0.25)
|
||||
else: discard
|
||||
of 'q', '\x1b':
|
||||
running = false
|
||||
of 'm':
|
||||
scope.mode =
|
||||
if scope.mode == ModeYT:
|
||||
ModeXY
|
||||
else:
|
||||
ModeYT
|
||||
of '+', '=':
|
||||
scope.gain = min(scope.gain * 1.3, 20.0)
|
||||
of '-':
|
||||
scope.gain = max(scope.gain / 1.3, 0.5)
|
||||
of ']':
|
||||
scope.timeDiv = min(scope.timeDiv * 1.5, 16.0)
|
||||
of '[':
|
||||
scope.timeDiv = max(scope.timeDiv / 1.5, 0.25)
|
||||
else:
|
||||
discard
|
||||
|
||||
audioRunning = false
|
||||
joinThread(aThread)
|
||||
audioChan.close()
|
||||
|
||||
aud.stop()
|
||||
crtTurnOff(hb)
|
||||
restoreMode()
|
||||
|
||||
Reference in New Issue
Block a user