What is the replacement for AKSampler.loadMelodicSoundFont in AudioKit 4.2?

风格不统一 提交于 2019-12-12 19:13:52

问题


I had to upgrade from AudioKit 4.0 to AudioKit 4.2 due to an incompatibility of AudioKit 4.0 with latest swift language and Xcode.

However, now my project cannot be compiled because loadMelodicSoundFont is not a member of AKSampler anymore while I'm using this method to load sf2 sound file.

I could find a documentation for 4.1 only on http://audiokit.io/docs/ and 4.1 has loadMelodicSoundFont apparently. And no documentation for 4.2 I could find.

So what is the replacement for this method in AudioKit 4.2?


回答1:


The new AKSampler class in the latest versions of AudioKit is an entirely new custom sampler. However, as of right now it no longer handles SoundFont files natively (that's what your sf2 file is).

The easiest way for you would simply be to switch to AKAppleSampler, which is the fully featured sampler from previous versions of AudioKit. It relies on the Apple AU code and is still able to load SoundFont files.

So in practice you would simply rename your references to AKSampler to AKAppleSampler in your code.




回答2:


You can rewrite the loadBetterSFZ sampler-extension function in the AKsampler example. This only reads for the included converted files and presumes the sample file is the last part of a region.

I did so and now I can use to sfz converted sf2 files from polyphony(windows) and sforzando and read the file for as far as I understand AKSampler can hold. I made a class to read the sfz file into a data structure that holds the data for reuse. For the sampler I wrote an extension that loads from the created data. Its all quick and dirty (I am not a programmer!). There are many different ways sfz files seem to be structured and I only tried a dozen or so.

example: add class SFZData to conductor in the example

get data with func SFZData.parseSFZ(folderPath: String, sfzFileName: String)->SFZ?

use SFZ for the extension in sampler: func loadSFZData(sfz:SFZ)

//here is what I did(all last versions and works on iPhone 6s)

            import Foundation


            class regionData {
                var lovel: Int32 = -1 //not set, use group
                var hivel: Int32 = -1
                var lokey: Int32 = -1
                var hikey: Int32 = -1
                var pitch: Int32 = -1
                var tune: Int32 = 0
                var transpose: Int32 = 0
                var loopmode: String = ""
                var loopstart: Float32 = 0
                var loopend: Float32 = 0
                var startPoint: Float32 = 0
                var endPoint: Float32 = 0
                var sample: String = ""
                init(){

                }
            }
            class groupData {
                var lovel: Int32 = 0
                var hivel: Int32 = 127
                var lokey: Int32 = 0
                var hikey: Int32 = 127
                var pitch: Int32 = 60
                var loopmode: String = ""
                var sample: String = ""
                var regions = [regionData]()
                init(){
                }
            }
            class globalData {
                var samplePath = ""
                var lovel: Int32 = 0
                var hivel: Int32 = 127
                var sample: String = ""
                var groups = [groupData]()
                //ampdata?
                //filterdata?
                init(){

                }
            }

            class ampData {
                // in global and or group?
            }

            class SFZ {
                var sfzName = ""
                var baseURL : URL!
                var global = globalData()
                var group = [groupData?]()
                init(){
                    //
                }
            }

            class SFZdata {
                var sfzChuncks = [String:SFZ]()

                init(){

                }

                func getData(folderPath: String, sfzFileName: String)->SFZ?{
                    let sfzdata = sfzChuncks[sfzFileName]
                        if sfzdata != nil {
                            return sfzdata
                    }

                    return parseSFZ(folderPath:folderPath,sfzFileName:sfzFileName)
                }

                func parseSFZ(folderPath: String, sfzFileName: String)->SFZ? {
                    //let globalName = "<global>"
                    //let groupName = "<group>"
                    let regionName = "<region>"
                    var filePosition : String.Index
                    var chunck = ""
                    var data: String
                    let sfz = SFZ()

                    //stopAllVoices()
                    //unloadAllSamples()

                    let baseURL = URL(fileURLWithPath: folderPath)
                    let sfzURL = baseURL.appendingPathComponent(sfzFileName)
                    do {
                        data = try String(contentsOf: sfzURL, encoding: .ascii)
                    }catch {
                        debugPrint("file not found")
                        return nil
                    }
                    sfz.sfzName = sfzFileName
                    filePosition = data.startIndex
                    while filePosition != data.endIndex {
                        chunck = findHeader(data: data,dataPointer: &filePosition)

                        switch chunck {
                        case "<global>":
                            //get end of gobal and read data
                            let globaldata = readChunck(data: data, dataPointer: &filePosition)
                            let trimmed = String(globaldata.trimmingCharacters(in: .whitespacesAndNewlines))
                            sfz.global = readGlobal(globalChunck: trimmed)!
                            break
                        case "<group>":
                            //get end of group and read data
                            //first read this one the

                            let groupdata = readChunck(data: data, dataPointer: &filePosition)
                            let trimmed = String(groupdata.trimmingCharacters(in: .whitespacesAndNewlines))
                            let mygroup = readGroup(groupChunck: trimmed)
                            chunck = findHeader(data: data, dataPointer: &filePosition)
                            while chunck == regionName {
                                //read region and append
                                let regiondata = readChunck(data: data, dataPointer: &filePosition)
                                let trimmed = String(regiondata.trimmingCharacters(in: .whitespacesAndNewlines))
                                let myRegion = readRegion(regionChunck: trimmed)
                                mygroup?.regions.append(myRegion)
                                chunck = findHeader(data: data, dataPointer: &filePosition)
                            }
                            if chunck != regionName && filePosition != data.endIndex {
                                //back to before header if ! endoffile
                                filePosition = data.index(filePosition, offsetBy: -(chunck.count))
                            }
                            sfz.group.append(mygroup)
                            break
                        // case region without group ? ignore
                        default:
                            //ignore
                            break
                        }
                    }
                    sfz.baseURL = URL(fileURLWithPath: folderPath)
                    sfzChuncks.updateValue(sfz, forKey: sfzFileName)
                    return sfz
                }

                func findHeader(data:String, dataPointer:inout String.Index)->(String) {
                    if dataPointer == data.endIndex {
                        return ("")
                    }
                    while  dataPointer != data.endIndex {
                        if data[dataPointer] == "<" { break }
                        dataPointer = data.index(after: dataPointer)
                    }
                    if dataPointer == data.endIndex {
                        return ("")
                    }
                    let start = dataPointer
                    while dataPointer != data.endIndex {
                        if  data[dataPointer] == ">"  { break }
                        dataPointer = data.index(after: dataPointer)
                    }
                    dataPointer = data.index(after: dataPointer)
                    if dataPointer == data.endIndex {
                        return ("")
                    }

                    return (String(data[start..<dataPointer]))
                }

                func readChunck(data:String,dataPointer:inout String.Index)->String{
                    var readData = ""
                    if dataPointer == data.endIndex { return readData }
                    while  dataPointer != data.endIndex {
                        if data[dataPointer] == "<" {
                            break
                        } else {
                            readData.append(data[dataPointer])
                            dataPointer = data.index(after: dataPointer)
                        }
                    }
                    if dataPointer == data.endIndex {return readData }
                    if data[dataPointer] == "<" {
                        dataPointer = data.index(before: dataPointer)
                    }
                    return readData
                }

                func readGlobal(globalChunck:String)->globalData?{
                    let globaldata = globalData()
                    var samplestring = ""
                    var global = globalChunck
                    for part in globalChunck.components(separatedBy: .newlines){
                        if part.hasPrefix("sample") {
                            samplestring = part
                        }
                    }
                    if samplestring == "" {
                        //check for structure
                        if global.contains("sample") {
                            //get it out
                            var pointer = global.startIndex
                            var offset = global.index(pointer, offsetBy: 6, limitedBy: global.endIndex)
                            var s = ""
                            while offset != global.endIndex {
                                s = String(global[pointer..<offset!])
                                if s.contains("sample") {break}
                                pointer = global.index(after: pointer)
                                offset = global.index(pointer, offsetBy: 6, limitedBy: global.endIndex)

                            }
                            if s.contains("sample") {
                                //read to end
                                samplestring = String(global[pointer..<global.endIndex])
                            }
                        }
                    }
                    if samplestring != "" {
                        globaldata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
                        global = global.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
                    }

                    for part in global.components(separatedBy: .newlines) {
                        if part.hasPrefix("lovel") {
                            globaldata.lovel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("hivel") {
                            globaldata.hivel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("sample") {
                            globaldata.sample = part.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
                        }
                    }
                    return globaldata
                }

                func readGroup(groupChunck:String)->groupData?{
                    let groupdata = groupData()
                    var samplestring = ""
                    var group = groupChunck
                    for part in groupChunck.components(separatedBy: .newlines){
                        if part.hasPrefix("sample") {
                            samplestring = part
                        }
                    }
                    if samplestring == "" {
                        //check for structure
                        if group.contains("sample") {
                            //get it out
                            var pointer = group.startIndex
                            var offset = group.index(pointer, offsetBy: 6, limitedBy: group.endIndex)
                            var s = ""
                            while offset != group.endIndex {
                                s = String(group[pointer..<offset!])
                                if s.contains("sample") {break}
                                pointer = group.index(after: pointer)
                                offset = group.index(pointer, offsetBy: 6, limitedBy: group.endIndex)

                            }
                            if s.contains("sample") {
                                //read to end
                                samplestring = String(group[pointer..<group.endIndex])
                            }
                        }
                    }
                    if samplestring != "" {
                        groupdata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
                        group = group.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
                    }

                    for part in group.components(separatedBy: .whitespacesAndNewlines) {
                        if part.hasPrefix("lovel") {
                            groupdata.lovel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("hivel") {
                            groupdata.hivel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("lokey") {
                            groupdata.lokey = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("hikey") {
                            groupdata.hikey = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("pitch_keycenter") {
                            groupdata.pitch = Int32(part.components(separatedBy: "=")[1])!
                        }else if part.hasPrefix("loop_mode") {
                            groupdata.loopmode = part.components(separatedBy: "=")[1]
                        }
                    }
                    return groupdata
                }

                func readRegion(regionChunck:String)->regionData{
                    let regiondata = regionData()
                    var samplestring = ""
                    var region = regionChunck
                    for part in regionChunck.components(separatedBy: .newlines){
                        if part.hasPrefix("sample") {
                            samplestring = part
                        }
                    }
                    // this for formats in wich ther are no newlines between region elements
                    if samplestring == "" {
                        //check for structure
                        if region.contains("sample") {
                            //get it out
                            var pointer = region.startIndex
                            var offset = region.index(pointer, offsetBy: 6, limitedBy: region.endIndex)
                            var s = ""
                            while offset != region.endIndex {
                                s = String(region[pointer..<offset!])
                                if s.contains("sample") {break}
                                pointer = region.index(after: pointer)
                                offset = region.index(pointer, offsetBy: 6, limitedBy: region.endIndex)

                            }
                            if s.contains("sample") {
                                //read to end
                                samplestring = String(region[pointer..<region.endIndex])
                            }
                        }
                    }
                    if samplestring != "" {
                        regiondata.sample = samplestring.components(separatedBy: "sample=")[1].replacingOccurrences(of: "\\", with: "/")
                        region = region.replacingOccurrences(of: samplestring, with: "", options: NSString.CompareOptions.literal, range: nil)
                    }
                    for part in region.components(separatedBy: .whitespacesAndNewlines) {
                        if part.hasPrefix("lovel") {
                            regiondata.lovel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("hivel") {
                            regiondata.hivel = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("key=") {
                            regiondata.pitch = Int32(part.components(separatedBy: "=")[1])!
                            regiondata.lokey = regiondata.pitch
                            regiondata.hikey = regiondata.pitch
                        }else if part.hasPrefix("transpose") {
                            regiondata.transpose = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("tune") {
                            regiondata.tune = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("lokey") { // sometimes on one line
                            regiondata.lokey = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("hikey") {
                            regiondata.hikey = Int32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("pitch_keycenter") {
                            regiondata.pitch = Int32(part.components(separatedBy: "=")[1])!
                        }  else if part.hasPrefix("loop_mode") {
                            regiondata.loopmode = part.components(separatedBy: "=")[1]
                        } else if part.hasPrefix("loop_start") {
                            regiondata.loopstart = Float32(part.components(separatedBy: "=")[1])!
                        } else if part.hasPrefix("loop_end") {
                            regiondata.loopend = Float32(part.components(separatedBy: "=")[1])!
                        }else if part.hasPrefix("offset") {
                            regiondata.startPoint = Float32(part.components(separatedBy: "=")[1])!
                        }
                        else if part.hasPrefix("end") {
                            regiondata.endPoint = Float32(part.components(separatedBy: "=")[1])!
                        }
                    }
                    return regiondata
                }
            }

extension for sampler:

     func loadSFZData(sfz:SFZ){
            stopAllVoices()
            unloadAllSamples()
            for group in sfz.group {
                for region in (group?.regions)! {
                    var sd = AKSampleDescriptor()
                    if region.pitch >= 0 {
                        sd.noteNumber = region.pitch
                    } else {
                        sd.noteNumber = (group?.pitch)!
                    }
                    var diff = Float(1)
                    if region.tune != 0 {
                        let fact =  Float(pow(1.000578,Double(region.tune.magnitude)))
                        if region.tune < 0 {
                            diff *= fact
                        } else {
                            diff /= fact
                        }
                    }
                    sd.noteFrequency = Float(AKPolyphonicNode.tuningTable.frequency(forNoteNumber: MIDINoteNumber(sd.noteNumber-region.transpose)))*diff
                    if region.lovel >= 0 {
                        sd.minimumVelocity = region.lovel
                    } else {
                        sd.minimumVelocity = (group?.lovel)!
                    }
                    if region.hivel >= 0 {
                        sd.maximumVelocity = region.hivel
                    } else {
                        sd.maximumVelocity = (group?.hivel)!
                    }
                    if region.lokey >= 0 {
                        sd.minimumNoteNumber = region.lokey
                    } else {
                        sd.minimumNoteNumber = (group?.lokey)!
                    }
                    if region.hikey >= 0{
                        sd.maximumNoteNumber = region.hikey
                    } else {
                        sd.maximumNoteNumber = (group?.hikey)!
                    }
                    sd.loopStartPoint = region.loopstart
                    sd.loopEndPoint = region.loopend
                    var loopMode = ""
                    if region.loopmode != "" {
                        loopMode = region.loopmode
                    } else if group?.loopmode != "" {
                        loopMode = (group?.loopmode)!
                    }
                    sd.isLooping = loopMode != "" && loopMode != "no_loop"

                    sd.startPoint = region.startPoint
                    sd.endPoint = region.endPoint
                    // build sampldescriptor from region
                    // now sample
                    var sample = region.sample
                    if sample == "" { sample = (group?.sample)! }
                    if sample == "" { sample = sfz.global.sample}
                    if sample != "" {
                        let sampleFileURL = sfz.baseURL.appendingPathComponent(sample)
                        if sample.hasSuffix(".wv") {
                            loadCompressedSampleFile(from: AKSampleFileDescriptor(sampleDescriptor: sd, path: sampleFileURL.path))
                        } else {
                            if sample.hasSuffix(".aif") || sample.hasSuffix(".wav") {
                                let compressedFileURL = sfz.baseURL.appendingPathComponent(String(sample.dropLast(4) + ".wv"))
                                let fileMgr = FileManager.default
                                if fileMgr.fileExists(atPath: compressedFileURL.path) {
                                    loadCompressedSampleFile(from: AKSampleFileDescriptor(sampleDescriptor: sd, path: compressedFileURL.path))
                                } else {
                                    do {
                                        let sampleFile = try AKAudioFile(forReading: sampleFileURL)
                                        loadAKAudioFile(from: sd, file: sampleFile)
                                    } catch {
                                        debugPrint("error loading audiofile")
                                    }
                                }
                            }
                        }
                    } //if sample
                } //region
            } //group
            buildKeyMap()
            restartVoices()
        }


来源:https://stackoverflow.com/questions/50054135/what-is-the-replacement-for-aksampler-loadmelodicsoundfont-in-audiokit-4-2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!