AudioKit 4.2 中 AKSampler.loadMelodicSoundFont 的替代品是什么?

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

由于 AudioKit 4.0 与最新的 swift 语言和 Xcode.

不兼容,我不得不从 AudioKit 4.0 升级到 AudioKit 4.2

但是,现在我的项目无法编译,因为在我使用此方法加载 sf2 声音文件时,loadMelodicSoundFont 不再是 AKSampler 的成员。

我只能在 http://audiokit.io/docs/ 上找到 4.1 的文档,而 4.1 显然有 loadMelodicSoundFont。我找不到 4.2 的文档。

那么在 AudioKit 4.2 中这个方法的替代品是什么?

最新版本的 AudioKit 中的新 AKSampler class 是一个全新的自定义采样器。但是,截至目前,它不再本地处理 SoundFont 文件(这就是您的 sf2 文件)。

最简单的方法就是切换到 AKAppleSampler,它是 AudioKit 以前版本中功能齐全的采样器。它依赖于 Apple AU 代码并且仍然能够加载 SoundFont 文件。

因此在实践中,您只需在代码中将对 AKSampler 的引用重命名为 AKAppleSampler

您可以重写 AKsampler 示例中的 loadBetterSFZ 采样器扩展函数。这只读取包含的转换文件并假定示例文件是区域的最后一部分。

我这样做了,现在我可以使用 sfz 从复音 (windows) 和 sforzando 转换后的 sf2 文件,并据我所知 AKSampler 可以读取文件。我做了一个 class 来将 sfz 文件读入一个数据结构,该数据结构保存数据以供重用。对于采样器,我编写了一个从创建的数据加载的扩展。这一切又快又脏(我不是程序员!)。 sfz 文件的结构似乎有很多不同的方式,我只尝试了十几种。

示例:将 class SFZData 添加到示例中的导体

使用 func 获取数据 SFZData.parseSFZ(folderPath: String, sfzFileName: String)->SFZ?

使用 SFZ 作为采样器的扩展:func loadSFZData(sfz:SFZ)

//这是我所做的(所有最新版本并适用于 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
                }
            }

采样器扩展名:

     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()
        }