About the WAV Loop & Cue Point chunks


#1

Hi,

I’m walking since few days into the documentation to find a way to read and write embedded loop & markers (cue point) informations in my WAV files?

I’ve checked the BWAV metadata, i’ve the informations about the originator (Logic Pro in my case) … but nothing else.

Maybe i’m wrong but nothing to do that?

Thanks by advance,
Max


#2

I’ve no idea about those values, but if you can let me know how they work, I’d be happy to add them to the bwav info - you can see what’s there at the moment if you look inside the WavAudioFormat class


#3

Ofcourse, you can check this section :
http://www.sonicspot.com/guide/wavefiles.html#cue

I don’t know how you can implement that inside the WavFormatReader class but that can be very usefull.


#4

I’ve check the juce_WavAudioFormat.cpp source and it’s clear, for the moment you get only the format (fmt), data (ofcourse:) and bwav datas (bext) chunks.

You can see in the WAV format description link that number of others possible chunks, not all are very usefull. But some are very powerfull and can find their place in many situations.

The Sampler (smpl) & Instrument (inst) chunks are very usefull for sampler-like application. Especially for the loop information in my case :slight_smile:

Maybe you can just provide a method to access to the raw chunks blocks?


#5

The only problem with offering access to the chunks would be that you wouldn’t really want to store them in memory, because they might be enormous. I’ll have a think about this.


#6

I’m revisiting this issue with more success than my last attempt.

I’m trying to gain access to the markers in audio files too. This is pretty straightforward to read these for both AIFF (‘MARK’ chunk) and WAV ('cue ’ and ‘LIST’ chunks combined).

I’ve written some helper functions to return the seek position for any given chunk name e.g. for example WAV version:

[code]int64 AudioIOHelper::getWavChunkPosition(InputStream* input, const char* name)
{
const int64 originalPosition = input->getPosition();
int64 currentPosition = 0;
input->setPosition(currentPosition);

int64 chunkPosition = -1;

if (input->readInt() == chunkName ("RIFF"))
{
	if(chunkName(name) == chunkName ("RIFF"))
	{
		chunkPosition = currentPosition;
		goto exit;
	}
	
	const uint32 len = (uint32) input->readInt();
	const int64 end = input->getPosition() + len;
	
	currentPosition = input->getPosition();
	
	if (input->readInt() == chunkName ("WAVE"))
	{
		if(chunkName(name) == chunkName ("WAVE"))
		{
			chunkPosition = currentPosition;
			goto exit;
		}
		
		currentPosition = input->getPosition();
		
		while (currentPosition < end && !input->isExhausted())
		{
			const int chunkType = input->readInt();
			
			if (chunkType == chunkName(name))
			{
				chunkPosition = currentPosition;
				goto exit;
			}
			
			uint32 length = (uint32) input->readInt();
			const int64 chunkEnd = input->getPosition() + length + (length & 1);

			input->setPosition (chunkEnd);
			currentPosition = chunkEnd;
		}
	}
}

exit:
input->setPosition(originalPosition);
return chunkPosition;
}[/code]

So I can easily then go to the relevant chunks and pull out the data. I don’t feel anything here is too hacky (even if it would be more elegant to be part of the WAV and AIFF AudioFormat subclasses).

Writing is more tricky unless someone has a more elegant solution. I can easily write the addition chunks at the end of the file but since the WAV and AIFF AudioFormatWriter subclasses write the header as they are deleted (and these can’t be subclassed as far as I can see, even if that would help) my additional chunks’ sizes can’t be inlcuded in the ‘FORM’/‘RIFF’ headers’ size. Some audio apps kindly read these chunks anway but of course that’s a very poor assumption.

The only thing I can think of doing (which IS hacky) is to
[list]
[]delete the AudioFormatWriter (to allow it to close the stream and write the “incorrect” header) [/]
[]reopen the file as an InputStream, seek to position 4 (where the ‘FORM’/RIFF’ size is), and read the size it thinks it is, then delete that stream[/]
[]reopen the file as an OuputStream, seek to position 4 and write the original size, plus the size of my additional chunk(s), then delete that stream[/][/list]

E.g., for WAV format:

[code]void AudioIOHelper::writeWavCuePoints(AudioFormatWriter* &writer, FileOutputStream* output, MyCuePointData const& cues)
{
uint32 sizeWritten = 0;

… write the additional chunks, keeping track of the sizeWritten …

if(sizeWritten > 0)
{
	File file = output->getFile();
	delete writer; // force the file to close and write the (incorrect) header
	writer = 0; // zero the caller's writer
	
	// find the current size it wrote to the header
	FileInputStream* input = file.createInputStream();
	input->setPosition(4);
	uint32 originalSize = input->readInt();
	delete input;
	
	output = file.createOutputStream();
	
	// write the new header adding on our additional content
	output->setPosition(4);
	output->writeInt(originalSize + sizeWritten);
	
	delete output;
}

}
[/code]

This works but it’s not elegant and I can forsee it creating problems if not careful.


#7

Sorry to bump this again…but…

Jules, I guess the answer is I should write something to slot into these audio formats in the style of the SMPLChunk and BWAVChunk structures (in the WavAudioFormat) that you can add to Juce?


#8

[quote=“martinrobinson”]Sorry to bump this again…but…

Jules, I guess the answer is I should write something to slot into these audio formats in the style of the SMPLChunk and BWAVChunk structures (in the WavAudioFormat) that you can add to Juce?[/quote]

That would certainly be very helpful if you need me to do it in a hurry!


#9

Hey Martin, just a comment on your code, i’d break out of your loop if length == 0 (oh lord help us all, it does happen!)


#10

Hmm. Thanks. I just went through and put in a bunch of these:

But then looking again I can’t see why the getPosition() followed by setPosition(position+length) doesn’t have the same effect.