[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だとサイン波でしか音がならなかったり。。。
試行錯誤した結果なので、実はもっとシンプルな方法があるのかもしれないです。
コメント
コメントを投稿