2

I am trying to load a song, mix it with filters and save it to file. However I cannot manage to get it work with AudioKit.renderToFile. I always get "no such file" after rendering.

My code is very similar to this.

if let fileUrl = Bundle.main.path(forResource: "song", ofType: "wav") {
    export()
}

Rendering:

func export(songUrl: String) {
    do {
        if let url = URL(string: songUrl) {
            if let file = try? AKAudioFile(forReading: url) {
                player = try AKAudioPlayer(file: file)
            }
        }
    } catch {
        fatalError("PLAYER URL ERROR")
    }

    mainMixer = AKMixer(player)
    AudioKit.output = mainMixer

    do {
        try AudioKit.start()
        self.outputFile = try AVAudioFile(forWriting: exportURL, settings: player.audioFile.fileFormat.settings)
        try AudioKit.renderToFile(self.outputFile, duration: self.player.duration, prerender: {
            self.player.play()
        })
    } catch {
        fatalError("Unexpected error: \(error).")
    }

    self.showFileSize()
}

Export URL:

let exportURL: URL = {
    let documentsURL = FileManager.default.temporaryDirectory
    return documentsURL.appendingPathComponent("exported_song.wav")
}()

showFileSize uses FileManager.default.attributesOfItem(atPath: filePath) but here it throws an exception:

The file “exported_song.wav” couldn’t be opened because there is no such file.

What I am doing wrong here? Thanks in advance!

Dev_IL
  • 252
  • 1
  • 15
  • I ran the sample project this programmer so graciously created for me to test with and I can tell that a file is definitely being created. Why iOS is not seeing it could be related to sandboxing perhaps? – Aurelius Prochazka Aug 06 '18 at 08:32
  • 1
    @AureliusProchazka You are right - the problem is with reading. I commited 2 changes on test project: 1) I check file with `FileManager.default.fileExists(atPath: url)` 2) I load data `let data = try Data(contentsOf: URL(fileURLWithPath: url))` 3) I try to play `audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: url))`. 1) and 2) works OK so the file is created but AVAudio cannot load it. I also tried init with AVAudio(data) but it also throws error. I even added option to share data file - and when i opened it and renemed on Mac - the sound is good (with all effects). – Dev_IL Aug 06 '18 at 19:03
  • 1
    @AureliusProchazka I think that there also may be something wrong with renderToFile. I printed AudioFile.fileFormat from resources: `` But from documents after render it looks like this: `` source bit is UNKNOWN and there are 0 frames/packet. Do you know what may be the issue? – Dev_IL Aug 07 '18 at 16:43
  • @AureliusProchazka I finally managed to get it work but it's not very trivial. What I did (accidentally) was to create output file and renderToFile twice. I updated test project so you can test it. In SongPlayer -> export method. Normally creating AVAudioPlayer or any other approach to create audio file/asset failed. Now after second render it works.. What may be interesting - I am printing file size and I can see that after first render it is (in bytes): 13857770 but after second: 13857778. So these additional 8 bytes must be doing "the job". – Dev_IL Aug 19 '18 at 18:22
  • @AureliusProchazka If you could analyze it and give your thoughts we will be able to create sample project and maybe marge it into your docs as IMO it was missing. – Dev_IL Aug 19 '18 at 18:27
  • @AureliusProchazka Do you have any idea why it has to be done twice? https://gist.github.com/Ufosek/0631189e41663cf11585fb732353cc53 – Dev_IL Nov 19 '18 at 12:00
  • No I don't know why it works on the second time, perhaps there's a race condition that the first process delays the second one to be successful. – Aurelius Prochazka Nov 23 '18 at 10:16

1 Answers1

1

Here's what worked for me to write a *.wav file output (ends up in Documents/out.wav):

private func testRecord() {
    let testOscillator = AKOscillator()
    AudioKit.output = testOscillator

    // write to file
    do {
        try AudioKit.start()

        print("home: \(NSHomeDirectory())")

        guard let outURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("out.wav") else {
            fatalError("Failed URL create")
        }

        guard let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100, channels: 2, interleaved: true) else {
            fatalError("Failed to create format")
        }

        let file = try AKAudioFile(forWriting: outURL, settings: format.settings)

        try AudioKit.renderToFile(file, duration: 10.0, prerender: {
            testOscillator.start()
        })
    } catch {
        print("renderToFile error: \(error)")
    }
    print("didn't crash")
}
Doug Voss
  • 1,012
  • 1
  • 14
  • 11