Page 1 of 7

Kernal & BASIC sound API for the ROM - ideas?

Posted: Wed Dec 07, 2022 4:59 pm
by ZeroByte

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.


Kernal & BASIC sound API for the ROM - ideas?

Posted: Wed Dec 07, 2022 6:07 pm
by Jestin

For comparison, here's how the VIC-20 manual introduces new users to sound:

image.png.91fd5f03ca6d2191db77f5b2f4c788f3.png

Notice this includes a full UI for selecting voices and notes.  Now here's how the X16 manual will currently have to introduce new users to sound:

image.png.13565f7457722028f38a892fa0869657.png

This will only play a single quick note of a marimba, and has no UI at all.

Now, here's what the same quick note will look like if we add the proposed BASIC commands for interfacing with the YM2151:

image.png.ed777faa34f00ca7886d8543ccc638ba.png

I really like the idea of being able to hand children a manual have let them play around with sound.  For that purpose, I'm obviously in favor of the BASIC commands.


Kernal & BASIC sound API for the ROM - ideas?

Posted: Wed Dec 07, 2022 7:02 pm
by neutrino

Just an idea.. perhaps a command like "Play" like that one in IBM BASIC ( ref ). But which specify which sound chip to be used and which plays in the background. Like many C64 SID tunes did by updating sound I/O during vertical blanking interval.

"GW-BASIC can play simple music using the PLAY statement, needing a string of notes represented in a music macro language, e.g., PLAY "edcdeeL2edfedL4c". More low-level control is possible with the SOUND statement, which takes the arguments of a frequency in hertz and a length in clock ticks for the standard internal PC speaker in IBM machines. Consequently, sound is limited to single channel beeps and whistles as befits a "business" machine. Home-based PCs like the Tandy 1000 allow up to three channels of sound for the SOUND and PLAY commands."

 


Kernal & BASIC sound API for the ROM - ideas?

Posted: Wed Dec 07, 2022 8:34 pm
by ZeroByte

To me, having something like a built-in music player (however simple) is well beyond the scope of this. It means that the Kernal must now have a function in the IRQ handler that checks the state of the music player at all times. Honestly, the code to play a song in BASIC on the YM is very simple to make. The worst thing about the YM is configuring the patches (instruments) on the voices. It's pretty easy on PSG as well using VPOKE except for having to figure out what frequency numbers to use. (having a table in the book / on a webpage to reference is one thing, but it's still a PITA)



Essentially, the goal here is to present the equivalent of a basic Casio keyboard - select an instrument and start plunking around. It doesn't have a built-in sequencer, or instrument creation functionality. It's meant to be a "plug-and-play" solution for beginners to have access to the stuff. If you wanna do fancy stuff, it's time to read the chips' reference materials and go down the rabbit hole yourself. Looping through notes from DATA statements and using them to issue PLAY commands (or whatever the BASIC commands end up being) isn't terribly complex, and makes for a good exercise in learning loops and data management in BASIC.

I do appreciate the feedback and strongly encourage more from you and from everyone else.

I haven't written any code for this yet. I'm taking a top-down approach so that I have a clear vision of the goal and what is needed to implement it before hitting the code.

 

On that front, to me, the decision point on the BASIC commands is this: should it be a list of chip-specific atomic commands (as in the list on the OP) or a slightly abstracted "music interface" that works like graphics tools where there's a currently-selected tool (paintbrush, line, flood fill bucket, etc) and color and "cursor location" - all commands acting relative to those?

This would make the list of commands shorter - needing only a command to select chip, voice, and instrument - then it's just NOTE NOTE NOTE NOTE

This seems to make sense to me, and shouldn't require a whole lot of state variables be added to the Kernal's memory area. I just wonder if it's TOO simplified.

 


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 9:07 am
by neutrino

@ZeroByte If the commands would need many repeated parameters that would be in essence copy & paste from previous line. Then it might make sense to select something and then every following command refer to that. So if a command for the voice is given any following commands regarding instrument uses the previously selected voice.

The caveat might be for when using multiple chips, voices etc without having to re-specify everything with single commands. Perhaps a dual approach could be doable with a "set-it-all" command which does chip-voice-instrument-note in one go. And then other commands if the programmer wants to use a different voice but everything else being the same.

I have a feeling the SID chip in stereo will find its way to the X16 so that may also be something to consider.. ?

 


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 3:33 pm
by ZeroByte

Add-ons would obviously not be supported in the system BASIC.

Of course, there's nothing that says the creator of said expansion HW could not also supply a .PRG that adds a BASIC shim for it. ?

 


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 3:54 pm
by BruceMcF


On 12/8/2022 at 10:33 AM, ZeroByte said:




Add-ons would obviously not be supported in the system BASIC.

Of course, there's nothing that says the creator of said expansion HW could not also supply a .PRG that adds a BASIC shim for it. ?



My thought exactly. The ROM should be for the built-in hardware, but having an autoboot program means that it's much easier to support add-ons with software extensions.


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 3:57 pm
by ZeroByte

More brainstorming on this....

So it makes the most sense to me that this interface should be as close to being that "entry-level Casio" mentality, and as such, the commands need to reflect that as closely as possible.

Therefore, it kinda makes sense to put <volume> on the xxNOTE commands as arguments:


  • FMNOTE <channel>,<note>,<oct> - plays/stops notes on the YM2151


  • PSGNOTE <channel>,<pitch> - sets the pitch on the selected channel, and applies the "current" volume, waveform, and pulse width


become:


  • FMNOTE <channel>,<note>,<octave>[, <volume>]


  • PSGNOTE <channel>,<note>,<octave>[, <volume>]


if not specified, volume = 100%

Likewise, the PSG changes to a "patch" mentality where it's understood that the config is placed into the voice and that's that. There's no "current instrument, waveform, volume, etc" concept. Set the voice, and from now until you change it, it plays notes as configured.



So the PSG commands in the OP's list (PSGVOL, PSGWAV, PSGPW) are replaced by:


  • PSGINST <channel>,<waveform>[, <pulsewidth>]


if not specified, pulsewidth = 50%



FMNOTE and PSGNOTE will both just poke the specified note onto the specified voice(s) assuming the voice is already set up. No fuss, no muss.

Volume is now just built into the note commands, so there's no need to have a separate command to set it, and no need for BASIC to track state on anything. Just poke it. I could also see VOLUME being a command to set the default [, <volume>] value to something other than 100% though...


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 3:58 pm
by Yazwho


On 12/8/2022 at 3:33 PM, ZeroByte said:




Of course, there's nothing that says the creator of said expansion HW could not also supply a .PRG that adds a BASIC shim for it. ?



Is it possible to patch in new 'commands' to Basic? Are there any examples?


Kernal & BASIC sound API for the ROM - ideas?

Posted: Thu Dec 08, 2022 4:17 pm
by ZeroByte


On 12/8/2022 at 9:58 AM, Yazwho said:




Is it possible to patch in new 'commands' to Basic? Are there any examples?



I don't have any examples to point to (never bookmarked anything - d'oh!) but from a little light browsing I did on the subject a while back, my takeaway was that you change the CHRIN vector/code (which BASIC uses to parse the screen data) and hook in custom tokens that way. How it's done is beyond my knowledge, but this technique should be doable on the X16 as well.

And yes, I said code - I think CHRIN actually has code in zeropage - actual code, and it's this code that BASIC shims tweak on CBM machines.