Swift的CoreAudio播放pcm文件

9 篇文章 0 订阅
2 篇文章 0 订阅
本文详细介绍了如何在Swift中使用AVFoundation和CoreAudio框架播放内存中的PCM音频。从读取不同格式的PCM文件,创建AVAudioEngine实例,将Data转换为AVAudioPCMBuffer,再到播放音频,整个过程清晰阐述,适合iOS和macOS开发者参考。
摘要由CSDN通过智能技术生成

开发中会遇到在swift中播放内存中pcm音频的需求。常见的pcm格式有signed16bit,signed32bit,float32bit,在iOS和macOS中,有个avfundation下的core audio就是专门处理pcm格式音频的。

从文件中读取pcm文件

  1. 生成想要的pcm文件,参照我之前的文章
  2. 生成三个文件,分别对应signed16bit,signed32bit,float32bit。
  3. 开始一个swift项目,创建一个Controller,参照之前的文章
  4. 把文件拉入到xcode项目中,并添加到Copy Bundle Resources,如下图:
    在这里插入图片描述
    5.在Controller中添加以下代码:
guard let filefullpathstr = Bundle.main.path(forResource: "s32le", ofType: "pcm") else { return  }
do {
	let url = URL(fileURLWithPath: filefullpathstr)
	let data = try Data(contentsOf: url)
}

添加player和engine

  1. 添加成员变量
private var audioPlayer = AVAudioPlayerNode()
private lazy var audioEngine: AVAudioEngine = {
     let engine = AVAudioEngine()
     // Must happen only once.
     engine.attach(self.audioPlayer)
     return engine
 }()
  1. 初始化engine,在controller的viewDidLoad的函数里面,添加以下代码,因为可能会抛出异常,需要做一次catch。这里需要注意。audioFormat必须为standardFormatWithSampleRate,不然会直接crash。有兴趣可以试一下,然后查一下错误代码,就能解锁苹果官方说明文档。
 audioEngine.connect(audioPlayer, to: audioEngine.mainMixerNode, format: audioFormat)
            try audioEngine.start()
 try audioEngine.start()

转换pcm的Data数据为AVAudioPCMBuffer

  1. google可得:这个函数扩展了Data类,因此在调用的时候只需要data.convertedTo即可。
extension Data {
    func convertedTo(_ format: AVAudioFormat) -> AVAudioPCMBuffer? {
        let streamDesc = format.streamDescription.pointee
        let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
        guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }

        buffer.frameLength = buffer.frameCapacity
        let audioBuffer = buffer.audioBufferList.pointee.mBuffers

        withUnsafeBytes { (bufferPointer) in
            guard let addr = bufferPointer.baseAddress else { return }
            audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
        }

        return buffer
    }
}

  1. 拿到数据后交给player播放
audioPlayer.scheduleBuffer(audioFileBuffer);
audioPlayer.play()

所有代码

//
//  MainViewController.swift
//  PlayPCM
//
//  Created by Niap on 2021/4/15.
//

import Foundation
import UIKit
import AVFoundation

extension Data {
    func convertedTo(_ format: AVAudioFormat) -> AVAudioPCMBuffer? {
        let streamDesc = format.streamDescription.pointee
        let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
        guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }

        buffer.frameLength = buffer.frameCapacity
        let audioBuffer = buffer.audioBufferList.pointee.mBuffers

        withUnsafeBytes { (bufferPointer) in
            guard let addr = bufferPointer.baseAddress else { return }
            audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
        }

        return buffer
    }
}


class MainViewController : UIViewController{
    
    private var audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100.0, channels: 1)
    private var audioPlayer = AVAudioPlayerNode()
    private lazy var audioEngine: AVAudioEngine = {
        let engine = AVAudioEngine()
        // Must happen only once.
        engine.attach(self.audioPlayer)
        return engine
    }()
    override func viewDidLoad() {
        guard let filefullpathstr = Bundle.main.path(forResource: "f32le", ofType: "pcm") else { return  }
        guard let audioFormat = self.audioFormat else { return  }
        
        do {
            let url = URL(fileURLWithPath: filefullpathstr)
            let data = try Data(contentsOf: url)
            guard let audioFileBuffer = data.convertedTo(audioFormat) else {return}
           
            
            audioEngine.connect(audioPlayer, to: audioEngine.mainMixerNode, format: audioFormat)
            try audioEngine.start()
            audioPlayer.scheduleBuffer(audioFileBuffer);
            //audioPlayer.volume = 1
            audioPlayer.play()
        } catch let error  {
            print("!! error \(error.localizedDescription)")
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值