Что является заменой AKSampler.loadMelodicSoundFont в AudioKit 4.2? - PullRequest
0 голосов
/ 27 апреля 2018

Мне пришлось обновить AudioKit 4.0 до AudioKit 4.2 из-за несовместимости AudioKit 4.0 с последней версией языка swift и Xcode.

Однако теперь мой проект не может быть скомпилирован, потому что loadMelodicSoundFont больше не является членом AKSampler, пока я использую этот метод для загрузки звукового файла sf2.

Я мог найти документацию для 4.1 только на http://audiokit.io/docs/, и 4.1, очевидно, имеет loadMelodicSoundFont. И никакой документации для 4.2 я не смог найти.

Так что же заменит этот метод в AudioKit 4.2?

Ответы [ 2 ]

0 голосов
/ 26 октября 2018

Вы можете переписать функцию расширения семплера loadBetterSFZ в примере AKsampler. Это читает только для включенных преобразованных файлов и предполагает, что образец файла является последней частью региона.

Я так и сделал, и теперь я могу использовать sfz для конвертации файлов sf2 из полифонии (windows) и sforzando и читать файл, насколько я понимаю, AKSampler может держать. Я создал класс для чтения файла sfz в структуру данных, которая содержит данные для повторного использования. Для сэмплера я написал расширение, которое загружается из созданных данных. Все быстро и грязно (я не программист!). Существует множество способов структурирования файлов sfz, и я попробовал всего лишь дюжину.

пример: добавить класс SFZData к проводнику в примере

получить данные с помощью функции 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()
        }
0 голосов
/ 27 апреля 2018

Новый класс AKSampler в последних версиях AudioKit - это совершенно новый пользовательский сэмплер. Однако на данный момент он больше не обрабатывает файлы SoundFont изначально (именно таков ваш файл sf2).

Самый простой способ для вас - просто переключиться на AKAppleSampler, который является полнофункциональным сэмплером из предыдущих версий AudioKit. Он использует код Apple AU и по-прежнему может загружать файлы SoundFont.

Таким образом, на практике вы просто переименовали бы свои ссылки на AKSampler на AKAppleSampler в своем коде.

...