Binary .wav to AudioBuffer?

Hi,

I’m making a sampler plugin with fixed .wav files included in it. It should be able to play these .wav files simultaneously according to MIDI messages received.

My question is If I put .wav files to Projucer how can I easily get the binary data from files and pass it on to AudioBuffer?

Any luck with turning source audio file to binary code?

MemoryInputStream inputStream(BinaryData::data_ptr, BinaryData::data_ptr_size);
WavAudioFormat wavFormat();
std::unique_ptr<AudioFormatReader> reader(wavFormat.createReaderFor(inputStream, false));
AudioBuffer<float> buffer(reader->numChannels, reader->lengthInSamples);
reader->read(&buffer, 0, buffer.getNumSamples(), 0, true, true);

Totally untested, but it should work or be very close. At the end, buffer will contain a floating point copy the contents of the wav file in memory. Note that everything will be freed when leaving scope, so make sure to return buffer or something.

2 Likes

Thanks, but I am trying to see how the data_ptr to ‘static const unsigned char’ in my auto generated BinaryData.cpp can have flaoting point precision of say 16 bit?

Unsigned char has 0-255 range and is 8 bit right? I cannot figure that one out at all.

The data pointer is a binary file content, therefore it has the wave header included.

The AudioFormatReader uses that information. In the AudioBuffer it is always converted to float arrays.
If you want to use double arrays, you have to convert them yourself, since the AudioFormatReader doesn’t support double precision. At least that was stated in a recent thread, haven’t checked myself.

1 Like

Very interesting.
I don’t have a quick way of listening to a sample played from such an array to judge, but did you hear the quality of such audio being played back? 8 bit audio is noticeably more noisy and distorted so you probably wouldn’t be too happy hearing it that way unless you work in audio designed in 8 bits (it’s a desired effect then).

If you did not hear any issues then most likely in some way it gets the source 16 or higher bit depth.

Thanks

Like I said, the array from the binary data is not an audio stream. It is a file content of a WAV file, that is read and interpreted by the AudioFormatReader.

Yes, you are right, 8 bit audio would sound very different.

HTH

1 Like

It does help thanks.

I am going to test the hell out of this as soon as I can to see how this conversion works.

"the array from the binary data is not an audio stream"
Yes, it is an array of unsigned char type values and audio stream would be signed floating point which could represent sinusoid with its positive and negative peaks. Projucer converts it from WAV file that way and then back if I understand correctly.

Thanks again

Sort of. Projucer is completely agnostic in terms of what you drag into the BinaryData file. There is the preview just for convenience. But in the array it is really the contents of the file, each byte a number.
There is no difference, if the AudioFormatReader reads from an actual file or from BinaryData, only that BinaryData is contained in the executable’s binary file.

You are an educator.

Thanks

MemoryInputStream inputStream(BinaryData::data_ptr, BinaryData::data_ptr_size);
WavAudioFormat wavFormat();
std::unique_ptr<AudioFormatReader> reader(wavFormat.createReaderFor(inputStream, false));
AudioBuffer<float> buffer(reader->numChannels, reader->lengthInSamples);
reader->read(&buffer, 0, buffer.getNumSamples(), 0, true, true);

I am struggling with this myself now, it’s crashing in wavFormat.createReaderFor as it seems to want to delete the inputstream object.
Just wondering if anyone can post another snippet?

Maybe just :

MemoryInputStream* inputStream = new MemoryInputStream(BinaryData::data_ptr, BinaryData::data_ptr_size);

One of those rare cases where just using a raw pointer and new is acceptable. (The audio format reader deletes the passed in object.)

Thanks @Xenakios

I got it working by removing the unique_ptr bit.

MemoryInputStream inputStream (BinaryData::lo_wav, BinaryData::lo_wavSize, false);
WavAudioFormat wavFormat;
AudioFormatReader* reader = wavFormat.createReaderFor (&inputStream, false);
reader->read (&buffer, 0, reader->lengthInSamples, 0, true, true);

Just for those who are new, you also need to set the buffer size before loading (at least with the latest version of JUCE)

	void readAudioFile(AudioSampleBuffer& buffer, const void* data, size_t sizeBytes)
	{
                WavAudioFormat wavFormat;
		MemoryInputStream inputStream(data, sizeBytes, false);
		AudioFormatReader* reader = wavFormat.createReaderFor(&inputStream, false);
		//
		if (reader != nullptr)
		{
			buffer.setSize(reader->numChannels, reader->lengthInSamples);
			reader->read(&buffer, 0, reader->lengthInSamples, 0, true, true);
		}
	}
1 Like

But this is leaking the AudioFormatReader. So I don’t know when to delete it correctly.

1 Like

Indeed, the createReaderFor is not yet converted to return an unique_ptr. But you can create one yourself:

void readAudioFile(AudioSampleBuffer& buffer, const void* data, size_t sizeBytes)
{
	WavAudioFormat wavFormat;
	auto* inputStream = new MemoryInputStream (data, sizeBytes, false); // createReaderFor will take ownership
	std::unique_ptr<AudioFormatReader> reader (wavFormat.createReaderFor (inputStream, false));
	if (reader.get() != nullptr)
	{
		buffer.setSize(reader->numChannels, reader->lengthInSamples);
		reader->read(&buffer, 0, reader->lengthInSamples, 0, true, true);
	}
	else
	{
		// if createReaderFor failed, it will not delete, so you could use the inputStream in a fallback
		delete inputStream;
	}
	// reader is automatically deleted by using uique_ptr
}

Hope that helps (and I didn’t make any typo / mistakes :wink: )

EDIT: and reading the docs simplifies things, the bool in createReaderFor allows the stream to be always deleted:

void readAudioFile(AudioSampleBuffer& buffer, const void* data, size_t sizeBytes)
{
	WavAudioFormat wavFormat;
	std::unique_ptr<AudioFormatReader> reader (wavFormat.createReaderFor (new MemoryInputStream (data, sizeBytes, false), true);
	if (reader.get() != nullptr)
	{
		buffer.setSize(reader->numChannels, reader->lengthInSamples);
		reader->read(&buffer, 0, reader->lengthInSamples, 0, true, true);
	}
	// reader is automatically deleted by using uique_ptr
}
2 Likes

(hug) Thanks!!!