Inserting an SFX in a track playing

ページ 1/2
| 2

By Metalion

Paragon (1446)

Metalion さんの画像

31-07-2021, 20:33

Hello everyone,

I'm working on a music replayer in asm. My music format is loosely based on the PLAY strings, with some differences. Anyway, the replayer is decrementing a different counter for each channel, depending on the lengths of the notes played, of course. As well as interpreting note lengths, alterations, etc ...

I would like to insert an SFX (like any noise inside a game - shoot, fall, explosion ...) or a little music (like a door opened, or treasure found, you get the idea) into a playing track, and be able to resume the music after the SFX/music is finished. The difficulty being that I'm using the full 3 PSG channels for the main track. And of course, all 3 channels must remain synchronized.

The solution I have found so far is to use a silent 'shadow channel'. So, when the SFX is needed :
- I copy the designated channel data (counter, queue, ...) to the shadow channel
- I load the SFX in the designated channel
- I continue to decode the 3 PSG channel PLUS the shadow channel (but not sending notes to the PSG for that one, of course)
- When the SFX is finished, I copy back the shadow channel data to the designated channel, and stop decoding the shadow channel.

Of course it needs for the replayer to decode 4 channels instead of 3, and maybe even more if inserted SFX/music needs more than one channel (although I could live with the capacity of only one channel for SFX/music).

Does someone have a better solution?
What are your thoughts?

Thanks.

ログイン/登録して投稿

By Sandy Brand

Master (245)

Sandy Brand さんの画像

01-08-2021, 00:16

Your approach is solid Smile

But, it also depends a bit on what level of quality your want to obtain for your sound-effects.

For music, the basic PSG is okay, but for sound-effects I find it rather lacking.
So if you want to go for something more advanced, you will need to change the PSG registers at a much higher rate in order to manually generate frequency/volume slides, bounces, etc.

Similarly, the noise generators are shared amongst all 3 PSG channels. So you might need to assign 'ownership' to either the music or the sound-effects. Trying to share this somehow will be hard and can cause audible artifacts.

It depends on how much CPU cycles you are willing to spend on updating the channels, and how advanced your sound-effects need to be. If you want to do something more fancy, you might want to consider extending your idea by having a separate dedicated replayer for the sound-effects.

But other than that, your idea is good I would say Smile

By thegeps

Paladin (863)

thegeps さんの画像

01-08-2021, 08:38

Mmm. I use arkos tracker and its replayers, so never wrote something like it.
But I think I would done it in this way:

When writing music, one of the channel has to be used less than others (in this channelntou'll play sfx)

Music
Music
Music/sfx

When it is needed to play a sfx then stop music and store song queue position for music/sfx channel.
Play sfx.
Stop sfx.
Add sfx lenght to previous saved queue position.
Play music again from new offsetted queue position.

By Metalion

Paragon (1446)

Metalion さんの画像

01-08-2021, 10:03

Sandy Brand wrote:

So if you want to go for something more advanced, you will need to change the PSG registers at a much higher rate in order to manually generate frequency/volume slides, bounces, etc.

It does not seem like a problem to me.
My replayer is on vblank, so I can change the PSG registers every frame.

Sandy Brand wrote:

Similarly, the noise generators are shared amongst all 3 PSG channels. So you might need to assign 'ownership' to either the music or the sound-effects. Trying to share this somehow will be hard and can cause audible artifacts.

You're right, of course. I had assumed that my main track did not use the noise generator, but of course I can never be sure of that. I guess it's a mater of dedicating one of the channel to the use of noise generator (if needed). Like the 3rd channel, for example.

Sandy Brand wrote:

But other than that, your idea is good I would say Smile

Wink

thegeps wrote:

When it is needed to play a sfx then stop music and store song queue position for music/sfx channel.
Play sfx.
Stop sfx.
Add sfx lenght to previous saved queue position.
Play music again from new offsetted queue position.

I cannot do that easily, because the lengths are interpreted from the data on the fly.

Let's say I have this in the main track : "qA4eC3eC3" (that's 1 quarter and 2 eigth), and I want to insert an SFX somewhere near the end of the quarter note. And let's say the SFX duration is 25 frames. When the SFX ends, if I simply add 25 frames to the note counter, I'll probably be after the quarter note, into the second note, which length I do not know, because it hasn't been interpreted yet by the replayer.

I guess I could write a 'catch-up' subroutine that would take the number of frame of the SFX played, and calculate the new position in the main track. But I'm thinking it's the same as decoding the 'shadow' channel every frame. The only difference being how the CPU load is shared.

By hit9918

Prophet (2905)

hit9918 さんの画像

01-08-2021, 21:18

Metalion wrote:

Does someone have a better solution?

yes, virtual channels.

Metalion wrote:

I copy the designated channel data (counter, queue, ...) to the shadow channel

things get swapped forth and back. even more, the many bytes of player state get copied. with virtual channels there is no such copy.

By thegeps

Paladin (863)

thegeps さんの画像

02-08-2021, 00:26

Metalion wrote:

I cannot do that easily, because the lengths are interpreted from the data on the fly.

Let's say I have this in the main track : "qA4eC3eC3" (that's 1 quarter and 2 eigth), and I want to insert an SFX somewhere near the end of the quarter note. And let's say the SFX duration is 25 frames. When the SFX ends, if I simply add 25 frames to the note counter, I'll probably be after the quarter note, into the second note, which length I do not know, because it hasn't been interpreted yet by the replayer.

I guess I could write a 'catch-up' subroutine that would take the number of frame of the SFX played, and calculate the new position in the main track. But I'm thinking it's the same as decoding the 'shadow' channel every frame. The only difference being how the CPU load is shared.

What about create a buffer for each channel where songs data are decoded in advance? So you will be able to do that

By Metalion

Paragon (1446)

Metalion さんの画像

02-08-2021, 01:53

thegeps wrote:

What about create a buffer for each channel where songs data are decoded in advance? So you will be able to do that

That might be a solution.
I'll look into it.

hit9918 wrote:

things get swapped forth and back. even more, the many bytes of player state get copied. with virtual channels there is no such copy.

Can you elaborate and explain to me the concept of virtual channels ?
Thank you.

EDIT : Maybe I have understood ... Could it be that instead of copying channel data between the shadow channel and the physical channels, you only need to keep a pointer to the channels that the PSG is playing ? So you can switch at any moment between whatever channel you need, without copying anything, only updating the pointer. You decode a number of virtual channels and decide by the way of the pointer, which one is physically rendered. At least, that's how I understand it Wink

By hit9918

Prophet (2905)

hit9918 さんの画像

02-08-2021, 22:28

Metalion wrote:

So you can switch at any moment between whatever channel you need, without copying anything, only updating the pointer. You decode a number of virtual channels and decide by the way of the pointer, which one is physically rendered.

Yes!

By ro

Scribe (4483)

ro さんの画像

03-08-2021, 11:36

I do it like this:
I have all replayers (musix and sfx) write their data to a 13 bytes buffer. Then at the end, I'll dump the buffer to PSG. That way, SFX and channels can overwrite each other at any given moment. With priorities on SFX / channels you can decide what needs to be played and what can be overwritten.

By Metalion

Paragon (1446)

Metalion さんの画像

04-08-2021, 09:23

The 13 bytes being mirrors of PSG registers, as a final result, right ?

By ro

Scribe (4483)

ro さんの画像

04-08-2021, 10:37

correct. So all replayers write to the buffer. The last part is to dump the bytes to PSG. So yeah, it is a mirror, or a cache, or ...well, that Smile

ページ 1/2
| 2