Kernal & BASIC sound API for the ROM - ideas?
Posted: Wed Dec 07, 2022 4:59 pm
TL;DR: The CommanderX16 ROM needs to have some audio-related goodies akin to the graphics goodies in order to make the system more accessible on the audio side.
I was in a discussion with discord user @Jestin last night regarding his crowd-sourced X16 beginner's guide project. Specifically, he was asking whether I'd be willing to write the audio section of the guide. Of course I'm interested in doing this. Over the course of the discussion, it became painfully obvious just how low-level X16 audio really is. There is exactly ZERO support for users getting into retro computing or in audio chip tinkering on the X16. The audio section would, of necessity, deal in POKEs, frequency calculations or have a table of notes to come back to any time you want to make a tune, etc. The PSG is bad enough in this regard, but the YM2151 is a hugely daunting prospect. This need not be the case.
Graphics functionality on the X16 has an entire suite of BASIC commands and even a dedicated screen mode. Why should sound be left out in the cold? I made Zsound so that something would exist for the community have something that lets them quickly and easily add sound to their projects. I'm proud of what it's accomplished thus far, but it is absolutely a developer-space toolkit. In order to even use it, you must be developing software in assembly, C, Prog8, or some "cross-plat" development environment. I think it would make sense to take this experience and create some simple utilities and BASIC commands that can at least get beeps, boops, and dings out of the X16 without having to dive into the arcane realm of audio.
So my proposal is to add a ROM bank dedicated to audio functionality, containing various things such as a small set of decent FM instruments, note->frequency lookup tables for PSG, BASIC commands, and a function API for calling these routines from projects just like any other Kernal calls. Being in a separate bank, these would be called with JSRFAR which isn't as performant as having code right there in the main Kernal API, but I think everyone can agree that having a dedicated bank makes more sense than squeezing everything into the Kernal bank.
If certain routines in the audio bank seem to be of particular general-purpose utility (e.g. a routine to initialize the YM2151, which is NOT as simple as just writing 0 into all registers), then perhaps those might merit an API slot in the main Kernal, but this remains to be discussed.
I haven't completely collected all of my ideas into one place yet, but here's a list of things that I think could be there:
BASIC:
FMNOTE <channel>,<note>,<oct> - plays/stops notes on the YM2151
FMINST <channel>,<instrument number> - chooses from an included bank of system instrument patches
FMPAT <channel>,<address> - loads a custom 26-byte patch (same format as in the ROM) from a location in RAM
FMLFO <waveform>,<speed>,<vibrato depth>,<tremolo depth> - one-shot command to configure the LFO on the YM
PSGNOTE <channel>,<pitch> - sets the pitch on the selected channel, and applies the "current" volume, waveform, and pulse width
PSG will have a sense of a "current setting" for volume, waveform, and pulsewidth that is used for all notes played.
the commands that set these values will optionally have a 2nd argument to specify a channel. If given, the command will modify a channel's setting and not the "current" value.
should FM work the same way, and apply "current instrument" ?
PSGVOL <0-100>[, <channel>] - sets the volume o-100%
PSGWAV <0-3>[, <channel] - same as in PSG: 0=pulse, 1=saw, 2=triangle, 3=noise
PSGPW <0-50>[, <channel>] - sets the duty cycle of 0..50%
Should there be a command to play a "wav" file on the PCM channel by streaming it from a file? (this would be a blocking type of command - execution pauses while it plays the sound) Obviously, supporting WAV format itself would likely be too much... or would it?
Kernal:
FM_INIT - initializes YM to its default state (chip default, not X16 default)
PSG_INIT - zeros out all PSG settings
the Kernal reset/init routine could (should?) pre-load the YM with some kind of default patch on all 8 channels so that it is immediately useful to programs, even not using BASIC - playing notes on the YM is NOT hard if the voices are configured for you.....
Audio Bank:
This should hold the underlying code for the vast majority of the above commands, with the possible exception of the x_INIT routines.
It would have an API set that's reachable with JSRFAR calls...
FM_PATCH - loads a 26-byte instrument patch from a given address
what other routines would be useful here?
contains a set of FM instrument patches - I'm thinking around 32 seems good. This doesn't need to be the ultimate library, just some basic, useful stuff to get folks off the ground.
PSG note->frequency value lookup table
Again, the above is mostly just brainstorming on my part. I realize that assigning Kernal API slots and BASIC tokens is a huge deal, as once they're created, they must exist forever, and the number of slots is limited on both of those resources. The Audio API, of course is its own new thing and relatively free from any kind of constraints as to what it may contain.
I'm inviting any feedback or suggestions on this, as it's a fairly sizeable undertaking, and when it comes to parsing commands in BASIC, I've got no clue what I'm doing (yet).
If this seems to get any traction, I'll likely want to get into some real-time collaboration with folks on Discord.