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

时间:2018-04-27 01:58:27

标签: ios swift audiokit

由于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中这个方法的替代品是什么?

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

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

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

示例:在示例中将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()
        }