TriggerFx sometimes gets stuck on a note

Topics related to the API, programming discussions & questions, coding tips, bugs, etc. should go here.
Post Reply
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

TriggerFx sometimes gets stuck on a note

Post by Artcfox »

Greetings everyone. My name is Matt, and I'm new to the UZEBOX community. So far I've been very impressed with what the UZEBOX is capable of.

I am trying to use TriggerFx() to play sound effects for a game I'm working on, but sometimes it gets stuck on a note, and then after a while (not sure the trigger) it gets unstuck and continues playing the sound. I read the tutorials, and the wiki, and forum posts, but I'm still not sure whether I want true or false for the last parameter. It doesn't seem to matter, as the note sometimes gets stuck either way.

It's hard to pin down exactly what I'm doing to cause this to happen, but I have noticed it more when I'm attempting to play multiple sounds very close together in time.

Here is what my patches.inc file looks like:

Code: Select all

//FX: jump
const char patch00[] PROGMEM ={
0,PC_WAVE,3,
0,PC_ENV_SPEED,-8,
0,PC_PITCH,80,
1,PC_NOTE_UP,4,
1,PC_NOTE_CUT,0,
0,PATCH_END
};

//FX: kill monster
const char patch01[] PROGMEM ={
0,PC_WAVE,4,
0,PC_ENV_SPEED,-8,
1,PC_PITCH,84,
1,PC_PITCH,80,
1,PC_PITCH,81,
1,PC_PITCH,80,
1,PC_NOTE_CUT,0,
0,PATCH_END
};

//FX: collect cookie
const char patch02[] PROGMEM ={
0,PC_WAVE,4,
0,PC_ENV_SPEED,-8,
1,PC_PITCH,95,
1,PC_NOTE_UP,2,
1,PC_NOTE_UP,2,
1,PC_NOTE_CUT,0,
0,PATCH_END
};

//FX: player death
const char patch03[] PROGMEM ={
0,PC_WAVE,4,
0,PC_ENV_SPEED,-16,
1,PC_PITCH,54,
1,PC_NOTE_DOWN,2,
1,PC_NOTE_DOWN,3,
1,PC_NOTE_CUT,0,
0,PATCH_END
};
	
const struct PatchStruct patches[] PROGMEM = {
{0,NULL,patch00,0,0},
{0,NULL,patch01,0,0},
{0,NULL,patch02,0,0},
{0,NULL,patch03,0,0},
};
And here is how I'm calling the effects from various places in my code:

Code: Select all

TriggerFx(3, 128, true);

Code: Select all

TriggerFx(1, 128, true);

Code: Select all

TriggerFx(2, 128, true);

Code: Select all

TriggerFx(0, 128, true);
I'm not 100% clear what the voice-stealing algorithm does, but I get the same effect with the last parameter true or false.

Can someone point me in the right direction as to why a note would get stuck on sometimes? I know that I'm running very close to the RAM limit of the chip (mode 3, 30x28, no scrolling, 24 RAM tiles), but it still happens even when I use fewer RAM tiles, so that might be a red herring.
User avatar
D3thAdd3r
Posts: 3175
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: TriggerFx sometimes gets stuck on a note

Post by D3thAdd3r »

Hey Matt, welcome aboard!

I don't think 24 ram tiles should cause any trouble depending on the game, how many bytes of ram free do you have of the 4096 total?

Looking at the patches I don't see why they would cause problems, do you have music running while the sound effects are playing? Silly question, but you are sure it isn't game logic getting hung and repeatedly calling the effect each frame? What is the master volume set to?

BTW the last parameter in TriggerFx() is "retrigger", so if true, and a sound effect is still playing when it gets called again, simply stop that effect and start it again from the beginning(using the same channel). If false, it let's the old instance continue on it's channel, and starts on a different(lower) channel so they blend/play in parallel. The voice stealing tries to use channel 3, if busy then channel 2, if that's busy too then 2 sfx are already playing so use the oldest one. The purpose is to allow a good amount of sfx to sound good with music. Generally the most important parts of the song go on the lower channels so the sound effects don't constantly disrupt important lead melodies too bad when they steal channels.

It's possible to roll a more customized version of all this pretty easily if you are trying to do something complex with sound effects also.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: TriggerFx sometimes gets stuck on a note

Post by Artcfox »

Thanks! Your games are wicked awesome.

I do not have music running, and the game logic is not hung. I haven't actually called anything to set the master volume. Do I need to?

The only other music related thing I've called is in the beginning:

Code: Select all

InitMusicPlayer(patches);
I have my:

Code: Select all

#include "data/patches.inc"
in a different .c file, and in my main .c file, I have:

Code: Select all

extern const struct PatchStruct patches[] PROGMEM;
at the top, above the call to InitMusicPlayer.


Thanks for clearing up the retrigger flag. I'm still at a loss for why this is happening, I'm really just going for super basic sound effects. I'm not sure exactly how much RAM my code is using. When I compile it, I'm getting:

Code: Select all

Program:   37752 bytes
(.text + .data + .bootloader)

Data:       3264 bytes
(.data + .bss + .noinit)
but I don't believe that's showing me everything that's on the stack.
User avatar
D3thAdd3r
Posts: 3175
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: TriggerFx sometimes gets stuck on a note

Post by D3thAdd3r »

I tried those effects and I can't get them to stick no matter how fast I trigger them in a simple sfx testing build. SetMasterVolume() isn't necessary, just grasping at straws because I don't see why it's happening. Everything you've shown is right.

The ram usage wont reflect the stack, but that should be a safe value. ~3800 bytes +/-50 is trouble depending on the game, recursion will kill that easily though. Stack issues usually come with pretty spectacular graphical glitches and crashes that are hard to miss.

What do you have for KERNEL_OPTIONS in your makefile?
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: TriggerFx sometimes gets stuck on a note

Post by Artcfox »

My KERNEL_OPTIONS:

Code: Select all

KERNEL_OPTIONS  = -DVIDEO_MODE=3 -DINTRO_LOGO=0 -DSCROLLING=0
KERNEL_OPTIONS += -DMAX_SPRITES=12 -DRAM_TILES_COUNT=24 -DSCREEN_TILES_V=28
and my CFLAGS:

Code: Select all

CFLAGS = $(COMMON)
CFLAGS += -Wall -gdwarf-2 -std=gnu99 -DF_CPU=28636360UL -O3 -fsigned-char -ffunction-sections
CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d
CFLAGS += $(KERNEL_OPTIONS)
(I changed -Os to -O3, because otherwise my stuff ran too slow, but that is a recent change, the behavior is the same with -Os).

If I change my RAM_TILES_COUNT to 25, then the graphics go completely wonky, which is why I suspect that I'm pretty close to the RAM limit.

It usually stops within a few seconds, but I'm striving for perfection. ;)

EDIT: I tried changing RAM_TILES_COUNT to 12, but it still hangs on a note sometimes.
User avatar
D3thAdd3r
Posts: 3175
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: TriggerFx sometimes gets stuck on a note

Post by D3thAdd3r »

This gets you back a bunch of ram and cycles for free even if it doesn't fix the issue.

Code: Select all

KERNEL_OPTIONS += -DSOUND_MIXER=1
On the ram thing I haven't run that tall of a screen for a long time, but I don't think that effects the % of ram you can get away with so it seems a little strange. Some games do have different requirements though. I see nothing wrong with that makefile. I am starting to suspect some hidden thing in the game logic or maybe running out of cycles. You have a WaitVsync(1) somewhere in your game loop I imagine, and your emulator and kernel versions aren't too far out of date? If it's not a top secret game perhaps you could post a hex to see what the problem looks/sounds like, it would verify it's not an emulator issue. From what I understand of it, I have not run into this problem before.

I would start commenting out as much as possible from the game loop that isn't required to get the sfx to play. You should at least be able to get those to work without hanging if no code is running besides triggering sfx with a button push loop. If the problem remained there, we could eliminate the possibility of it being game code. More likely, you could find the issue as you incrementally enable pieces of code that were commented out and see the issue reappear.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: TriggerFx sometimes gets stuck on a note

Post by Artcfox »

I'll look more into that kernel option when I get home tonight. A quick test now with -DSOUND_MIXER=1 set makes the game feels like I've hit the cycle limit again, but I don't have time now to give it a proper test, or to check to see if it fixes the note sustaining issue.

If I compile with -Os, I definitely run out of cycles, but with -O3 I don't, but I suspect that I'm still very close to the cycle limit. The symptoms that I've seen that lead me to believe I've hit the cycle limit are things slow down/get choppy. Could hitting the cycle limit cause the ring buffer used for sounds to go wonky?

I do have WaitVsync(1), and I last sync'd from git in the beginning of June when I got my UZEBOX. I don't think it's an emulator issue, because it also happens on the real hardware.

It's not top secret, but I'm really hoping to have it more complete before I debut it. I'm not sure the best way to capture a teaser/trailer. I don't have a capture device, so I'll have to figure something out.

I haven't dug into the internals of TriggerFx() yet, but could it be that the sound engine is missing an interrupt?

I did find a way to reproduce it pretty regularly on actual hardware with a certain test level. The only thing that I need to do is to run right, hit jump (jump sound plays) and then stomp on an enemy (squished sound starts to play, and if I caught it at the right time, then it sustains a single note near the beginning of the squished sound), meanwhile I continue running to the right, and a few seconds later the note that's being sustained finishes, and the rest of the squished sound effect finishes playing, and things are quiet again.

Nothing actually hangs in terms of gameplay stopped, it's just that one of the notes from a sound effect gets sustained for a few seconds—usually around when I have multiple sound effects playing at the same time, which also happens to be when I'm running more game logic code than normal, because typically that also means that my collision checks succeed, and it does more work killing enemies, etc.

I really appreciate these tips. I'll try stripping things out tonight to see if lowering the cycles fixes the issue.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: TriggerFx sometimes gets stuck on a note

Post by Artcfox »

I guess the emulator doesn't run the same on all computers, because now that I'm back on my real computer (despite it being crappier), it runs as smooth as ever with the following kernel options set

Code: Select all

KERNEL_OPTIONS  = -DVIDEO_MODE=3 -DINTRO_LOGO=0 -DSCROLLING=0 -DSOUND_MIXER=1 -DSOUND_CHANNEL_4_ENABLE=0
KERNEL_OPTIONS += -DMAX_SPRITES=7 -DRAM_TILES_COUNT=24 -DSCREEN_TILES_V=28
And wow this frees up a lot of RAM! I can use even more RAM tiles now! :D This is great! I'm not exactly sure what:

Code: Select all

-DSOUND_CHANNEL_4_ENABLE=0
buys me in terms of RAM/cycle savings, but I'm not using the PCM/noise channel, so I figure that I'll disable that channel.

Unfortunately the sustained note issue still persists, even when I've commented out some of the code (background animations) to free up some cycles. I'll keep looking though. The other thing I was thinking about trying is to play background music, because it seems that playing another sound effect often (but not always) stops any of the sustained notes.
User avatar
D3thAdd3r
Posts: 3175
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: TriggerFx sometimes gets stuck on a note

Post by D3thAdd3r »

For inline mixer I don't think turning channel 4 off gains much or even nothing? You can try some of these, it depends on the game, but sometimes it yields benefits:

Code: Select all

CFLAGS += -mcall-prologues -fdata-sections -ffreestanding -fomit-frame-pointer
DSOUND_MIXER=1 should recover you that nice 512 byte chunk of ram and a couple thousands cycles. The original mixer used the ring buffer to store precalculated sound bytes before cycle perfect screen rendering happens. Then each HSYNC spit some of that data out to the PWM to make physical sound. The inline mixer gets rid of the buffer and crunches all that stuff(during HSYNC) as the screen is being rendered, so it's a pretty intense piece of work that gives you more free ram and cycles(since it doesn't precalculate and uses otherwise wasted cycles).

With enough ram to push to stack it can be safe to "miss vsync"..maybe it just takes ie. 2.5 frames to finish 1 game tick sometimes. If you never miss vsync you can get away with using more ram because it never has a bunch of stuff on the stack when the interrupt comes. As far as I have ever seen, all those "impossible" wonky glitch situations are the result of a stack overflow. That stuff is pretty hard to estimate, since with extra cycles perhaps screen rendering interrupt happens at a different point in logic, like perhaps sprites x is all set to SCREEN_TILES_H*TILE_WIDTH to clear them then before sprites get setup on frame 3 the frame is drawn. I've wasted a lot of time on some strange things that turned out to be just that, so I try not guess too deep into it past "too many cycle" or "not too many". Does commenting out WaitVsync(1) do anything different?

By gameplay hanging I guess I meant a logic oversight. You could print a '0' at the top left of the screen at the beginning of each frame, then whatever collision causes the sound effect prints a '1' there. If you saw the '1' "hung" there too after the squishing event it would mean something like that was happening. Had stuff like that happen more times than I can count. Pretty tough to say for sure, but one way or another it will get worked out.

Another thing to consider is there is only some few thousands cycles total to do the game logic each frame with a screen that tall. Some games need things that takes cycles and there aren't any tricks to entirely get around it. I've made some pretty simple games where a sacrifice to use a screen of 26 tiles high was still the right thing to do. Worth trying it, just have to make sure you don't have anything writing past VRAM then.

After all this analysis and convincing you it's a cycles issue I'm just waiting for Alec to chime in and point out something obvious here as I'm out of any other ideas :lol:
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: TriggerFx sometimes gets stuck on a note

Post by Artcfox »

That's pretty awesome, and yes the stuff the kernel does is pretty amazing.

I don't think that I'm missing vsync, but I hadn't thought that the ISR would use more RAM to push to the stack if I did. Running really close to the RAM limit seems like a bad idea if you miss vsync.

I commented out the WaitVsync(1) call, and the game runs impossible-to-play-fast, so I'm guessing that I'm not missing vsync normally. Whether there are some crazy situations that by chance alignment all occur during a single frame, like collecting multiple treasures and stomping multiple enemies, (playing sounds for each) not to mention if that happens to be a frame where I'm animating all the treasures, and some entities. That seems very unlikely, but it certainly is possible.

That 0/1 thing sounds like a really good trick to try. Right now I'm going through my code again and seeing if there are any places where I can cut out even more cycles.
Post Reply