AppleSequencer export m4a format file error When use Audiokit? - audiokit

I am a newbie to swift developer, I am now trying to make some music apps. I have been using audiokit frameworks to solve some audio problems. Audiokit is very helpful to me. I use AppleSequencer to initialize the audio and complete the correct playback.
But now I have some problems. Specifically, when I export the file using engine.renderToFile, I get a noise file.
The following is the main code part, I also uploaded the relevant code to the github address:https://github.com/devlong/sequenceExport.git
import AudioKit
import AVFoundation
class SequenceExport: NSObject {
static let shared = SequenceExport.init()
private override init(){}
var mixer = Mixer();
var sequencer : AppleSequencer?
var engine = AudioEngine()
var currentMidiSample:MIDISampler?
var track : MusicTrackManager?
func setCurrentSequencer() {
let instrumentName = "edm-kit"
currentMidiSample = MIDISampler.init(name: instrumentName)
engine.output = currentMidiSample;
do {
try engine.start()
} catch {
Log("AudioKit did not start \(error)")
}
let directory = "Sounds/\(instrumentName)"
let resetPath = Bundle.main.path(forResource:instrumentName, ofType: "aupreset", inDirectory: directory)
do{
try currentMidiSample!.loadPath(resetPath!)
}catch let error {
print("************load resetPath error!!!!!!:\(error)")
}
sequencer = AppleSequencer(filename: "tracks")
track = sequencer!.newTrack()
for index in 0...4 {
track!.add(
noteNumber:MIDINoteNumber(5+index),
velocity: 100, //调节单个音符声音
position: Duration.init(beats: Double(index)),
duration: Duration.init(seconds: 0.5))
}
sequencer!.setGlobalMIDIOutput(currentMidiSample!.midiIn)
sequencer!.setTempo(120)
sequencer!.setLength(Duration.init(beats: 4))
sequencer!.enableLooping()
sequencer!.play()
}
func exportM4a() {
guard let outputURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("audio_file_new.m4a") else { return }
print("outputURL !!!!!!:\(outputURL)")
guard let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 0, interleaved: true) else {
fatalError("Failed to create format")
}
let file = try! AVAudioFile(forWriting: outputURL, settings: format.settings)
do{
try engine.renderToFile(file, maximumFrameCount: 1_096, duration: 5) {
self.sequencer!.play()
} progress: { progress in
// print("progress !!!!!!:\(progress)")
}
}catch let error {
print("export !!!!!!:\(error)")
}
}
}
I tried to find many ways. Audiokit has a way to export mid format, but this is not my requirement. I need to find out how to export m4a, mp3 and other format files from AppleSequencer. Can anyone help me?
If anyone has a way, I hope to help me make changes in the github code, which will help more people who encounter such problems.
I have uploaded the code to the address:https://github.com/devlong/sequenceExport.git

I think that AppleSequencer just won't run in outside of a real time context, so nothing happens when you try to use renderToFile. You could use a NodeRecorder to recorder the track as its being played, but that would be in real time, not super fast.

Related

How do you record and measure audio noise in DB (DBFS to DB)?

I'm trying to measure audio sound frequency in decibels, but I'm getting issues. I have used AVAudioSession & AVAudioRecorder to record audio and used averagePower & peakPower methods to get power levels.
According to the Apple document averagePower could be -160 (minimum) to 0 (maximum), but when I started recording it s showing me -60 to -50 power even in a silent environment. When I started speaking it moves to -40 to -30. I feel it is wrong. Any suggestion would be appreciated!
How can I convert DBFS to DB?
Also there is method powerToDecibels(_:zeroReference:) in Accelerate which converts power to db but it is not working. Can I know what is the value of zeroReference? or how can I used it to convert?
Here is my code to record audio. Please let me know if anything is wrong:
import UIKit
import AVFoundation
import Accelerate
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setUpAudioCapture()
// Do any additional setup after loading the view.
}
private func setUpAudioCapture() {
let recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(.playAndRecord)
try recordingSession.setActive(true)
try recordingSession.setMode(.measurement)
recordingSession.requestRecordPermission({ result in
guard result else { return }
})
captureAudio()
} catch {
print("ERROR: Failed to set up recording session.")
}
}
private func captureAudio() {
let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let audioFilename = documentPath.appendingPathComponent("record.caf")
let settings:[String : Any] =
[
AVFormatIDKey :kAudioFormatAppleIMA4 as AnyObject,
AVSampleRateKey:44100,
AVNumberOfChannelsKey:1,
AVLinearPCMBitDepthKey:32 ,
AVLinearPCMIsBigEndianKey:false,
AVLinearPCMIsFloatKey:false,
AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue,
]
do {
let audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.prepareToRecord()
audioRecorder.record()
audioRecorder.isMeteringEnabled = true
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
audioRecorder.updateMeters()
let db = audioRecorder.averagePower(forChannel: 0)
let peakdb = audioRecorder.peakPower(forChannel: 0)
let pTd = vDSP.powerToDecibels([db], zeroReference: -80)
print("Avg",db, "peak",peakdb, "powerToDecibels", pTd)
}
} catch {
print("ERROR: Failed to start recording process.")
}
}
}
I want record audio and measure sound in db.

How can I get AudioKit to stream an audio file from a remote URL?

I'm trying to play audio files from URLs in an iOS app (Swift 4). I'd like them to buffer and play while downloading. And I quite like AudioKit, but for the life of me and I can't figure out how to make it read remote files.
Any suggestions?
You can't find it because its not there. We've never implemented any streaming functionality. It could be added though, and a number of people like yourself would be quite pleased. Consider contributing some code if you manage to work it out. We can offer you membership to AudioKit's Slack group during development if you wish.
If you only need to stream and play audio files you might just use AVFoundation.AVPlayer. If you want to integrate it into AudioKit then more is needed currently.
You can cache the URL of the remote file locally then load that cached file into AKPlayer. Obviously not streaming - but it will work:
guard let remote = URL(string: "https://www.sample-videos.com/audio/mp3/crowd-cheering.mp3"),
let data = NSData(contentsOf: remote) else {
AKLog("Remote failed to load.")
return
}
let cachedFile = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(remote.lastPathComponent)
try? data.write(to: cachedFile)
let player = AKPlayer(url: cachedFile)
Note that this is just a quick macOS sample - you will likely want to write to a temp directory or some other place.
I tried using Ryan Francesconi's solution and it works. But when I stoped the player once and trying to play again, I got the error below.
func setupPlayer() {
guard let remote = URL(string: "https://xxxxxx.mp3"),
let data = NSData(contentsOf: remote) else {
print("Remote failed to load.")
return
}
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let cachedFile = documentsDirectoryURL.appendingPathComponent(remote.lastPathComponent)
try? data.write(to: cachedFile)
try? player.load(url: cachedFile)
}
But finally I managed to fixed it doing the following:
func setupPlayer() {
guard let remote = URL(string: "https://https://xxxxxx.mp3"),
let data = NSData(contentsOf: remote) else {
print("Remote failed to load.")
return
}
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
cachedFile = documentsDirectoryURL.appendingPathComponent(remote.lastPathComponent)
try? data.write(to: cachedFile)
try? player.load(url: cachedFile)
}
func stop() {
engine.stop()
}
func pause() {
currentTime = player.getCurrentTime()
player.pause()
}
func play() {
try? player.load(url: cachedFile)
player.editStartTime = currentTime
print(currentTime)
player.play()
}
Done.

Render audio file offline using AVAudioEngine

I want to record audio file and save it by applying some effects.
Record is okay and also playing this audio with effect is okay too.
The problem is when I try to save such audio offline it produces empty audio file.
Here is my code:
let effect = AVAudioUnitTimePitch()
effect.pitch = -300
self.addSomeEffect(effect)
func addSomeEffect(_ effect: AVAudioUnit) {
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker)
let format = self.audioFile.processingFormat
self.audioEngine.stop()
self.audioEngine.reset()
self.audioEngine = AVAudioEngine()
let audioPlayerNode = AVAudioPlayerNode()
self.audioEngine.attach(audioPlayerNode)
self.audioEngine.attach(effect)
self.audioEngine.connect(audioPlayerNode, to: self.audioEngine.mainMixerNode, format: format)
self.audioEngine.connect(effect, to: self.audioEngine.mainMixerNode, format: format)
audioPlayerNode.scheduleFile(self.audioFile, at: nil)
do {
let maxNumberOfFrames: AVAudioFrameCount = 8096
try self.audioEngine.enableManualRenderingMode(.offline,
format: format,
maximumFrameCount: maxNumberOfFrames)
} catch {
fatalError()
}
do {
try audioEngine.start()
audioPlayerNode.play()
} catch {
}
let outputFile: AVAudioFile
do {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("temp.m4a")
if FileManager.default.fileExists(atPath: url.path) {
try? FileManager.default.removeItem(at: url)
}
let recordSettings = self.audioFile.fileFormat.settings
outputFile = try AVAudioFile(forWriting: url, settings: recordSettings)
} catch {
fatalError()
}
let buffer = AVAudioPCMBuffer(pcmFormat: self.audioEngine.manualRenderingFormat,
frameCapacity: self.audioEngine.manualRenderingMaximumFrameCount)!
while self.audioEngine.manualRenderingSampleTime < self.audioFile.length {
do {
let framesToRender = min(buffer.frameCapacity,
AVAudioFrameCount(self.audioFile.length - self.audioEngine.manualRenderingSampleTime))
let status = try self.audioEngine.renderOffline(framesToRender, to: buffer)
switch status {
case .success:
print("Write to file")
try outputFile.write(from: buffer)
case .error:
fatalError()
default:
break
}
} catch {
fatalError()
}
}
print("Finish write")
audioPlayerNode.stop()
audioEngine.stop()
self.outputFile = outputFile
self.audioPlayer = try? AVAudioPlayer(contentsOf: outputFile.url)
}
AVAudioPlayer fails to open file with output url. I looked at the file through the file system and it is empty and can't be played.
Picking different categories for AVAudioSession is not working too.
Thanks for help!
UPDATE
I switched to use .caf file extension in my record in output file and it worked. Any idea why is .m4a is not working?
You need to nil outputFile to flush the header and close the m4a file.

Audio Stream via firebase

I have uploaded some songs in firebase Storage directly,I just want to stream the song in AVAudioPlayer.
Below is the code which I am trying:
var mainRef: FIRStorageReference {
return FIRStorage.storage().reference(forURL: "gs://musicapp-d840c.appspot.com")
}
var audioStorageRef: FIRStorageReference{
return mainRef.child("SongsPath")
}
audioStorageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
} else {
if let url = url {
do {
self.audioPlayer = try AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: String(describing: url)) as URL)
self.audioPlayer.play()
} catch {}
let storyboard = UIStoryboard(name: "AudioPlayer", bundle: nil)
let audioVc = storyboard.instantiateViewController(withIdentifier: "AudioPlayerViewController") as! AudioPlayerViewController
audioVc.playThisSong = String(describing: url)
self.present(audioVc, animated: false, completion: nil)
}
}
}
Here the song url from the firebase is passing but it is skipping the self.audioPlayer.play. ,I just want to stream the audio. Can I get a proper solution for this?
This is not an answer for streaming.
This is an answer for downloading the file, storing it locally, and playing the audio after the file has finished downloading.
Get a Firebase storage reference using a path string with the file extension. Get a file url to store it on the device using the same path string that we use for the Firebase storage reference.
Initiate the download task using write(toFile: URL). Store the download task in a variable to add observers. When the download is successful, play the audio.
In Swift 4:
var player: AVAudioPlayer?
let pathString = "SongsPath.mp3"
let storageReference = Storage.storage().reference().child(pathString)
let fileUrls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
guard let fileUrl = fileUrls.first?.appendingPathComponent(pathString) else {
return
}
let downloadTask = storageReference.write(toFile: fileUrl)
downloadTask.observe(.success) { _ in
do {
self.player = try AVAudioPlayer(contentsOf: fileUrl)
self.player?.prepareToPlay()
self.player?.play()
} catch let error {
print(error.localizedDescription)
}
}
This is minimal code. Implement error handling as you see fit.
Firebase example of downloading locally

Swift 3 Record audio and upload to Firebase storage and play back

I am making a chat room application, so far it is able to send message, image and video.
I am using a very similar method to send video and it works, but it's not working when sending audio.
The audio file and audio url is upload to Firebase successfully, But when I tried to play back the audio, it show this error: The operation couldn’t be completed. (OSStatus error 2003334207.).
The project is getting quite overwhelmingly large, and I have very little experience using AVAudio, so if you guys had similar problems before please teach me how to fix it. Thanks!!!
Here is the code of setting up the audioRecorder, and I get the url here and pass it to other func to put the audio file to Firebase storage.
func startRecording() {
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.low.rawValue
]
do {
let audioFileUrl = getAudiFileURL()
audioRecorder = try AVAudioRecorder(url: audioFileUrl, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
blackView.isHidden = false
} catch {
finishRecording(success: false)
}
}
Here is where I try to upload the audio file to Firebase storage, and it does print out the correct downloadURL. (The URL is pointing to the file's location in the iOS devices.)
func handleAudioSendWith(url: String) {
guard let fileUrl = URL(string: url) else {
return
}
let fileName = NSUUID().uuidString + ".m4a"
FIRStorage.storage().reference().child("message_voice").child(fileName).putFile(fileUrl, metadata: nil) { (metadata, error) in
if error != nil {
print(error ?? "error")
}
if let downloadUrl = metadata?.downloadURL()?.absoluteString {
print(downloadUrl)
let values: [String : Any] = ["audioUrl": downloadUrl]
self.sendMessageWith(properties: values)
}
}
}
Here is how I set up the url for the audioRecorder above.
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
func getAudiFileURL() -> URL {
return getDocumentsDirectory().appendingPathComponent(".m4a")
}
And this is where I play the audio:
func handleAudioPLay() {
if let audioUrl = message?.audioUrl, let url = URL(string: audioUrl) {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.delegate = self
audioPlayer?.prepareToPlay()
audioPlayer?.play()
print("Audio ready to play")
} catch let error {
print(error.localizedDescription)
}
}
}
I can actually download the sound file from Firebase using the url and play it on my computer, which means the url is fine.
I have solved the problem by downloading the sound file using URLSession, and play it using AVAudioPlayer(data: data!, fileTypeHint: "aac").

Resources