// 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 // 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