Kernal & BASIC sound API for the ROM - ideas?

Chat about anything CX16 related that doesn't fit elsewhere
TomXP411
Posts: 1785
Joined: Tue May 19, 2020 8:49 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by TomXP411 »



On 12/20/2022 at 10:23 AM, Ed Minchau said:




I don't think you can do that. Sure, you can VLOAD into VRAM,  but PCM audio has its own register. Right now, best we can do is load into RAM and then push one byte at a time into the PCMAudio register. 



For my video demos I've been breaking up the audio files into separate 1/10th of a second clips, about 2574 bytes each (can't recall the exact numbers off the top of my head). That's more than enough time for a snare, kick, or high hat (or tom, probably not enough time for a cymbal).



For about 10kb of ROM we could have samples of various drum sounds. The problem then becomes combining two or more PCM sounds that happen at the same time. Perhaps an 8 bit sample could be enough for various drum sounds, to make the necessary math faster.



Ah, so the PCM buffer is separate from VERA's VRAM? I was not aware of that. That's actually good news... although it does make it harder to use from BASIC, since it's not really possible to push data into the register fast enough:

BASIC can handle roughly 500 transactions per second to VERA, and to keep an audio buffer full, you'd need to at least match your sample rate in terms of bytes per second. I guess you could use a machine language routine to drive the PCM buffer from a RAM bank, but you'd still only fit something like 1-2 seconds of audio in there. Having said that.. perhaps that's the best way to use PCM audio from BASIC: 

PCMPLAY Bank, Address, Length, [other setup parameters]

The catch would be that you can only play a sample that you can fit in memory (no streaming from disk), and if you play from a bank, then the sample must fit in one bank. 

 

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by ZeroByte »


PCMPLAY (if implemented) would behave as follows:

 

PCMPLAY "filename" - it proceeds to play the file, and execution halts while the file plays.

So long as the file's data rate didn't exceed the ability to stream it into the FIFO, all would go well. In the forthcoming r42 kernal, MACPTR is able to stream data into IO (or any single address really, but it wouldn't be useful if the dst address wasn't a chip) and this mode is actually its fastest now. You could just MACPTR 512 bytes at a time into FIFO until EOF or STOP key is pressed.



PCM streaming requires quite a bit of attention, and for higher bitrates, it takes quite a lot of CPU to accomplish, even from RAM. I guestimate that a "WAV file play" command would be able to keep up with something around 16K 8bit mono.

A blocking call such as I'm describing would be able to just monitor the AFLOW bit, and whenever it goes high, MACPTR some more data into the FIFO and then return control to the program. It's simple, crude, and requires a file be there, and I could also see it being able to play from some point in RAM if you were to BLOAD the file somewhere...

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by ZeroByte »


This lets you have the ability to play sounds at points during a program's execution. (tada.wav, anyone?)

 

Let's not forget though, that it's definitely quite doable for "background player" code to exist - you just have to load it and run it, and communicate with it via SYS calls (or if the program also installs a BASIC wedge to add commands, that'd work as well), and there is already such a program in the audio downloads directory here. The goal for the BASIC extensions is to make it possible to get some simple use out of the X16's sound, but anything advanced is really going to require something more substantial, just due to the nature of how time-dependent audio is, and how loosey-goosey with time BASIC is.

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by ZeroByte »


One thing you'll be happy to hear, @TomXP411: we're using MIDI as the underlying pitch scheme in all of the pitch conversion code we're including in the bank for general consumption.

There is going to be a set of routines available to convert between FM, PSG, and MIDI notes. (and maybe even 16.8 fixed point frequencies) Not sure exactly what the outside format should be where pitch bends are concerned, but it just seemed like the most professional-grade standard to base things on. Thanks for that suggestion. These routines are still in their infancy, but will be useful for many applications, especially trackers that want to have consistent pitches between the chips, and having such code in ROM means smaller code in RAM.

The current design is to have the routines accept a pitch value in some format in .XY and then transform it into the desired format, returning the result in .XY (and A for things like the fractional frequency, or some subset of pitch bend for MIDI)



(I just added this post to point out that a Fisher-Price BASIC interface is not the only goal of this project)

TomXP411
Posts: 1785
Joined: Tue May 19, 2020 8:49 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by TomXP411 »



On 12/21/2022 at 12:28 PM, ZeroByte said:




One thing you'll be happy to hear, @TomXP411: we're using MIDI as the underlying pitch scheme in all of the pitch conversion code we're including in the bank for general consumption.



There is going to be a set of routines available to convert between FM, PSG, and MIDI notes. (and maybe even 16.8 fixed point frequencies) Not sure exactly what the outside format should be where pitch bends are concerned, but it just seemed like the most professional-grade standard to base things on. Thanks for that suggestion. These routines are still in their infancy, but will be useful for many applications, especially trackers that want to have consistent pitches between the chips, and having such code in ROM means smaller code in RAM.



(I just added this post to point out that a Fisher-Price BASIC interface is not the only goal of this project)



Very nice. ? I saw the demo of the first prototype, and I'm impressed. The BASIC interpreter is really starting to shape up into a nice starter setup for new users.

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by ZeroByte »


BASIC to FM and PSG is fairly simple because of the note format in BASIC: note=1-12 , octave is a separate argument. (for now - we're not 100% on this)

One issue we've yet to address is the 1-12 range. We're in consensus that it should describe C .. B and not C# .. C as the YM does.

note 0 = release

note > 12 = noop. (makes it easier to make simplified music data format - noop = rest, for all intents and purposes.)

We're also investigating negative value = no-retrigger (for ties/slurs) mode. (or "legato mode" as Furnace calls it)

User avatar
Jestin
Posts: 85
Joined: Sat Jun 27, 2020 10:14 pm
Contact:

Kernal & BASIC sound API for the ROM - ideas?

Post by Jestin »


I just want to give a quick update on the progress of this endeavor, because we've gotten a lot working.

For interfacing with the YM2151, we've created the following BASIC statements:


  • FMINIT - Initialize YM2151 settings and load a set of default patches into all 8 channels.  Also useful for silencing all channels at once.


  • FMINST - Load a predefined  patch into a channel from the audio bank.  Currently there are 162 patches in the ROM to choose from.


  • FMNOTE - Play a note on a channel.  This supports a way to select an octave, a note, and whether this causes a re-trigger or just updates the note being played from the previous trigger.  It can also be used to silence a channel, and there are 3 "NOP" notes that will neither stop the current note, re-trigger it, nor update it.


  • FMFREQ - Play a given frequency in hertz on a channel.


  • FMDRUM - In one statement this loads a percussion patch (patches 25 - 87) and triggers it one time on a channel.


  • FMVIB - Set the speed and depth of the LFO so it affects the patches that use it.


  • FMVOL - Set the volume of a channel.


LIkewise, we have a similar set of BASIC statements for the VERA's PSG channels:


  • PSGINIT - Initialize PSG settings and set the duty cycle of all 16 channels.


  • PSGWAV - Set the waveform and duty cycle of a PSG channel.


  • PSGNOTE - Play a note on a channel.  This supports a way to select an octave and a note.  It can also be used to silence a channel, and there are 3 "NOP" notes that will neither stop the current note nor update it.


  • PSGFREQ - Play a given frequency in hertz on a channel.


  • PSGVOL - Set the volume of a channel.


There's more that the kernel API will be able to do (such as loading custom patches), and we are still looking into adding an additional BASIC statement or two.  Also, the init routines have been added to boot up, so they won't need to be called before using these statements.  You will be able to boot up and immediately type `FMNOTE 0,$4A` to play a concert A with the grand piano patch.  Hopefully we'll get the PR out soon and it will be included in the r42 release of the rom!

TomXP411
Posts: 1785
Joined: Tue May 19, 2020 8:49 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by TomXP411 »



On 12/31/2022 at 11:00 PM, Jestin said:




I just want to give a quick update on the progress of this endeavor, because we've gotten a lot working.



For interfacing with the YM2151, we've created the following BASIC statements:




  • FMINIT - Initialize YM2151 settings and load a set of default patches into all 8 channels.  Also useful for silencing all channels at once.


  • FMINST - Load a predefined  patch into a channel from the audio bank.  Currently there are 162 patches in the ROM to choose from.


  • FMNOTE - Play a note on a channel.  This supports a way to select an octave, a note, and whether this causes a re-trigger or just updates the note being played from the previous trigger.  It can also be used to silence a channel, and there are 3 "NOP" notes that will neither stop the current note, re-trigger it, nor update it.


  • FMFREQ - Play a given frequency in hertz on a channel.


  • FMDRUM - In one statement this loads a percussion patch (patches 25 - 87) and triggers it one time on a channel.


  • FMVIB - Set the speed and depth of the LFO so it affects the patches that use it.


  • FMVOL - Set the volume of a channel.




LIkewise, we have a similar set of BASIC statements for the VERA's PSG channels:




  • PSGINIT - Initialize PSG settings and set the duty cycle of all 16 channels.


  • PSGWAV - Set the waveform and duty cycle of a PSG channel.


  • PSGNOTE - Play a note on a channel.  This supports a way to select an octave and a note.  It can also be used to silence a channel, and there are 3 "NOP" notes that will neither stop the current note nor update it.


  • PSGFREQ - Play a given frequency in hertz on a channel.


  • PSGVOL - Set the volume of a channel.




There's more that the kernel API will be able to do (such as loading custom patches), and we are still looking into adding an additional BASIC statement or two.  Also, the init routines have been added to boot up, so they won't need to be called before using these statements.  You will be able to boot up and immediately type `FMNOTE 0,$4A` to play a concert A with the grand piano patch.  Hopefully we'll get the PR out soon and it will be included in the r42 release of the rom!



This is brilliant. Not only does this make BASIC easier, but if these are callable from machine language, you just made everyone's lives easier all the way around. Programming music and sound went from being "I might be able to..." to "sure, no problem."

MooingLemur
Posts: 34
Joined: Sat Feb 19, 2022 4:44 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by MooingLemur »


In addition to a few as yet unmentioned commands, Jestin and I have added `FMPLAY` and `PSGPLAY`, which work very similarly to the `PLAY` command in GWBASIC ?  The command will block and not return until all of the notes and rests in the string have been played.  This is in contrast to `FMNOTE` and `PSGNOTE` which return immediately.  You can combine the two techniques and, well, a video works better than an explanation:





 

And the BASIC commands are just a shim that call an API that does the real work. All of the API including the heart of these BASIC commands will be available to machine language programs.

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

Kernal & BASIC sound API for the ROM - ideas?

Post by ZeroByte »


This has definitely turned out to be quite a featureful enhancement for BASIC as well as the functionality available as low-level calls. @MooingLemur has produced tons of useful code for this project, and @Jestin on the BASIC parsing side of things. I'm excited because this will build in a nice set of functionality that makes playing music on the YM2151 and PSG much easier for anyone, regardless of the programming language in use.

 

Post Reply