Give the machine a sound device
Four channels on ports 0x40 to 0x4F, each one a whole soundThing voice:
two oscillators, two envelopes, a filter and the routing between them. A
channel keeps its patch between notes, so a program sets an instrument up
once and then plays it.
Six ports rather than forty, because a voice has around forty settings and
four of them would spend more than half the port space on one device.
There is a selector and a value instead: say which channel, say which
setting, write it. That is three writes to change a setting and two to
play a note, which is the right way round - patches are loaded, notes are
played in an inner loop.
Samples come from the machine's clock and not the host's: 48,000 a second
of emulated time, worked out in whole numbers so it never drifts. A
million cycles is exactly 48,000 samples on any host at any speed, which
is what makes a sound something a test can compare. --sound writes them
out, the way --screen writes a picture, for the same reason: the suite has
no speaker.
Tests/sound.sh is 22 checks and found three real defects the first time it
ran, all the same shape - a synthesizer written for a patch editor, wired
up as hardware and inheriting the editor's assumptions:
- Only one voice had an oscillator switched on, so three of the four
channels could not make a sound whatever was written to them.
- That voice's oscillator arrived at full gain and every other one
arrived at nothing, an asymmetry with no reason behind it.
- A note with no sustain is silent but not over, so the obvious way to
wait for a sound to finish waits for ever.
The first two are fixed by the device defining its own power-on state
rather than inheriting synthInit's: every channel arrives able to make a
sound, so writing a note number is the whole of playing a note. The third
was already written into the manual as advice, an hour before the check
existed. The check disagreed with the documentation and the check was
right; the manual now says the one rule, which is that a note sounds until
the gate is dropped.
Programs/Examples/tune.asm plays eight notes, taking its tempo from the
screen's frame interrupt because that is the only regular beat this
machine has. It spends 99.8% of its cycles asleep in WAIT.
Voyager has no speaker yet - this is the device and its tests. Playing the
samples out of the window is the next commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
b0d06aa6e5
commit
d388cd3122
@@ -38,7 +38,7 @@ OBJ_DIR = Object
|
||||
# SplitBit from Voyager is one file each: a terminal or a window. Anything that drifts out
|
||||
# of the shared list and into one of those is behaviour the other does not have, which is
|
||||
# the thing this split exists to prevent.
|
||||
MACHINE_SRCS = machine.c io.c controller.c video.c font.c utility.c cpu.c bootstrap.c assembly.c rom.c
|
||||
MACHINE_SRCS = machine.c io.c controller.c video.c font.c sound.c synth.c utility.c cpu.c bootstrap.c assembly.c rom.c
|
||||
EMU_SRCS = emulator.c $(MACHINE_SRCS)
|
||||
VOY_SRCS = voyager.c $(MACHINE_SRCS)
|
||||
ASM_SRCS = Assembler.c assembly.c firstPass.c Assm-util.c secondPass.c
|
||||
@@ -70,6 +70,9 @@ LINT_TARGET = SplitLint
|
||||
RAYLIB_CFLAGS := $(shell pkg-config --cflags raylib 2>/dev/null)
|
||||
RAYLIB_LIBS := $(shell pkg-config --libs raylib 2>/dev/null)
|
||||
ifeq ($(strip $(RAYLIB_LIBS)),)
|
||||
# The -lm is not spare, even though MATHLIB names it again on the link line below. This
|
||||
# variable is what the probe underneath test-links with, and Raylib does not link without it,
|
||||
# so taking it out here does not tidy a duplicate - it makes the probe say Raylib is missing.
|
||||
RAYLIB_LIBS := -lraylib -lm
|
||||
endif
|
||||
HAVE_RAYLIB := $(shell printf '#include <raylib.h>\nint main(void){return (int)GetTime();}\n' \
|
||||
@@ -121,14 +124,18 @@ $(SRC_DIR_EMU)/rom.c: Programs/Boot/stage1.asm $(ASM_TARGET)
|
||||
|
||||
$(OBJ_DIR)/rom.o: $(SRC_DIR_EMU)/rom.c $(SRC_DIR_EMU)/rom.h
|
||||
|
||||
# The maths library, which the machine wants now that it has a synthesizer in it: the voice
|
||||
# engine works in hertz and seconds and reaches for powf and tanf to do it.
|
||||
MATHLIB = -lm
|
||||
|
||||
# Emulator binary
|
||||
$(EMU_TARGET): $(EMU_OBJS)
|
||||
$(CC) $(CFLAGS) -o $(EMU_TARGET) $(EMU_OBJS)
|
||||
$(CC) $(CFLAGS) -o $(EMU_TARGET) $(EMU_OBJS) $(MATHLIB)
|
||||
|
||||
# Voyager: the same machine with a screen and a speaker. Its own object for the front end,
|
||||
# and the shared ones for everything that is actually the machine.
|
||||
$(VOY_TARGET): $(VOY_OBJS)
|
||||
$(CC) $(CFLAGS) -o $(VOY_TARGET) $(VOY_OBJS) $(RAYLIB_LIBS)
|
||||
$(CC) $(CFLAGS) -o $(VOY_TARGET) $(VOY_OBJS) $(RAYLIB_LIBS) $(MATHLIB)
|
||||
|
||||
$(OBJ_DIR)/voyager.o: $(SRC_DIR_EMU)/voyager.c
|
||||
mkdir -p $(OBJ_DIR)
|
||||
@@ -211,6 +218,8 @@ test: all strict
|
||||
@echo
|
||||
@./Tests/video.sh
|
||||
@echo
|
||||
@./Tests/sound.sh
|
||||
@echo
|
||||
@./Tests/terminal.sh
|
||||
@echo
|
||||
@./Tests/native.sh
|
||||
@@ -256,6 +265,8 @@ sanitize:
|
||||
@echo
|
||||
@./Tests/video.sh
|
||||
@echo
|
||||
@./Tests/sound.sh
|
||||
@echo
|
||||
@./Tests/terminal.sh
|
||||
@echo
|
||||
@./Tests/native.sh
|
||||
|
||||
Reference in New Issue
Block a user