Some muted knocking sounds when using pitch bend

I have made a MIDI application in C for Windows using Fmod and my own sound files. I connect a MIDI keyboard, and it works fine, except for some minor issues.

Included in the program is a pitch bend function that makes it possible to move or slide the sound between notes by rapidly changing the pitch in small increments. When doing this there is however occasinally, and with irregular intervals, som muted knocking sounds in the background, and I wonder if it could be possible to get rid of these. It also frequently happens that the controls on the PC screen freezes, while it is still possible to play the keyboard. In addition, I have som sound loops going on in the background using other channels.

The sound files are from 2 to 10 seconds long, and I notice that there are more knocking sounds with the longer sound files. I also use a short dsp fade in function, because it softens the playing of the sounds, but I also notice that there is more knocking sounds when using this function than when not using it. I therefore wonder if it is a question about the computer’s capacity. That there are too much processing going on in short time intervals for a normal computer to handle.

I could give the whole code, about 100 lines, but maybe it’s not necessary. I give for now the parts that I believe is most relevant:

First Playsound with the dsp function:

            result = FMOD_System_PlaySound(systemx, sound[soundnr], 0, 1, &channel[chx]);
	result = FMOD_System_Update(systemx);			
	FMOD_Channel_SetFrequency(channel[chx], kfreq * scale[keynumber]);
	FMOD_Channel_SetVolume(channel[chx], Volume);                

            FMOD_System_GetSoftwareFormat(systemx, &rate, 0, 0);
	ERRCHECK(result,101);
	result = FMOD_Channel_GetDSPClock(channel[chx], 0, &dspclock);
	ERRCHECK(result,102);
	FMOD_System_Update(systemx);				
	result =  FMOD_Channel_AddFadePoint(channel[chx], dspclock, 0.0f);
	ERRCHECK(result,102);
	result =  FMOD_Channel_AddFadePoint(channel[chx], dspclock + (rate * 0.05), Volume);
	ERRCHECK(result,102);
            
            FMOD_System_Update(systemx);
	result = FMOD_Channel_SetPaused(channel[chx], 0);

Then the code for the pitch bend:

   // GLOBAL VARIABLES:  
   //  float Slidingpoint[1000];
   //  int S_point;
  //   int Slidingnote[37];
  //   int Slide;

  if(message == 224){
            //keyvalue=((dwParam1>>16)&0xFF); // Value given earlier when pressing a key

	Slide = (int) (keyvalue - 64);				
	FMOD_System_Update(systemx);
	if(keynumber < 12) frequency = Slidingpoint[S_point + Slide];
	if(keynumber > 11 && keynumber < 24) frequency = Slidingpoint[S_point + Slide] / 2;
	if(keynumber > 23) frequency = Slidingpoint[S_point + Slide] / 4;
	
	result = FMOD_Channel_SetFrequency(channel[chx], kfreq * frequency);
	FMOD_System_Update(systemx);	
     }

The variable ‘keyvalue’ increases or decreases as the wheel on the midi keyboard is moved forward or backward. The ‘Slidingpoint’ table is a series of frequencies in small intervalls. The ‘S_point’ variable is the element number in ‘Slidingpoint’ that corresponds to the frequency of the note from which the pitch bend starts. Hope it’s understandable.

Anyway, thanks for a fantastic system, and thanks in advance if someone would try to help me out with this. It would be greatly appreciated.

Sincerely

Hi Keitel

One observation of the code you have shared for playing a sound then adding fade points is that you are racing the mixer clock there and you’re fairly likely to be setting the first fade point in the past so the first fade value the system actually uses will probably be greater than zero. This could lead to a popping artifact (perhaps what you’re describing as knocking).

What I mean by racing the mixer clock is you are querying the master channel group’s DSP clock and then trying to use it before the mixer updates again, which would put the clock value you are using in the past. You’re querying the DSP clock, updating the system, setting fade points on the sound’s channel, updating the system again and then un-pausing the channel. Most often by the time the channel actually starts the master channel group DSP clock will have been updated by the mixer and your first fade point will be in the past, but occasionally you might beat the mixer and your first fade point will have the desired effect. Hence “racing” the mixer.

Your fade points are also being added with sample accuracy while un-pausing a channel is not a sample accurate action. On the occasions where you win the race with the mixer that’s probably working out okay since the fade point and the un-pause action will probably both occur at the beginning of a mix block but it’s not the best practice.

If all you want to achieve is a volume ramp when starting the channel then you can do it without touching fade points simply by creating the channel paused, setting the channel’s volume to zero, un-pausing the channel, and then setting the volume to the desired volume. The system will automatically ramp volume changes for playing channels over 64 samples to avoid popping. You could replace your sound playing function with something like this much simpler implementation (error check omitted for brevity):

FMOD_System_PlaySound(systemx, sound[soundnr], 0, 1, &channel[chx]);
FMOD_Channel_SetFrequency(channel[chx], kfreq * scale[keynumber]);
FMOD_Channel_SetVolume(channel[chx], 0.0f);
FMOD_Channel_SetPaused(channel[chx], 0);
FMOD_Channel_SetVolume(channel[chx], Volume);
FMOD_System_Update(systemx);

That will start the channel playing at volume zero and ramp the volume to Volume over a very short interval. Note that there is no need to update the system between issuing these commands.

If you want to control the duration of the ramp then you’ll need to do something similar to what you are currently doing using fade points, but you need to ensure your fade points are being set in the future and you should use the channel’s start delay to start the channel audibly at the beginning of your fade interval. The basic procedure is:

  • Query the system’s DSP block size using FMOD_System_GetDSPBufferSize
  • Create the channel using FMOD_System_PlaySound (start the channel paused as you are already doing)
  • Get the channel’s parent channel group’s DSP clock using FMOD_Channel_GetDSPClock
  • Set fade points on the channel using the parent channel group’s current DSP clock offset by a multiple of the DPS block size (an offset of 2 DSP blocks should be long enough)
  • Set the channel’s start delay to the same DSP clock as the first fade point using FMOD_Channel_SetDelay
  • Unpause the channel

Putting this in action, your playing sound function would look something like this (again error checking is omitted here):

// local variable: unsigned int dsp_block_len;
FMOD_System_GetSoftwareFormat(systemx, &rate, 0, 0);
FMOD_System_GetDSPBufferSize(systemx, &dsp_block_len);

FMOD_System_PlaySound(systemx, sound[soundnr], 0, 1, &channel[chx]);
FMOD_Channel_SetFrequency(channel[chx], kfreq * scale[keynumber]);
FMOD_Channel_GetDSPClock(channel[chx], 0, &dspclock);
FMOD_Channel_AddFadePoint(channel[chx], dspclock + dsp_block_len * 2, 0.0f);
FMOD_Channel_AddFadePoint(channel[chx], dspclock + dsp_block_len * 2 + (rate * 0.05), Volume);
FMOD_Channel_SetDelay(channel[chx], dspclock + dsp_block_len * 2, 0, 0);
FMOD_Channel_SetPaused(channel[chx], 0);
FMOD_System_Update(systemx);

You should also check out our gapless_playback API example, it does similar logic with start delays using our C++ API and has additional code comments which may aid your understanding.

I’m not sure whether these observations apply to your pitch bending use case or if there might be other causes for those knocking artifacts. If you update your implementation according to this information and you are still getting the artifacts then it might be helpful to share you entire program for additional review.

Cheers
Derek

1 Like

Thanks a lot for your detailed answer. I tested your suggestions and found that the dsp version still sounds the best. But even with your version, I still had some knocking sounds. But then I noticed that you had added the GetDSPBufferSize function, and by searching my program I found that I had set the buffersize, with this command, at 512 in the beginning of the Create section.

FMOD_System_GetDSPBufferSize(systemx, 512, 4) (I’m programming in C).

I don’t remember why I have done this. But my program has many other functions in addition to the midi controller. Anyway, I changed the buffersize to 1024, which I believe to be the default value, and then all the knocking sounds seemed to dissapear for the short sound files (about 1,5 - 2 secons), while the longer ones still had some noice, but now very seldom. Yet, if it is a question of the length of the sound files it’s no problem, as I can easily make them shorter.

So thus the issue seems to be solved first of all by increasing the buffersize both by the Getdspbuffersize command and by doubling it in other dsp functions, as you did.

But I still have a problem with the controls on the UI often freezing when having played with the keyboard for a little while. I can still play with the keyboard, but the selection or list boxes don’t open, and the scrollbars don’t move. But that’s maybe not directly related to Fmod. I should maybe rather ask MSDN about that.

Sincerely

Hi Keitel

Glad to hear this is working for you. The UI issue certainly sounds like more of a matter for MSDN :slight_smile:

A couple of notes for the record.

Your observations about longer vs shorter sound files being more problematic don’t make any sense from an FMOD perspective - the sound file length absolutely does not matter to our mixer.

It’s likely there are other relevant factors in your app in play. If things are working for you then that’s fine but I wanted to point that out to avoid any cargo cult thinking developing around the sound lengths - I wouldn’t want anyone else encountering a similar issue to take it on faith that using shorter samples might be a workaround! And on a similar note for yourself, if you find these problems persist I would avoid getting into a spiral of ever shorter sounds without a better understanding of if or how the sound lengths play into the behavior.

Reducing the DSP buffer size will reduce latency but increases pressure on the mixer, so using larger buffers would make sense if mixer overruns were part of the problem in your case.

Cheers
Derek

1 Like