Drow::ReversibleAudioSource not functional?


#1

Hello

I needed a way to play soundfiles in reverse, and I found out by reading its doc that the drow::AudioFilePlayerExt class embeds a ReversibleAudioSource that would allow this.

However, the code that links this source to the others has been commented out and, indeed, when I try to enable it, it just outputs silence.

I have read the code but, AFAIK, it should work.

Is there anybody who could help me making this work ?

Thank you very much

Benjamin


#2

Well it depends on what kind of audio source you think you’re going to be playing in reverse. This is a difficult problem, think about MP3 decoding you can’t just read the file backwards. Frames have to be decoded individually. What is your use-case?


#3

Hello

Thanks for your reply !

My use case is the following : I 'd like to be able to load an mp3 file, and scrub in it as if it was an old-fashioned reel-to-reel machine. For this, I am changing the pitch with a ResamplingAudioSource (works very nicely), but I’d also like to play backwards.

I have tried with WMA decoder, MP3 decoder and even by loading a .wav file but I am out of luck. As you rightly pointed out, I think it’s related to the loss of caching and to the fact that it takes a couple of audio buffers to retrieve the audio at the new position.

However, even when reading the file in memory and creating a MemoryInputStream on a wave file, it does not work. So I guess the format decoders have some sort of fifo, even when the format reader latency is very short.

So I wanted to change my approach and decode the whole file into an AudioSampleBuffer, and create a PositionableAudioSource that is able to simply play a sample buffer. There wouldn’t be any internal buffering and, so, it should work. However, it is so simple a player that I thought maybe I could find something in the existing classes to fit my needs but, so far, I haven’t found it.

Benjamin-


#4

Decoding the entire file into memory is definitely an easy way to make that work.


#5

Yeh sorry about that, I didn’t actually get around to finishing that class which is why it is disabled, I should probably remove it from the repository.

I used to have something that works in a similar way and did work so I quickly ported it to the AudioSource subclass but never got around to fully testing it. I’m pretty sure the algorithm makes sense but with audio sources you get a lot of problems because of the background buffering. If you play in reverse and are buffering say 32768 samples, the thread will constantly read all these samples, and reverse them even though you only need the first 512 or similar. It might work if you don’t use a BufferingAudioSource.

Loading the file into memory and playing it back in reverse will be the quickest and easiest way although memory hungry if you’re doing it for a lot of long files at once. I created some other classes such as AudioSampleBufferAudioFormat which you might find useful here. This can be used to play back an AudioSampleBuffer through the usual PositionableAudioSource classes.


#6

What I do in VFLib is have a module called vf_unfinished where I put things that are missing documentation and/or implementation. This way it gets tracked in revision control, and also compiled constantly (to detect breaking changes). When the code is done it is a trivial matter to move it to a different module.


#7

Actually I made it work by :

  • correcting a bug where the buffer reversal did not take the startSample into account. I changed this :

reverseArray (info.buffer->getSampleData (0), info.numSamples);

to that

reverseArray (info.buffer->getSampleData (0)+info.startSample, info.numSamples);

(and also corrected it in the stereo and multichannel case)

  • making sure the source did work correctly even with requests where the info.numSamples did change over time.

To do this, I have added an int64 previousReadPosition to the class, that I init to inputSource->getNextReadPosition(). I use it like that :

void ReversibleAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
{
if (isForwards)
{
previousReadPosition = input->getNextReadPosition();
input->getNextAudioBlock (info);
}
else
{
int64 nextReadPosition = previousReadPosition - info.numSamples;

	if (nextReadPosition < 0 && input->isLooping())
		nextReadPosition += input->getTotalLength();
	
	input->setNextReadPosition (nextReadPosition);
    input->getNextAudioBlock (info);

	if (info.buffer->getNumChannels() == 1)
	{
		reverseArray (info.buffer->getSampleData (0)+info.startSample, info.numSamples);
	}
	else if (info.buffer->getNumChannels() == 2) 
	{
		reverseTwoArrays (info.buffer->getSampleData (0)+info.startSample, info.buffer->getSampleData (1)+info.startSample, info.numSamples);
	}
	else
	{
		for (int c = 0; c < info.buffer->getNumChannels(); c++)
			reverseArray (info.buffer->getSampleData(c)+info.startSample, info.numSamples);
	}
    previousReadPosition = nextReadPosition;
}

}

This, in addition to loading the whole file in memory, works.

Thanks again for your time.

Benjamin-


#8

Great stuff golinvauxb cheers!

I knew I wrote that a long time ago before I got my head around the AudioSourceChannelInfo struct properties in a bit of a rush. I’ve checked in the changes you suggested now so you might want to grab these and see if they work.

Thanks again!


#9

Your’re welcome !

On the other hand, putting this revrsible source AFTER a transport source where resampling is enabled messes things (audible cracks are present when playing the file backwards). There is probably some logic in there that flushes the buffer taps or something when buffer reads are not contiguous. Anyway, I’ve solved this by taking into account the desired resampling factor in a ResamplingSource at the END of the graph. Just to let you know, for the AudioFilePlayer[Ext] might need this.

Bye and thanks to everyone in this great community !