[RESOLVED - my bad] Problem upsampling

I’m getting odd results from upsampling a pair of float buffers. I have a file that contains pairs of left/right samples which have been converted to short integers, initially created from downsampled audio. I need to upsample and rewrite the file, again as pairs of short integers.

What I do (in the code below) is to read in the data in blocks, splitting the left/right samples into two arrays, and converting to floats as I go. Then I assign the pointer arrays to an AudioBuffer object, which is then used to create an AudioBlock object. Next I upsample that audio block, creating a new AudioBlock with the upsampled data. I get the channel pointers (0 and 1), then loop through that data, re-interleaving the data and re-converting them to short integers.

The problem is that what I see in the resulting file is that the values that get interjected between my original values are not reasonable. They should be values that fall between their adjacent values, but they are not. They seem random to me. I do see the original values in that data, but the values in between don’t make any sense.

Perhaps someone can spot an error in my code or use of the upsampler that would cause this oddness? Thanks much! (By the way, the reason I have all those increments on separate lines is that I was trying to debug exactly where the problem might lie. The fprintf lines are because I copied this code from a stand-alone console app test.)

//--------------------------------------------------------------------------
File* TrackedAudioManager::upsampleFile( File* pOldFile, int downsampleExponent, int downsampleFactor )
{
	// Create a new file in the same location, but with a ".dau" extension
	String newFileName( pOldFile->getFileNameWithoutExtension() );
	newFileName += ".dau";
	File* pUpsampledFile = new File(pOldFile->getSiblingFile(newFileName));
	
	// Create input and output streams
	juce::FileInputStream inStream( *pOldFile );
	juce::FileOutputStream outStream( *pUpsampledFile );
	if (outStream.openedOk())
	{
		outStream.setPosition (0);
		outStream.truncate();
	}

	juce::int64 totalInFrames = inStream.getTotalLength() / (2 * sizeof(short));
	// fprintf( stderr, "Total frames to read = %lld\n", totalInFrames );

	static const int BufferSize = 65536;
	std::array<std::vector<float>, 2> frameBuffers;
	frameBuffers[0].reserve(BufferSize);
	frameBuffers[1].reserve(BufferSize);

	float* pInBuffers[2];
	pInBuffers[0] = frameBuffers[0].data();
	pInBuffers[1] = frameBuffers[1].data();
	
	//
	juce::dsp::Oversampling<float> oversampler( 2, downsampleExponent,
		juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, true, false ); // false, true );
	oversampler.reset();
	oversampler.initProcessing(BufferSize); // max size before upsampling (according to header)

	short inBuffer[2*BufferSize]; // interleaved data takes twice the space!
	short outBuffer[4*BufferSize]; // interleaved data takes twice the space!
	
	//
	juce::int64 remainingFrames = totalInFrames;
	juce::int64 totalFramesWritten = 0;
	while (remainingFrames > 0)
	{
		// Compute the number of frames to read for this loop
		juce::int64 framesToRead = BufferSize;
		if (framesToRead > remainingFrames)
			framesToRead = remainingFrames;
		
		// Read in the buffer in one chunk
		int bytesToRead = static_cast<int>(framesToRead) * 2 * sizeof(short);
		inStream.read( inBuffer, bytesToRead );
		
		// Copy the data from the input buffer into left/right buffers (de-interleaving)
		short* pSource = inBuffer;
		float* pLeft = pInBuffers[0];
		float* pRight = pInBuffers[1];
		for (juce::int64 i = 0; i < framesToRead; ++i)
		{
			// Convert short values to floats for upsampling
			short tmp = *pSource;
			*pLeft = (float)tmp / 32767.0f;
			pSource++;
			pLeft++;
			tmp = *pSource;
			*pRight = (float)tmp / 32767.0f;
			pSource++;
			pRight++;
		}
		
		//fprintf( stderr, "Read %lld frames\n", framesToRead );
		
		// Update the remaining number of frames (for the next/last loop)
		remainingFrames -= framesToRead;
		
		// Now, pInBuffers are two pointers to buffers of size BufferSize.
		// After upsampling, the resulting left-right buffers will be of size 2*BufferSize.

		// Assign the input buffer pointers to an AudioBlock via an AudioBuffer
		juce::AudioBuffer<float> audioInBuffer( pInBuffers, 2, BufferSize );
		juce::dsp::AudioBlock<float> audioInBlock( audioInBuffer );
		// Upsample the data
		juce::dsp::AudioBlock<float> audioOutBlock = oversampler.processSamplesUp( audioInBlock );
		
		// Get the left and right channel buffer pointers
		pLeft = audioOutBlock.getChannelPointer(0);
		pRight = audioOutBlock.getChannelPointer(1);
		
		// Copy the left/right buffers into the outut buffer (interleaving)
		short* pDest = outBuffer;
		juce::int64 framesToWrite = framesToRead * downsampleFactor;
		for (juce::int64 i = 0; i < framesToWrite; ++i)
		{
			// Convert back to short values for writing
			*pDest = (short)(*pLeft * 32767.0f);
			pDest++;
			pLeft++;
			*pDest = (short)(*pRight * 32767.0f);
			pDest++;
			pRight++;
		}
		
		// Write out the buffer in one chunk
		int bytesToWrite = static_cast<int>(framesToWrite) * 2 * sizeof(short);
		outStream.write(outBuffer, bytesToWrite);
		//fprintf( stderr, "Wrote %lld frames\n", framesToWrite );
		totalFramesWritten += framesToWrite;
	}
	
	// fprintf( stderr, "Total frames written = %lld\n", totalFramesWritten );
	
	return pUpsampledFile;
}

Here’s a screenshot of where the first non-zero value appear, both in the original data and in the upsampled date. (This is stereo data, but the second channel is actually empty in this example. The underlined values are the original values, and the circled values are the newly added values.)

just to be sure I would init framesToWrite with audioOutBlock.getNumSamples()

this would be easier to debug as a wav-file, because then you could load it into audacity and just look at the waveform. sometimes intermediate samples are not just somewhere in the middle between the 2 actual samples because it might be a situation of overshoot or something and it would be easier to spot that visually

Good idea. That helped me find the problem. I output my buffers as a wav file for both the input and output, and quickly saw the problem. I forgot that my data was BigEndian (when it was generated on the Mac), and needed to be byte-swapped while converting. Works fine now. Whew! :slight_smile: