Once I have connected the Bluetooth LE peripheral (headphones) to my device.
How I could use it to play sound ?
EDIT: I want to force play sound on peripheral
Actually I'm using this code to play sound on device speaker:
var engine = AVAudioEngine()
var player = AVAudioPlayerNode()
var pitch = AVAudioUnitTimePitch()
override func viewDidLoad() {
super.viewDidLoad()
player.volume = 1.0
let path = NSBundle.mainBundle().pathForResource("Test", ofType: "m4a")!
let url = NSURL.fileURLWithPath(path)
let file = try? AVAudioFile(forReading: url)
let buffer = AVAudioPCMBuffer(PCMFormat: file!.processingFormat, frameCapacity: AVAudioFrameCount(file!.length))
do {
try file!.readIntoBuffer(buffer)
} catch _ {
}
engine.attachNode(player)
engine.attachNode(pitch)
engine.connect(player, to: pitch, format: buffer.format)
engine.connect(pitch, to: engine.mainMixerNode, format: buffer.format)
player.scheduleBuffer(buffer, atTime: nil, options: AVAudioPlayerNodeBufferOptions.Loops, completionHandler: nil)
engine.prepare()
do {
try engine.start()
} catch _ {
}
}
Related
I am currently using Microsoft Azure Cognitive Speech SDK to play text to speech.
I am able to get the data from the Stream which is provided in the following format (reference):
.audio16Khz32KBitRateMonoMp3
This is set like this:
private let inputFormat = AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: 16000,
channels: 1,
interleaved: false
)!
I'm using AVAudioEngine & AVAudioPlayerNode:
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
override func viewDidLoad() {
super.viewDidLoad()
let mainMixer = engine.mainMixerNode
engine.attach(player)
engine.connect(player, to: mainMixer, format: inputFormat)
try! engine.start()
}
I am able to play this back with some success using the following:
func playAudio(dialogue: String, audioPlayer: AVAudioPlayerNode, then completion: #escaping ( () -> Void)) {
audioAsset = nil
try? FileManager.default.removeItem(at: recordingPath)
FileManager.default.createFile(atPath: recordingPath.path, contents: nil, attributes: nil)
do {
let configuration = try SPXSpeechConfiguration(subscription: Microsoft.key, region: Microsoft.region)
configuration.setSpeechSynthesisOutputFormat(.audio16Khz32KBitRateMonoMp3)
let synthesizer = try SPXSpeechSynthesizer(speechConfiguration: configuration, audioConfiguration: nil)
let speechResult = try synthesizer.startSpeakingSsml(dialogue)
let stream = try SPXAudioDataStream(from: speechResult)
guard
let mutableFile = FileHandle(forWritingAtPath: recordingPath.path),
let streamData = NSMutableData(capacity:Int(bufferCapacity))
else {
fatalError()
}
while stream.read(streamData, length:bufferCapacity) > 0 {
mutableFile.write(streamData as Data)
mutableFile.seekToEndOfFile()
do {
let buffer = try readFileIntoBuffer(audioUrl: recordingPath)
audioPlayer.scheduleBuffer(buffer, at: currentBufferTime(buffer: buffer)) { [weak self] in
guard let self = self else { return }
if let audioAsset = self.audioAsset, audioPlayer.currentTime >= CMTimeGetSeconds(audioAsset.duration) {
DispatchQueue.main.async {
audioPlayer.stop()
}
completion()
}
}
audioPlayer.play()
} catch {
print("Unable To Play Azure Buffer Stream \(error)")
}
}
print("Did Complete Azure Buffer Rendering To File")
audioAsset = AVURLAsset.init(url: recordingPath, options: nil)
mutableFile.closeFile()
} catch {
print("Unable To Run Azure Vocder \(error)")
}
}
With my Buffer creation function being as follows:
func currentBufferTime(buffer: AVAudioPCMBuffer) -> AVAudioTime {
let framecount = Double(buffer.frameLength)
let samplerate = buffer.format.sampleRate
let position = TimeInterval(framecount / samplerate)
return AVAudioTime(sampleTime: AVAudioFramePosition(position), atRate: 1)
}
func readFileIntoBuffer(audioUrl: URL) throws -> AVAudioPCMBuffer {
let audioFile = try AVAudioFile(forReading: audioUrl)
let audioFileFormat = audioFile.processingFormat
let audioFileSize = UInt32(audioFile.length)
let audioBuffer = AVAudioPCMBuffer(pcmFormat: audioFileFormat, frameCapacity: audioFileSize)!
try audioFile.read(into: audioBuffer)
return audioBuffer
}
The issue is that this is not performant and the CPU is around 100% for a significant amount of time when running the function.
As such my question is what is a more optimum way of reading the data into a PCM Buffer?
I have looked at many examples and there doesn't seem to be any thing which works. For example:
func toPCMBuffer(format: AVAudioFormat, data: NSData) -> AVAudioPCMBuffer? {
let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: UInt32(data.count) / format.streamDescription.pointee.mBytesPerFrame)
guard let buffer = buffer else { return nil }
buffer.frameLength = buffer.frameCapacity
let channels = UnsafeBufferPointer(start: buffer.int32ChannelData, count: Int(buffer.format.channelCount))
data.getBytes(UnsafeMutableRawPointer(channels[0]) , length: data.count)
return buffer
}
I'm trying to record some audio using AVAudioEngine, but when I start recording with the methods that I have in my setup class, the app crashes and gives me some error, like the following:
2021-07-15 21:28:27.569564-0400 App[75861:6634462] [avae] AVAEInternal.h:88 required condition is false: [AVAudioEngineGraph.mm:1357:Initialize: (IsFormatSampleRateAndChannelCountValid(outputHWFormat))]
2021-07-15 21:28:27.569679-0400 App[75861:6634462] [avae] AVAudioEngine.mm:167 Engine#0x282ad0de0: could not initialize, error = -10875
2021-07-15 21:28:27.571773-0400 App[75861:6634462] Audio files cannot be non-interleaved. Ignoring setting AVLinearPCMIsNonInterleaved YES.
2021-07-15 21:28:27.575892-0400 App[75861:6634462] [avae] AVAEInternal.h:88 required condition is false: [AVAudioEngineGraph.mm:1357:Initialize: (IsFormatSampleRateAndChannelCountValid(outputHWFormat))]
This is the structure that I'm using to make the recording using AVAudioEngine:
class Recorder {
enum RecordingState {
case recording, paused, stopped
}
private var engine: AVAudioEngine!
private var mixerNode: AVAudioMixerNode!
private var state: RecordingState = .stopped
init() {
setupSession()
setupEngine()
}
fileprivate func setupSession() {
let session = AVAudioSession.sharedInstance()
try? session.setCategory(.record)
try? session.setActive(true, options: .notifyOthersOnDeactivation)
}
fileprivate func setupEngine() {
engine = AVAudioEngine()
mixerNode = AVAudioMixerNode()
mixerNode.volume = 0
engine.attach(mixerNode)
makeConnections()
engine.prepare()
}
fileprivate func makeConnections() {
let inputNode = engine.inputNode
let inputFormat = inputNode.outputFormat(forBus: 0)
engine.connect(inputNode, to: mixerNode, format: inputFormat)
let mainMixerNode = engine.mainMixerNode
let mixerFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: inputFormat.sampleRate, channels: 1, interleaved: false)
engine.connect(mixerNode, to: mainMixerNode, format: mixerFormat)
}
func startRecording() throws {
let tapNode: AVAudioNode = mixerNode
let format = tapNode.outputFormat(forBus: 0)
let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let file = try AVAudioFile(forWriting: documentURL.appendingPathComponent("recording.caf"), settings: format.settings)
tapNode.installTap(onBus: 0, bufferSize: 4096, format: format, block: {
(buffer, time) in
try? file.write(from: buffer)
})
try engine.start()
state = .recording
}
func stopRecording() {
mixerNode.removeTap(onBus: 0)
engine.stop()
state = .stopped
}
}
And this is how I'm calling the class to start recording:
let hola = Recorder()
do {
try hola.startRecording()
} catch { }
I want to change rate and pitch of audio file. when i'm changing rate of audio pitch automatically changing and getting not clear sound of audio.
i'm using this code
var engine: AVAudioEngine!
var player: AVAudioPlayerNode!
var pitch : AVAudioUnitTimePitch!
var file = AVAudioFile()
var totalDuration : TimeInterval!
func configurePlayer() {
engine = AVAudioEngine()
player = AVAudioPlayerNode()
player.volume = 1.0
let path = Bundle.main.path(forResource: "sample", ofType: "wav")
let url = URL.init(fileURLWithPath: path!)
file = try! AVAudioFile(forReading: url)
let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: AVAudioFrameCount(file.length))
do {
try file.read(into: buffer!)
} catch _ {
}
pitch = AVAudioUnitTimePitch()
pitch.rate = 1
engine.attach(player)
engine.attach(pitch)
engine.attach(speedEffect)
engine.connect(player, to: pitch, format: buffer?.format)
engine.connect(pitch, to: engine.mainMixerNode, format: buffer?.format)
player.scheduleBuffer(buffer!, at: nil, options: AVAudioPlayerNodeBufferOptions.loops, completionHandler: nil)
engine.prepare()
do {
try engine.start()
} catch _ {
}
}
#IBAction func slideValueChanged(_ sender: UISlider) {
let newRate = sender.value/120;
pitch.rate = newRate
}
when changing rate using slider getting bad sound.
slider minValue: 60 maxValue: 240
I have set up an audio multitrack player using apple's AVFoundation. I use nine AVAudioPlayerNodes attached to an AVAudioEngine and they are played at precisely the same time. In spriteKit, in my game scene, I would like to detect the end of the file in any of the AVAudioPlayerNodes so that I can run subsequent code. How do I do that? Unfortunately AVAudioPlayerNodes don't have the same convenient functions as the simple AVAudioPlayer class. Here is the multiTrack function:
import SpriteKit
import AVFoundation
var onesie = AVAudioPlayer()
var singleTrack = AVAudioPlayerNode()
var trackOne = AVAudioPlayerNode()
var trackTwo = AVAudioPlayerNode()
var trackThree = AVAudioPlayerNode()
var trackFour = AVAudioPlayerNode()
var trackFive = AVAudioPlayerNode()
var trackSix = AVAudioPlayerNode()
var trackSeven = AVAudioPlayerNode()
var trackEight = AVAudioPlayerNode()
var trackNine = AVAudioPlayerNode()
//variables to hold NSURLs as AVAudioFiles for use in AudioPlayer Nodes.
var single = AVAudioFile()
var one = AVAudioFile()
var two = AVAudioFile()
var three = AVAudioFile()
var four = AVAudioFile()
var five = AVAudioFile()
var six = AVAudioFile()
var seven = AVAudioFile()
var eight = AVAudioFile()
var nine = AVAudioFile()
//varibles for audio engine and player nodes. The "mixer" is part of the engine and already hooked up to the output
var engine = AVAudioEngine()
//reference the mixer
let mainMixer = engine.mainMixerNode
func audioMultiTrack(trackOneFN: String, trackTwoFN: String, trackThreeFN: String, trackFourFN: String, trackFiveFN: String, trackSixFN: String, trackSevenFN: String, trackEightFN: String, trackNineFN: String){
/*access audio filess for audio players (tracks)*/
//1
guard let trackOneFile = NSBundle.mainBundle().URLForResource(trackOneFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//2
guard let trackTwoFile = NSBundle.mainBundle().URLForResource(trackTwoFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//3
guard let trackThreeFile = NSBundle.mainBundle().URLForResource(trackThreeFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//4
guard let trackFourFile = NSBundle.mainBundle().URLForResource(trackFourFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//5
guard let trackFiveFile = NSBundle.mainBundle().URLForResource(trackFiveFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//6
guard let trackSixFile = NSBundle.mainBundle().URLForResource(trackSixFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//7
guard let trackSevenFile = NSBundle.mainBundle().URLForResource(trackSevenFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//8
guard let trackEightFile = NSBundle.mainBundle().URLForResource(trackEightFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//9
guard let trackNineFile = NSBundle.mainBundle().URLForResource(trackNineFN, withExtension: "mp3") else {
fatalError("File not found.")
}
//place NSURLs in AVAudioFile variables
//1
do {
try one = AVAudioFile(forReading: trackOneFile)
} catch {
fatalError("error loading track one file.")
}
//2
do {
try two = AVAudioFile(forReading: trackTwoFile)
} catch {
fatalError("error loading track two file.")
}
//3
do {
try three = AVAudioFile(forReading: trackThreeFile)
} catch {
fatalError("error loading track three file.")
}
//4
do {
try four = AVAudioFile(forReading: trackFourFile)
} catch {
fatalError("error loading track four file.")
}
//5
do {
try five = AVAudioFile(forReading: trackFiveFile)
} catch {
fatalError("error loading track five file.")
}
//6
do {
try six = AVAudioFile(forReading: trackSixFile)
} catch {
fatalError("error loading track six file.")
}
//7
do {
try seven = AVAudioFile(forReading: trackSevenFile)
} catch {
fatalError("error loading track six file.")
}
//8
do {
try eight = AVAudioFile(forReading: trackEightFile)
} catch {
fatalError("error loading track six file.")
}
//9
do {
try nine = AVAudioFile(forReading: trackNineFile)
} catch {
fatalError("error loading track six file.")
}
/*hook up audio units*/
//attach audio players (tracks) to audio engine
engine.attachNode(trackOne)
engine.attachNode(trackTwo)
engine.attachNode(trackThree)
engine.attachNode(trackFour)
engine.attachNode(trackFive)
engine.attachNode(trackSix)
engine.attachNode(trackSeven)
engine.attachNode(trackEight)
engine.attachNode(trackNine)
//connect the tracks to the mixer
engine.connect(trackOne, to: mainMixer, format: nil)
engine.connect(trackTwo, to: mainMixer, format: nil)
engine.connect(trackThree, to: mainMixer, format: nil)
engine.connect(trackFour, to: mainMixer, format: nil)
engine.connect(trackFive, to: mainMixer, format: nil)
engine.connect(trackSix, to: mainMixer, format: nil)
engine.connect(trackSeven, to: mainMixer, format: nil)
engine.connect(trackEight, to: mainMixer, format: nil)
engine.connect(trackNine, to: mainMixer, format: nil)
//connect audio files to audio players (tracks)
trackOne.scheduleFile(one, atTime: nil, completionHandler: nil)
trackTwo.scheduleFile(two, atTime: nil, completionHandler: nil)
trackThree.scheduleFile(three, atTime: nil, completionHandler: nil)
trackFour.scheduleFile(four, atTime: nil, completionHandler: nil)
trackFive.scheduleFile(five, atTime: nil, completionHandler: nil)
trackSix.scheduleFile(six, atTime: nil, completionHandler: nil)
trackSeven.scheduleFile(seven, atTime: nil, completionHandler: nil)
trackEight.scheduleFile(eight, atTime: nil, completionHandler: nil)
trackNine.scheduleFile(nine, atTime: nil, completionHandler: nil)
//try to start the audio engine
do {
try engine.start()
} catch {
print("error starting engine")
}
//function to create a precice time to start all audio players (tracks)
func startTime () ->AVAudioTime{
let samplerate = one.processingFormat.sampleRate
let sampleTime = AVAudioFramePosition(samplerate)
let time = AVAudioTime(sampleTime: sampleTime, atRate: samplerate)
return time
}
//start audio players (tracks) at precise time
trackOne.playAtTime(startTime())
trackTwo.playAtTime(startTime())
trackThree.playAtTime(startTime())
trackFour.playAtTime(startTime())
trackFive.playAtTime(startTime())
trackSix.playAtTime(startTime())
trackSeven.playAtTime(startTime())
trackEight.playAtTime(startTime())
trackNine.playAtTime(startTime())
}
I'm working on an iOS app with Swift and Xcode 6. What I would like to do is play an audio file using an AVAudioEngine, and until this point everything OK. But how can I play it without stopping, I mean, that when it ends playing it starts again?
This is my code:
/*==================== CONFIGURATES THE AVAUDIOENGINE ===========*/
audioEngine.reset() //Resets any previous configuration on AudioEngine
let audioPlayerNode = AVAudioPlayerNode() //The node that will play the actual sound
audioEngine.attachNode(audioPlayerNode) //Attachs the node to the audioengine
audioEngine.connect(audioPlayerNode, to: audioEngine.outputNode, format: nil) //Connects the applause playback node to the sound output
audioPlayerNode.scheduleFile(applause.applauseFile, atTime: nil, completionHandler: nil)
audioEngine.startAndReturnError(nil)
audioPlayerNode.play() //Plays the sound
Before saying me that I should use AVAudioPlayer for this, I can't because later I will have to use some effects and play three audio files at the same time, also repeatedly.
I found the solution in another question, asked and also auto-answered by #CarveDrone , so I've just copied the code he used:
class aboutViewController: UIViewController {
var audioEngine: AVAudioEngine = AVAudioEngine()
var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()
override func viewDidLoad() {
super.viewDidLoad()
let filePath: String = NSBundle.mainBundle().pathForResource("chimes", ofType: "wav")!
println("\(filePath)")
let fileURL: NSURL = NSURL(fileURLWithPath: filePath)!
let audioFile = AVAudioFile(forReading: fileURL, error: nil)
let audioFormat = audioFile.processingFormat
let audioFrameCount = UInt32(audioFile.length)
let audioFileBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity: audioFrameCount)
audioFile.readIntoBuffer(audioFileBuffer, error: nil)
var mainMixer = audioEngine.mainMixerNode
audioEngine.attachNode(audioFilePlayer)
audioEngine.connect(audioFilePlayer, to:mainMixer, format: audioFileBuffer.format)
audioEngine.startAndReturnError(nil)
audioFilePlayer.play()
audioFilePlayer.scheduleBuffer(audioFileBuffer, atTime: nil, options:.Loops, completionHandler: nil)
}
The only thing you have to change is the filePath constant. Here is the link to the original answer: Having AVAudioEngine repeat a sound
Swift 5, thanks at #Guillermo Barreiro
var audioEngine: AVAudioEngine = AVAudioEngine()
var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()
override func viewDidLoad() {
super. viewDidLoad()
guard let filePath: String = Bundle.main.path(forResource: "chimes", ofType: "wav") else{ return }
print("\(filePath)")
let fileURL: URL = URL(fileURLWithPath: filePath)
guard let audioFile = try? AVAudioFile(forReading: fileURL) else{ return }
let audioFormat = audioFile.processingFormat
let audioFrameCount = UInt32(audioFile.length)
guard let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount) else{ return }
do{
try audioFile.read(into: audioFileBuffer)
} catch{
print("over")
}
let mainMixer = audioEngine.mainMixerNode
audioEngine.attach(audioFilePlayer)
audioEngine.connect(audioFilePlayer, to:mainMixer, format: audioFileBuffer.format)
try? audioEngine.start()
audioFilePlayer.play()
audioFilePlayer.scheduleBuffer(audioFileBuffer, at: nil, options:AVAudioPlayerNodeBufferOptions.loops)
}