Concerto Dev Log

All aspects of programming on the Commander X16.
User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


Hi all,

updates of my Concerto synth engine will be posted in this thread. The file upload can be found below and will be updated as soon as there are significant additions to the feature set.


 

The source code can be found on GitHub: https://github.com/biermanncarl/cx16-concerto

Version 0.1.0 has the following features:


  • 32 synth patches


  • 16 monophonic channels, each playing a dynamically assigned synth patch


  • up to 6 oscillators per voice


  • up to 3 envelopes and 1 LFO per voice


  • pitch, volume and pulse width modulation


  • volume control per voice ("velocity")


  • save and load presets (currently 1 slot)


User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


I've just made a release for Version 0.1.1.

There are no changes to the actual features of the synth engine. However, the code has been reorganized so that it is MUCH easier to include the synth engine with or without UI into your own ca65 project.


  • Separated synth engine and UI so that they can be separately included


  • Provided a simple API for both


  • Added documentation and examples on how to use them


  • Added precompiled binaries of all examples


The example below shows what it takes to utilize Concerto in your own ca65 program


.zeropage

.include
"concerto_synth/synth_zeropage.asm"



.code

jmp start



.include "concerto_synth/concerto_synth.asm"



start:

jsr concerto_synth::initialize

jsr concerto_synth::activate_synth



; play a note

lda #60

sta concerto_synth::note_pitch

lda #63

sta concerto_synth::note_volume

lda #0

sta concerto_synth::note_channel

lda #0

sta concerto_synth::note_timbre

sei

jsr concerto_synth::play_note

cli



; and wait until key is pressed

mainloop:

jsr $FFE4 ; GETIN

beq mainloop



jsr concerto_synth::deactivate_synth

rts


 

User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


Released v0.2.0. The first modulation API has arrived! Pitchbend is now available.


  • Added pitchbend. You can set pitch directly or set a glide slope or even do both. Example for API usage is included in the repo.


  • Added voice vibrato dial. This vibrato affects all oscillators at once, whereas earlier, you had to set vibrato for each oscillator individually (which you still can do). Vibrato uses the voice's LFO (low frequency oscillator).


User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


Released v0.3.0.


  • Copy/Paste function added to the GUI. Copy/paste timbres from one slot to another, without the need of having an SD card mounted. Load/save a single preset to the SD card still works as before.


  • Dump and Restore all timbres functions have been added to the API. If you have a file opened, simply call the function to dump/restore all timbre data to/from the open file. This makes incorporation of Concerto timbre data into other file formats as easy as calling any subroutine.


  • Added a macro that loads the "factory presets" (the same ones as in the initial demo video). It's called CONCERTO_LOAD_FACTORY_PRESETS. It's only here for testing purposes, and provides an easy and quick way to get access to non-trivial timbre data.


Next thing I want to look into is adding FM to the engine. No promises on how that might work out, though ?

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

Concerto Dev Log

Post by ZeroByte »


Thoughts on FM - I'd imagine it would be hard to get perfect sync between the two to the point that the FM actually blends with the notes as intended, but having a "linked keypress" event to a given voice/voices might be a basic way to accomplish this. Obviously the UI for editing the FM patches is going to be quite different. Loading patches into the YM takes "considerable" time compared to the PSG, especially since you have to delay some 20-odd cycles per write to the YM, so having the synth dynamically allocate FM voices would be "interesting" to see how that comes out.

Question - I'm guessing that Concerto dynamically allocates PSG voices based on how many osc's are used in a given timbre and on how many notes of polyphony you're using at the moment. Would it be able to juggle this with multiple timbres?

User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


Thanks for your thoughts! Nice to have someone with some experience in this area "aboard" ?

Yes, Concerto dynamically allocates PSG voices as needed, and I plan to do something similar with the FM voices. The idea is to simply add one (optional) FM voice to the PSG oscillators. So the user can either use PSG oscillators, the FM voice, or both together. I think that combining FM and PSG can give some very juicy results.

I am not so much worried (yet) as to whether the timing will work. Sure, writing values to the PSG registers doesn't require a lot of cycles. But computing the values that are written to the registers does take a lot of cycles. For example, in each tick, the volume envelope value is multiplied with the oscillator's volume knob and the voice's volume (a voice here referring to a voice of a timbre that can contain multiple oscillators). On top of that, the oscillator's frequencies are computed in every tick, which also involves a multiplication. And then add all the optional modulation routings. And all this has turned out to work just fine. @m00dawg and I haven't encountered a situation where we got Concerto to freeze due to overwhelming the CPU.

Thus, I am not so much worried about, say, 1000-2000 cycles necessary to initialize an FM voice.

Does that reasoning make sense?


15 hours ago, ZeroByte said:




Loading patches into the YM takes "considerable" time compared to the PSG, especially since you have to delay some 20-odd cycles per write to the YM



That is interesting! How did you get this number of 20-odd cycles?

If we can somehow accurately estimate how much we'd have to wait after each write, we can put some code in between the writes in order to make use of the waiting time. I am imagining, since some bytes on the YM2151 contain multiple parameters, one could use the time to stitch those values together from their bare pieces (e.g. FL and CON). I intend to keep those values separate in memory for easier editing.

User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »



1 hour ago, kliepatsch said:




For example, in each tick, the volume envelope value is multiplied with the oscillator's volume knob and the voice's volume



Thinking about this, I think those values should be added/subtracted instead of multiplied. That's because the number we put into the PSG register is getting exponentiated, thus effectively multiplying the factors for us. I need to give this a shot at some point and compare it to the current way of computing the volume. If this works out, it would significantly reduce the CPU load of Concerto!

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

Concerto Dev Log

Post by ZeroByte »


I’m not looking at the spec sheet right now but it states that after writing data to YM, it requires either 56 or 64 of ITS clock cycles to do the write. I think it’s 64. The YM runs at ~3.5Mhz.

3.5/8 * 64 = 28 .. which sounds right to me.

The YM has a feature where if you read from it on the data port, it returns a byte with the 2 LSB being the two timers’ IRQ status flags and the MSB is a busy flag which clears after that 64-cycle delay.

I was going through the Z80 sound CPU routine in Street Fighter II (yeah, I’m a nerd) to see how they did things, and their YMWrite() routine started with a busy loop checking that bit before proceeding.

I was going to suggest what you thought of in your post - spread the YM writes through the oscillator computations.

I enjoy your demo vid for Concerto - and for whatever reason tho, I just suck at noodling with synths to make cool sounds tho. LOL.

User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Concerto Dev Log

Post by kliepatsch »


Thanks for the explanation. Following your argument, I think it's worse: The specs say 68 YM clock cycles, and you have to multiply by 8 and divide by 3.5 (the CPU runs faster, so it gets more cycles done). I'm getting 156 cycles from this. Hmm... That'll be a challenge to make efficient code for ?

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

Concerto Dev Log

Post by ZeroByte »


I hadn't finished drinking my coffee when I wrote that - lol. You're right. I think 28 sounded familiar to me because it's ~20 cycles per write on C64 (I've toyed with the idea of making a YM2151 cart for C64).

156 cycles is definitely a hard pill to swallow if you're going to just busy loop them away. I'm working out what my sound player routine's going to look like in Flappy Bird, and I think what I'm going to do is interleave the YM writes between the PSG voices. Not that Flappy Bird is some audible feast - I'm thinking about what a general-purpose sound routine might look like so others could just import that and use it in their own projects.

I'm currently taking inspiration from how Wolf3d's music routine works: The music/sfx data is just a byte stream of reg+value pairs alternating with 16-bit pause values. If I had a queue of YM values to write, and N queues for PSG voices, then I'd just interleave the YM messages with the PSG voice queues and it should work out well enough.

Post Reply