65816-llvm-mos/runtime/include/iigs/sound.h
Scott Duensing da095402ec Updated
2026-06-02 23:17:57 -05:00

137 lines
5.9 KiB
C++

// iigs/sound.h - convenience wrappers for the SoundManager toolset.
//
// What's here today: the simplest correct wrappers around the existing
// toolbox calls - SysBeep, FFStartSound on a pre-loaded DOC RAM region,
// FFStopSound, FFSoundDoneStatus polling, plus iigsLoadDocSample (a
// thin wrapper around WriteRamBlock that stages caller-RAM bytes into
// the Ensoniq DOC's 64 KB audio RAM) and iigsSoundProbeInit (a small
// MMStartUp + SoundStartUp helper so CLI-style sound demos don't have
// to pull in startdesk()'s 16-tool chain). Lower-level than
// std::sound but a thin layer above iigs/toolbox.h.
//
// Phase 1.6 (2026-06-01) corrected the IigsSoundParmT layout to match
// ORCA's authoritative SoundParamBlock (18 bytes). The previous 6-byte
// struct was silently wrong; any caller relying on the old layout MUST
// migrate. The new layout matches the Apple SoundManager reference
// (Apple Tech Note #76) exactly.
//
// Phase 2.4 (2026-06-01) added iigsLoadDocSample so callers can stage
// in-RAM waveform bytes directly without going through the raw
// WriteRamBlock toolbox call.
//
// Caller must have started up the SoundManager before any of these
// functions are called. startdesk() in iigs/desktop.h does that for
// you; for a CLI-style sound demo where a full desktop is overkill,
// call iigsSoundProbeInit() yourself first.
#ifndef IIGS_SOUND_H
#define IIGS_SOUND_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
// SoundParamBlock consumed by FFStartSound. Layout MUST match ORCA's
// authoritative SoundParamBlock (tools/orca-c/ORCACDefs/sound.h:69):
// 18 bytes total, field order load-bearing. Do NOT reorder; the
// toolset reads by offset.
//
// waveStart is a 24/32-bit BYTE address into DOC RAM (NOT a 256-byte
// page index, as the previous incorrect layout assumed). Pass the
// byte offset where the sample begins in DOC RAM.
// waveSize is in 256-byte pages.
// volSetting's high byte must be zero (DOC volume is u8).
// nextWavePtr chains additional waves; NULL terminates.
typedef struct IigsSoundParmT {
void * waveStart; // 4B: DOC RAM byte address of wave
uint16_t waveSize; // 2B: wave length in 256-byte pages
uint16_t freqOffset; // 2B: pitch offset
uint16_t docBuffer; // 2B: DOC buffer start, low byte = 0
uint16_t bufferSize; // 2B: DOC buffer size, low byte = 0
struct IigsSoundParmT * nextWavePtr; // 4B: next wave in chain, NULL = end
uint16_t volSetting; // 2B: DOC volume (high byte = 0)
} IigsSoundParmT;
_Static_assert(sizeof(IigsSoundParmT) == 18, "IigsSoundParmT must be 18 bytes per ORCA SoundParamBlock");
// ---- one-call wrappers --------------------------------------------
// Lightweight startup helper for sound-only demos that don't want to
// drag in startdesk()'s full 16-tool chain. Calls MMStartUp +
// SoundStartUp in the right order. Safe to call after the Loader
// already started up Memory Manager (the toolset reference-counts).
// Returns the userId allocated by MMStartUp; the caller can pass it
// to NewHandle/similar if it needs to allocate from the same pool.
//
// Pair with iigsSoundProbeShutdown() at exit, or just exit straight to
// GS/OS - Finder will clean up the tool startup chain on app
// termination.
unsigned short iigsSoundProbeInit(void);
// Shut down the SoundManager started by iigsSoundProbeInit(). Optional
// - Finder will reclaim everything on app exit.
void iigsSoundProbeShutdown(void);
// Stage a waveform from caller RAM into the Ensoniq DOC's 64 KB audio
// RAM. Wraps the WriteRamBlock toolbox call (tool 0x0908, set 0x08).
//
// SoundManager must already be started up (see iigsSoundProbeInit() or
// startdesk()). Returns nothing; WriteRamBlock has no error result and
// silently truncates if docOffset + size overflows DOC RAM. Use
// iigsPlayDocSample() afterwards to play the staged region.
//
// wave pointer to the raw sample bytes (signed 8-bit, DOC's
// native format). Reads `size` bytes starting here.
// size number of bytes to copy. Must be a non-zero multiple
// of 256 - DOC RAM addressing is page-aligned (256-byte
// pages) and FFStartSound consumes lengths in pages.
// docOffset destination BYTE offset into DOC RAM (0..65535). The
// low byte should be zero (page-aligned).
void iigsLoadDocSample(const signed char *wave, unsigned short size, unsigned short docOffset);
// System beep. Same as the toolbox SysBeep but named consistently.
void iigsBeep(void);
// Play a sample that has already been written into DOC RAM. Returns
// immediately (asynchronous); use iigsSoundWait() to block until done.
//
// Phase 1.6 (2026-06-01) BREAKING CHANGE: the signature has been
// rewritten to match the corrected struct. Old callers passed
// (docPage, pages, pitch, volume, channel) which silently produced
// wrong DOC RAM addresses (the old waveStart was 1 byte, not 4).
//
// docAddr DOC RAM BYTE address where the sample begins (NOT a
// 256-byte page index). Multiply your old "docPage" by
// 256 to get the equivalent byte address.
// pages length in 256-byte pages.
// freqOffset DOC pitch offset.
// volume 0..255 (placed in volSetting, high byte zeroed).
// genNum generator number (0..15) in the low byte, priority
// (0..255) in the high byte. This is FFStartSound's
// arg0 - the channel is NOT in the struct anymore.
void iigsPlayDocSample(void *docAddr, uint16_t pages,
uint16_t freqOffset, uint8_t volume,
uint16_t genNum);
// Stop playback on the given generator (0..15). Pass 0xFF to stop
// all generators.
void iigsSoundStop(uint8_t generator);
// Block until generator 0 finishes playing. Polls FFSoundDoneStatus.
void iigsSoundWait(void);
#ifdef __cplusplus
}
#endif
#endif // IIGS_SOUND_H