Wahwah effect on 16-bit PCM data

I’m currently writing a game that makes use of this effect, but since OpenAL-Soft does not have the EFX effect implemented, I’ve been attempting to write it in the engine.

I like the process in Audacity’s Wahwah.cpp, but apparently the float values used are totally different than what I’m expecting. Another problem is that the PCM data I’m trying to give it is a series of shorts (this is Java, by the way) since the files are 16-bit FLAC, so even if the float values were what I thought they were, the types just get messy and things explode (in my ear (ack)).

My question is, what do I need to do to the PCM data to get it to work with the Wahwah effect? Alternatively, what other wahwah algorithm is available that lets me process the shorts as they are?

Here’s the cpp file I’m looking at, for reference:
http://code.google.com/p/audacity/source/browse/audacity-src/trunk/src/effects/Wahwah.cpp
In particular, EffectWahwah::ProcessSimpleMono().

Thanks in advance!

A “Wahwah” effect is basically a sweeping filter. Typically it’s a low-pass filter with resonance at the filter frequency.
The Audacity Wahwah effect uses a low frequency sine to sweep the filter frequency up and down.
It may be easier to write the effect from scratch rather than adapt the Audacity code.

I ended up experimenting with a few other forms of the effect with no luck, so I returned to Audacity’s version.

It actually seems to do… something with the wah oscillation/resonance, but the resulting audio has a weird effect to it. You can hear the wah’s effect, but I guess it’s just adding it on top of the source data?

Source file: http://www.flibitijibibo.com/files/musicTest.flac
Resulting audio: http://www.flibitijibibo.com/files/musicTestFail.flac

This is the source of the method:

/** Creates a wahwah'd version of the PCM data.
 * @param sourceData The 16-bit stereo PCM data
 * @param sampleRate The sample rate of the PCM data
 * @return The wahwah'd PCM data
 */
private ByteBuffer createWahData(ByteBuffer sourceData, int sampleRate) {
	ByteBuffer result = BufferUtils.createByteBuffer(sourceData.limit());

	// Create the separate channels
	ByteBuffer leftChannelBytes = BufferUtils.createByteBuffer(sourceData.limit() / 2);
	ByteBuffer rightChannelBytes = BufferUtils.createByteBuffer(sourceData.limit() / 2);
	for (int x = 0; x < sourceData.limit() - 2; x += 2) {
		leftChannelBytes.put(sourceData.get(x));
		rightChannelBytes.put(sourceData.get(x + 1));
	}
	leftChannelBytes.rewind();
	rightChannelBytes.rewind();

	// BEGIN WahWah.cpp ADAPTION
	
	// #define lfoskupsamplse = 30
	int lfoskipsamples = 30;
	// EffectWahwah::EffectWahwah()
	float freq = 1.5f;
	float depth = 0.7f;
	float res = 2.5f;
	float freqofs = 0.3f;
	// EffectWahwah::NewTrackSimpleMono()
	double lfoskip = (freq * 2 * Math.PI / (double) sampleRate);
	int skipcount = 0;
	double xn1 = 0, xn2 = 0, yn1 = 0, yn2 = 0;
	double b0 = 0.0, b1 = 0.0, b2 = 0.0;
	double a0 = 0.0, a1 = 0.0, a2 = 0.0;
	// EffectWahwah::ProcessSimpleMono()
	double frequency, omega, sn, cs, alpha;
	double in, out;

	// We are currently just messing with the left channel.
	for (int x = 0; x < leftChannelBytes.limit(); x += 2) {
		in = leftChannelBytes.getShort(x);

		if ((skipcount++) % lfoskipsamples == 0) {
			frequency = (1 + Math.cos(skipcount * lfoskip)) / 2;
			frequency = frequency * depth * (1 - freqofs) + freqofs;
			frequency = Math.exp((frequency - 1) * 6);
			omega = Math.PI * frequency;
			sn = Math.sin(omega);
			cs = Math.cos(omega);
			alpha = sn / (2 * res);
			b0 = (1 - cs) / 2;
			b1 = 1 - cs;
			b2 = (1 - cs) / 2;
			a0 = 1 + alpha;
			a1 = -2 * cs;
			a2 = 1 - alpha;
		}
		out = (b0 * in + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2) / a0;
		xn2 = xn1;
		xn1 = in;
		yn2 = yn1;
		yn1 = out;

		// Prevents clipping
		if (out < -32768.0)
			out = -32768.0;
		else if (out > 32767.0)
			out = 32767.0;

		leftChannelBytes.putShort(x, (short) (out));
	}

	// END WahWah.cpp ADAPTION

	// Create the final ByteBuffer
	for (int x = 0; x < sourceData.limit() / 2; x++) {
		result.put(leftChannelBytes.get(x));
		result.put(rightChannelBytes.get(x));
	}
	result.rewind();
	return result;
}

Good news! I’m a dunce!

You’ll notice at the top of that method that I only pass half of each sample to the channel buffers. This meant that I was editing half of each sample, which caused that bizarre addition-of-resonance effect. Actually passing the whole sample fixes the problem.

Works just fine now, don’t mind me!