iOS 将 m4a 转换为 WAV 的代码

iOS Code to Convert m4a to WAV

有人有任何代码片段可以说明如何将 M4a 文件转换为 WAV 文件吗?我知道有些库可以反过来转换。

谢谢。

AVFoundation 框架中的 AVAssetReader 和 AVAssetWriter 可用于读取 AAC 文件并将该数据作为 WAV/RIFF 文件写入 iOS 设备。 Apple 开发者网站上有示例代码。这不仅仅是一个简短的片段。

如果其他人需要一些代码来执行此操作,请在 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;

这是对@O2U 回答的编辑。如上代码实际上并没有将其转换为 wave 文件。请在第 //Create destination file ExtAudioFileCreateWithURL(destinationURL, kAudioFileCAFType, &dstFormat, nil, AudioFileFlags.EraseFile.rawValue, &destinationFile)

行的上述代码中使用 "kAudioFileWAVEType" 而不是 "kAudioFileCAFType"

只是为了更新 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)")
}

这是 MSCottWaller Swift 3 回答的 Objective-C 版本。您需要@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);
}

我只是将文件的扩展名更改为 .wav 并删除了 .m4a 文件并且它起作用了。

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)

我什至播放了 .wav 文件,它工作正常。

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

像这样将文件扩展名从 .m4a 转换为 .wav 有什么缺点吗?

我试图在 Swift 5.

中将 m4a 转换为 Wav 格式

以上所有代码都已过时且无法正常工作。 完成一些调整后,这里是工作示例

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)
    reportError(error: error)

    error = ExtAudioFileSetProperty(sourceFile!,
            kExtAudioFileProperty_ClientDataFormat,
            thePropertySize,
            &dstFormat)
    reportError(error: error)

    error = ExtAudioFileSetProperty(destinationFile!,
                                     kExtAudioFileProperty_ClientDataFormat,
                                    thePropertySize,
                                    &dstFormat)
    reportError(error: error)

    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)
        reportError(error: error)

        if(numFrames == 0){
            error = noErr;
            break;
        }
        
        sourceFrameOffset += numFrames
        error = ExtAudioFileWrite(destinationFile!, numFrames, &fillBufList)
        reportError(error: error)
    }
    
    error = ExtAudioFileDispose(destinationFile!)
    reportError(error: error)
    error = ExtAudioFileDispose(sourceFile!)
    reportError(error: error)
}

func reportError(error: OSStatus) {
    // Handle error
    print(error)
}