1

I'd like to reverse an existing audio file (e.g. WAV, CAF, ...) on iOS. Any suggestions on how to achieve this? Open-source libraries?

Tom Nys
  • 579
  • 6
  • 13
  • In case it helps, here's a tutorial in C# on how to reverse a .wav file: https://medium.com/swlh/reversing-a-wav-file-in-c-482fc3dfe3c4 – David Klempfner Apr 19 '20 at 01:54

4 Answers4

3

I have worked on a sample app, which records what user says and plays them backwards. I have used CoreAudio to achieve this. Link to app code.

As each sample is 16-bits in size(2 bytes)(mono channel). You can load each sample at a time by copying it into a different buffer by starting at the end of the recording and reading backwards. When you get to the start of the data you have reversed the data and playing will be reversed.

    // set up output file
AudioFileID outputAudioFile;

AudioStreamBasicDescription myPCMFormat;
myPCMFormat.mSampleRate = 16000.00;
myPCMFormat.mFormatID = kAudioFormatLinearPCM ;
myPCMFormat.mFormatFlags =  kAudioFormatFlagsCanonical;
myPCMFormat.mChannelsPerFrame = 1;
myPCMFormat.mFramesPerPacket = 1;
myPCMFormat.mBitsPerChannel = 16;
myPCMFormat.mBytesPerPacket = 2;
myPCMFormat.mBytesPerFrame = 2;


AudioFileCreateWithURL((__bridge CFURLRef)self.flippedAudioUrl,
                       kAudioFileCAFType,
                       &myPCMFormat,
                       kAudioFileFlags_EraseFile,
                       &outputAudioFile);
// set up input file
AudioFileID inputAudioFile;
OSStatus theErr = noErr;
UInt64 fileDataSize = 0;

AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);

theErr = AudioFileOpenURL((__bridge CFURLRef)self.recordedAudioUrl, kAudioFileReadPermission, 0, &inputAudioFile);

thePropertySize = sizeof(fileDataSize);
theErr = AudioFileGetProperty(inputAudioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);

UInt32 dataSize = fileDataSize;
void* theData = malloc(dataSize);

//Read data into buffer
UInt32 readPoint  = dataSize;
UInt32 writePoint = 0;
while( readPoint > 0 )
{
    UInt32 bytesToRead = 2;

    AudioFileReadBytes( inputAudioFile, false, readPoint, &bytesToRead, theData );
    AudioFileWriteBytes( outputAudioFile, false, writePoint, &bytesToRead, theData );

    writePoint += 2;
    readPoint -= 2;
}

free(theData);
AudioFileClose(inputAudioFile);
AudioFileClose(outputAudioFile);
Kiran Balegar
  • 916
  • 6
  • 16
2

Kiran's answer in Swift 2.2:

    let forwardAudioURL: NSURL = ... // wherever your original audio is
    let reversedAudioURL: NSURL = ... // wherever you want the reversed file to go

    // Load forward audio into originalAudioFile
    var originalAudioFile: AudioFileID = nil
    let possibleError1 = AudioFileOpenURL(forwardAudioURL,
                                          AudioFilePermissions.ReadPermission,
                                          0,
                                          &originalAudioFile)

    // Load the size in bytes of the original audio into originalAudioSize variable
    var originalAudioSize: Int64 = 0
    var propertySize: UInt32 = 8
    let possibleError2 = AudioFileGetProperty(originalAudioFile,
                                              kAudioFilePropertyAudioDataByteCount,
                                              &propertySize,
                                              &originalAudioSize)

    if possibleError1 != 0 || possibleError2 != 0 {
        // Handle errors if you want
    }

    // Set up file that the reversed audio will be loaded into
    var reversedAudioFile: AudioFileID = nil
    var format = AudioStreamBasicDescription()
    format.mSampleRate = 16000
    format.mFormatID = kAudioFormatLinearPCM
    format.mChannelsPerFrame = 1
    format.mFramesPerPacket = 1
    format.mBitsPerChannel = 16
    format.mBytesPerPacket = 2
    format.mBytesPerFrame = 2
    AudioFileCreateWithURL(reversedAudioURL,
                           kAudioFileCAFType,
                           &format,
                           AudioFileFlags.EraseFile,
                           &reversedAudioFile)

    // Read data into the reversedAudioFile
    var readPoint: Int64 = originalAudioSize
    var writePoint: Int64 = 0
    var buffer: Int16 = 0
    while readPoint > 0 {
        var bytesToRead: UInt32 = 2;
        AudioFileReadBytes(originalAudioFile,
                           false,
                           readPoint,
                           &bytesToRead,
                           &buffer)
        AudioFileWriteBytes(reversedAudioFile,
                            false,
                            writePoint,
                            &bytesToRead,
                            &buffer)
        writePoint += 2
        readPoint -= 2
    }
    AudioFileClose(originalAudioFile)
    AudioFileClose(reversedAudioFile)
mckennac4
  • 46
  • 4
2

A WAV file just contains raw PCM samples. If you play them in the reverse order, you will get the reverse sound. See this question for details. (and also this search)

Community
  • 1
  • 1
AShelly
  • 34,686
  • 15
  • 91
  • 152
  • I take it a PCM sample is just one distinct sound? – Matt Greer Jul 06 '11 at 21:20
  • 1
    A PCM sample is one digital sample of an analog waveform (and sound is ultimately just a waveform... If you play the waveform backwards, you get the reversed sound) See the graphic in http://en.wikipedia.org/wiki/Pulse-code_modulation. Each discrete level is one sample. The WAV file is just an array of those samples (plus a bit of metadata). Reverse the array, feed it to your WAV player and you are done. – AShelly Jul 06 '11 at 21:26
1

Using libsndfile, I was able to read in the recorded audio sample. The following code snippet then shows how to open the input file, and write the output in reversed order. You should note though that all data is read in memory!

SF_INFO info_read;
info_read.format = 0;
SNDFILE* sndfilein = sf_open("my_input_file.caf", SFM_READ, &info_read);

SF_INFO info_write;
info_write.format = info.format;
info_write.channels = info.channels;
info_write.frames = info.frames;
info_write.samplerate = info.samplerate;
info_write.sections = info.sections;
info_write.seekable = info.seekable;

SNDFILE* sndfileout = sf_open("my_output_file.caf", SFM_RDWR, &info_write);

int* buf = new int[8000 * 30];
int framesRead = sf_readf_int(sndfilein, buf, info.frames);
for (int i = 0; i < info.frames; i++)
    sf_writef_int(sndfileout, &buf[info.frames - i], 1);
delete[] buf;

sf_close(sndfilein);
sf_close(sndfileout);
Tom Nys
  • 579
  • 6
  • 13