0

I am porting an Android app to iPhone (more like improving the iPhone app based on the Android version) and I need to split and combine large uncompressed audio files.

Currently, I load all the files into memory and split them and combine them in separate functions. It crashes with 100MB+ files.

This is the new process needed to do it:

I have two recordings (file1 and file2) and a split position where I want file2 to be inserted inside file1.

-create the input streams for file1 and file2 and the output stream for the outputfile.

-rewrite the new CAF header

-read the data from inputStream1 until it reaches the split point and I write all that data to the output file. and write it to the output stream.

-read all data from inputStream2 and write it to output file.

-read remaining data from inputStream1 and write it to output file.

Here is my Android code for the process:

    File file1File = new File(file1);
    File file2File = new File(file2);

    long file1Length = file1File.length();
    long file2Length = file2File.length();

    FileInputStream file1ByteStream = new FileInputStream(file1);
    FileInputStream file2ByteStream = new FileInputStream(file2);
    FileOutputStream outputFileByteStream = new FileOutputStream(outputFile);


    // time = fileLength / (Sample Rate * Channels * Bits per sample / 8)
    // convert position to number of bytes for this function
    long sampleRate = eRecorder.RECORDER_SAMPLERATE;
    int channels = 1;
    long bitsPerSample = eRecorder.RECORDER_BPP;
    long bytePositionLength = (position * (sampleRate * channels * bitsPerSample / 8)) / 1000;



    //calculate total data size
            int dataSize = 0;
            dataSize = (int)(file1Length + file2Length);

            WriteWaveFileHeaderForMerge(outputFileByteStream, dataSize,
                    dataSize + 36,
                    eRecorder.RECORDER_SAMPLERATE, 1,
                    2 * eRecorder.RECORDER_SAMPLERATE);




    long bytesWritten = 0;

    int length = 0;

    //set limit for bytes read, and write file1 bytes to outputfile until split position reached
    int limit = (int)bytePositionLength;


    //read bytes to limit
    writeBytesToLimit(file1ByteStream, outputFileByteStream, limit);    
    file1ByteStream.close();


    file2ByteStream.skip(44);//skip wav file header
    writeBytesToLimit(file2ByteStream, outputFileByteStream, (int)file2Length);
    file2ByteStream.close();

    //calculate length of remaining file1 bytes to be written
    long file1offset = bytePositionLength;

    //reinitialize file1 input stream
    file1ByteStream = new FileInputStream(file1);

    file1ByteStream.skip(file1offset);
    writeBytesToLimit(file1ByteStream, outputFileByteStream, (int)file1Length);

    file1ByteStream.close();
    outputFileByteStream.close();

And this is my writeBytesToLimit function:

private void writeBytesToLimit(FileInputStream inputStream, FileOutputStream outputStream, int byteLimit) throws IOException
{
    int bytesRead = 0;
    int chunkSize = 65536;
    int length = 0;
    byte[] buffer = new byte[chunkSize];
    while((length = inputStream.read(buffer)) != -1)
    {
        bytesRead += length;
        if(bytesRead >= byteLimit)
        {
            int leftoverBytes = byteLimit % chunkSize;              
            byte[] smallBuffer = new byte[leftoverBytes];
            System.arraycopy(buffer, 0, smallBuffer, 0, leftoverBytes);
            outputStream.write(smallBuffer);
            break;
        }

        if(length == chunkSize)
            outputStream.write(buffer);
        else
        {
            byte[] smallBuffer = new byte[length];
            System.arraycopy(buffer, 0, smallBuffer, 0, length);
            outputStream.write(smallBuffer);
        }

    }

}

How do I do this in iOS? Using the same delegate for two NSInputStreams and an NSOutputStream looks like it will get very messy. Has anyone seen an example of how to do this (and do it clean)?

rmooney
  • 6,123
  • 3
  • 29
  • 29

1 Answers1

2

I ended up using NSFileHandle. For example, this is the first part of what I am doing.

NSData *readData = [[NSData alloc] init];
NSFileHandle *reader1 = [NSFileHandle fileHandleForReadingAtPath:file1Path];
NSFileHandle *writer = [NSFileHandle fileHandleForWritingAtPath:outputFilePath];

//start reading data from file1 to split point and writing it to file
long bytesRead = 0;
while(bytesRead < splitPointInBytes)
{

    //read a chunk of data
    readData = [reader1 readDataOfLength:chunkSize];
    if(readData.length == 0)break;
    //trim data if too much was read
    if(bytesRead + readData.length > splitPointInBytes)
    {
        //get difference of read bytes and byte limit
        long difference = bytesRead + readData.length - splitPointInBytes;

        //trim data
        NSMutableData *readDataMutable = [NSMutableData dataWithData:readData];
        [readDataMutable setLength:readDataMutable.length - difference];
        readData = [NSData dataWithData:readDataMutable];

        NSLog(@"Too much data read, trimming");
    }

    //write data to output file
    [writer writeData:readData];

    //update byte counter
    bytesRead += readData.length;
}
long file1BytesWritten = bytesRead;
rmooney
  • 6,123
  • 3
  • 29
  • 29