iOS Code to Convert m4a to WAV

心已入冬 提交于 2019-11-29 02:46:44

Just to update for Swift 3:

func convertAudio(_ url: URL, outputURL: URL) {
    var error : OSStatus = noErr
    var destinationFile: ExtAudioFileRef? = nil
    var sourceFile : ExtAudioFileRef? = nil

    var srcFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
    var dstFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()

    ExtAudioFileOpenURL(url as CFURL, &sourceFile)

    var thePropertySize: UInt32 = UInt32(MemoryLayout.stride(ofValue: srcFormat))

    ExtAudioFileGetProperty(sourceFile!,
                            kExtAudioFileProperty_FileDataFormat,
                            &thePropertySize, &srcFormat)

    dstFormat.mSampleRate = 44100  //Set sample rate
    dstFormat.mFormatID = kAudioFormatLinearPCM
    dstFormat.mChannelsPerFrame = 1
    dstFormat.mBitsPerChannel = 16
    dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame
    dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame
    dstFormat.mFramesPerPacket = 1
    dstFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked |
    kAudioFormatFlagIsSignedInteger

    // Create destination file
    error = ExtAudioFileCreateWithURL(
        outputURL as CFURL,
        kAudioFileWAVEType,
        &dstFormat,
        nil,
        AudioFileFlags.eraseFile.rawValue,
        &destinationFile)
    print("Error 1 in convertAudio: \(error.description)")

    error = ExtAudioFileSetProperty(sourceFile!,
                                    kExtAudioFileProperty_ClientDataFormat,
                                    thePropertySize,
                                    &dstFormat)
    print("Error 2 in convertAudio: \(error.description)")

    error = ExtAudioFileSetProperty(destinationFile!,
                                    kExtAudioFileProperty_ClientDataFormat,
                                    thePropertySize,
                                    &dstFormat)
    print("Error 3 in convertAudio: \(error.description)")

    let bufferByteSize : UInt32 = 32768
    var srcBuffer = [UInt8](repeating: 0, count: 32768)
    var sourceFrameOffset : ULONG = 0

    while(true){
        var fillBufList = AudioBufferList(
            mNumberBuffers: 1,
            mBuffers: AudioBuffer(
                mNumberChannels: 2,
                mDataByteSize: UInt32(srcBuffer.count),
                mData: &srcBuffer
            )
        )
        var numFrames : UInt32 = 0

        if(dstFormat.mBytesPerFrame > 0){
            numFrames = bufferByteSize / dstFormat.mBytesPerFrame
        }

        error = ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList)
        print("Error 4 in convertAudio: \(error.description)")

        if(numFrames == 0){
            error = noErr;
            break;
        }

        sourceFrameOffset += numFrames
        error = ExtAudioFileWrite(destinationFile!, numFrames, &fillBufList)
        print("Error 5 in convertAudio: \(error.description)")
    }

    error = ExtAudioFileDispose(destinationFile!)
    print("Error 6 in convertAudio: \(error.description)")
    error = ExtAudioFileDispose(sourceFile!)
    print("Error 7 in convertAudio: \(error.description)")
}

If anyone else needs some code to do this here it is in Swift

func convertAudioFile(sourceURL: CFURLRef, destinationURL: 
CFURLRef, outputFormat: OSType , 
outputSampleRate: Float64) ->  OSStatus
{
var error : OSStatus = noErr
var destinationFile : ExtAudioFileRef = nil
var sourceFile : ExtAudioFileRef = nil

var srcFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
var dstFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()

var audioConverter : AudioConverterRef = nil

audioConverter = AudioConverterRef.init()

ExtAudioFileOpenURL(sourceURL, &sourceFile)

var thePropertySize: UInt32 = UInt32(strideofValue(srcFormat))

ExtAudioFileGetProperty(sourceFile, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &srcFormat)

dstFormat.mSampleRate = (outputSampleRate == 0 ? srcFormat.mSampleRate : outputSampleRate)  //Set sample rate

dstFormat.mFormatID = outputFormat
dstFormat.mChannelsPerFrame = 1
dstFormat.mBitsPerChannel = 16
dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame
dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame
dstFormat.mFramesPerPacket = 1
dstFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger // little-endian

//Create destination file
ExtAudioFileCreateWithURL(destinationURL, kAudioFileCAFType, &dstFormat, nil,
    AudioFileFlags.EraseFile.rawValue, &destinationFile)

ExtAudioFileSetProperty(sourceFile, kExtAudioFileProperty_ClientDataFormat, thePropertySize, &dstFormat)
ExtAudioFileSetProperty(destinationFile, kExtAudioFileProperty_ClientDataFormat, thePropertySize, &dstFormat)

var size : UInt32 = UInt32(strideofValue(audioConverter))

ExtAudioFileGetProperty(destinationFile, kExtAudioFileProperty_AudioConverter, &size, &audioConverter)

var canResume : UInt32 = 0

size = UInt32(strideofValue(canResume))

error = AudioConverterGetProperty(audioConverter, kAudioConverterPropertyCanResumeFromInterruption, &size, &canResume)

let bufferByteSize : UInt32 = 32768
var srcBuffer = [UInt8](count: 32768, repeatedValue: 0)

var sourceFrameOffset : ULONG = 0

print("Converting audio file")

while(true){

    var fillBufList = AudioBufferList(
        mNumberBuffers: 1,
        mBuffers: AudioBuffer(
            mNumberChannels: 2,
            mDataByteSize: UInt32(srcBuffer.count),
            mData: &srcBuffer
        )  
    )

    var numFrames : UInt32 = 0

    if(dstFormat.mBytesPerFrame > 0){
        numFrames = bufferByteSize / dstFormat.mBytesPerFrame
    }

    ExtAudioFileRead(sourceFile, &numFrames, &fillBufList)

    if(numFrames == 0){
        error = noErr;
        break;
    }

    sourceFrameOffset += numFrames

    error = ExtAudioFileWrite(destinationFile, numFrames, &fillBufList)
}

ExtAudioFileDispose(destinationFile)
ExtAudioFileDispose(sourceFile)

let audioAsset = AVURLAsset.init(URL: destinationURL, options: nil)
if(audioAsset.duration.seconds < 5.0){
    error = -2500
}

return error;

Here is an Objective-C version of MScottWaller's Swift 3 answer. You need to @import AudioToolbox.

-(void) convertAudio:(NSURL*)url outputURL:(NSURL*)outputURL
{
    OSStatus error = noErr;
    ExtAudioFileRef destinationFile = nil;
    ExtAudioFileRef sourceFile = nil;

    AudioStreamBasicDescription srcFormat;
    AudioStreamBasicDescription dstFormat;

    ExtAudioFileOpenURL((__bridge CFURLRef)url, &sourceFile);


    UInt32 thePropertySize = sizeof(srcFormat); //UInt32(MemoryLayout.stride(ofValue: srcFormat));;
    ExtAudioFileGetProperty(sourceFile, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &srcFormat);

    dstFormat.mSampleRate = 44100;  //Set sample rate
    dstFormat.mFormatID = kAudioFormatLinearPCM;
    dstFormat.mChannelsPerFrame = 1;
    dstFormat.mBitsPerChannel = 16;
    dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame;
    dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame;
    dstFormat.mFramesPerPacket = 1;
    dstFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;

    // Create destination file
    error = ExtAudioFileCreateWithURL(
                                      (__bridge CFURLRef)outputURL,
                                      kAudioFileWAVEType,
                                      &dstFormat,
                                      nil,
                                      kAudioFileFlags_EraseFile,
                                      &destinationFile);
    NSLog(@"Error 1 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);

    error = ExtAudioFileSetProperty(sourceFile,
                                    kExtAudioFileProperty_ClientDataFormat,
                                    thePropertySize,
                                    &dstFormat);
    NSLog(@"Error 2 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);

    error = ExtAudioFileSetProperty(destinationFile,
                                    kExtAudioFileProperty_ClientDataFormat,
                                    thePropertySize,
                                    &dstFormat);
    NSLog(@"Error 3 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);

    const UInt32 bufferByteSize = 32768;
    UInt8 srcBuffer[bufferByteSize];// = [UInt8](repeating: 0, count: 32768)
    memset(srcBuffer, 0, bufferByteSize);
    unsigned long sourceFrameOffset = 0;

    while(true)
    {
        AudioBufferList fillBufList;
        fillBufList.mNumberBuffers = 1;
        fillBufList.mBuffers[0].mNumberChannels = 2;
        fillBufList.mBuffers[0].mDataByteSize = bufferByteSize;
        fillBufList.mBuffers[0].mData = &srcBuffer;

        UInt32 numFrames = 0;

        if(dstFormat.mBytesPerFrame > 0){
            numFrames = bufferByteSize / dstFormat.mBytesPerFrame;
        }

        error = ExtAudioFileRead(sourceFile, &numFrames, &fillBufList);
        NSLog(@"Error 4 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);

        if(numFrames == 0)
        {
            error = noErr;
            break;
        }

        sourceFrameOffset += numFrames;
        error = ExtAudioFileWrite(destinationFile, numFrames, &fillBufList);
        NSLog(@"Error 5 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);
    }

    error = ExtAudioFileDispose(destinationFile);
    NSLog(@"Error 6 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);
    error = ExtAudioFileDispose(sourceFile);
    NSLog(@"Error 7 in convertAudio: %d - %@", error, [NSError errorWithDomain:NSOSStatusErrorDomain code:error userInfo:nil].description);
}

AVAssetReader and AVAssetWriter in the AVFoundation framework can be used read AAC files and write that data as WAV/RIFF files on iOS devices. There's sample code on the Apple developer site. It's a bit more than a short snippet.

Here is an edit to @O2U answere. As above code does not actually convert it to wave file. Please use "kAudioFileWAVEType" instead of "kAudioFileCAFType" in above code at line //Create destination file ExtAudioFileCreateWithURL(destinationURL, kAudioFileCAFType, &dstFormat, nil, AudioFileFlags.EraseFile.rawValue, &destinationFile)

I simply changed the extension of the file to .wav and removed the .m4a file and it worked.

func getDirectory() -> URL {
    let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentDirectory = path[0]
    return documentDirectory
}

let date = Date().timeIntervalSince1970

fileName = getDirectory().appendingPathComponent("\(date).m4a")
wavFileName = getDirectory().appendingPathComponent("\(date).wav")

try! FileManager.default.copyItem(at: fileName, to: wavFileName)
try! FileManager.default.removeItem(at: fileName)

I even played .wav file and it's working fine.

audioPlayer = try! AVAudioPlayer(contentsOf: wavFileName)
audioPlayer.play()

Are there any drawbacks for converting the file extension from .m4a to .wav like this?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!