[Swift] MIDIデータの再生
先日のApp審査の却下を受けて、UIBackgroundModes Keyをオフにして再生する方法を調査。
結論としては、AudioKitのフレームワークは使用せず、Appleが提供しているフレームワークで実現することにしました。
これだけだと、サウンドフォントを使った再生がうまくできなかったので、AUGraphも使いました。
まずは、初期化。
これで、一通りセットアップが完了。
で、MIDIイベント(ノートオン)をMusicTrackに追加します。
再生は、MusicPlayerで行います。
(1) 再生停止、(2) 再生位置を最初(0秒)にセット、(3) 再生
の順に処理します。
これで、期待する動きをさせることができました。
AVAudioUnitSampler ならサウンドフォントで音が鳴ったので、AudioEngineで紐付けをして、MusicSequenceで再生してみたけど、ループ再生をやりたければ、MusicPlayerを使う必要があり、MusicPlayerだとサイン波でしか音がならなかったり。。。
試行錯誤した結果なので、実はもっとシンプルな方法があるのかもしれないです。
結論としては、AudioKitのフレームワークは使用せず、Appleが提供しているフレームワークで実現することにしました。
シーケンサー
MIDIデータを再生するために、下記のオブジェクト利用。var musicSequence : MusicSequence? var musicPlayer : MusicPlayer? var musicTrack : MusicTrack?
これだけだと、サウンドフォントを使った再生がうまくできなかったので、AUGraphも使いました。
var processGraph : AUGraph? var chordUnit : AudioUnit? var ioUnit : AudioUnit? var chordNode = AUNode() var ioNode = AUNode()
まずは、初期化。
init() { NewAUGraph(&processGraph) NewMusicPlayer(&musicPlayer) NewMusicSequence(&musicSequence) MusicSequenceNewTrack(musicSequence!, &musicTrack) MusicSequenceSetAUGraph(musicSequence!, processGraph) MusicPlayerSetSequence(musicPlayer!, musicSequence) setup() loadSF2Preset(unit: chordUnit!) } private func setup() { var cd:AudioComponentDescription = AudioComponentDescription( componentType: OSType(kAudioUnitType_MusicDevice), componentSubType: OSType(kAudioUnitSubType_Sampler), componentManufacturer: OSType(kAudioUnitManufacturer_Apple), componentFlags: 0, componentFlagsMask: 0) var ioUnitDescription:AudioComponentDescription = AudioComponentDescription( componentType: OSType(kAudioUnitType_Output), componentSubType: OSType(kAudioUnitSubType_RemoteIO), componentManufacturer: OSType(kAudioUnitManufacturer_Apple), componentFlags: 0, componentFlagsMask: 0) let ioUnitOutputElement:AudioUnitElement = 0 let chordOutputElement:AudioUnitElement = 0 var outIsInitialized:DarwinBoolean = false var isRunning:DarwinBoolean = false if let graph = processGraph { AUGraphAddNode(graph, &cd, &chordNode) AUGraphAddNode(graph, &ioUnitDescription, &ioNode) AUGraphOpen(graph) AUGraphNodeInfo(graph, chordNode, nil, &chordUnit) AUGraphNodeInfo(graph, ioNode , nil, &ioUnit) AUGraphConnectNodeInput(graph, chordNode, chordOutputElement, // srcnode, inSourceOutputNumber ioNode , ioUnitOutputElement) // destnode, inDestInputNumber AUGraphIsInitialized(graph, &outIsInitialized) if outIsInitialized == false { AUGraphInitialize(graph) } AUGraphIsRunning(graph, &isRunning) if isRunning == false { AUGraphStart(graph) } } } /// Get Sound Font URL private func soundbankURL(idx:Int) -> URL { let sfname = soundFontName[idx] guard let bankURL = Bundle.main.url(forResource: sfname, withExtension: "sf2") else { fatalError("Sound font file not found.") } return bankURL } /// Load Sound Font func loadSF2Preset(unit: AudioUnit, preset:UInt8 = 0, index: Int = 0) { let bankURL = soundbankURL(idx:index) var instdata = AUSamplerInstrumentData(fileURL: Unmanaged.passUnretained(bankURL as CFURL), instrumentType: UInt8(kInstrumentType_DLSPreset), bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), presetID: preset) AudioUnitSetProperty( unit, UInt32(kAUSamplerProperty_LoadInstrument), UInt32(kAudioUnitScope_Global), 0, &instdata, UInt32(MemoryLayout<AUSamplerInstrumentData>.size)) }
これで、一通りセットアップが完了。
で、MIDIイベント(ノートオン)をMusicTrackに追加します。
mess = MIDINoteMessage(channel: channel, note: UInt8(note), velocity: UInt8(vel), releaseVelocity: 0, duration: Float32(duration)) MusicTrackNewMIDINoteEvent(track, position, &mess)
再生は、MusicPlayerで行います。
(1) 再生停止、(2) 再生位置を最初(0秒)にセット、(3) 再生
の順に処理します。
MusicPlayerStop(player) MusicPlayerSetTime(player, 0) MusicPlayerStart(player)
これで、期待する動きをさせることができました。
AVAudioUnitSampler ならサウンドフォントで音が鳴ったので、AudioEngineで紐付けをして、MusicSequenceで再生してみたけど、ループ再生をやりたければ、MusicPlayerを使う必要があり、MusicPlayerだとサイン波でしか音がならなかったり。。。
試行錯誤した結果なので、実はもっとシンプルな方法があるのかもしれないです。
コメント
コメントを投稿