소개
애플 기본 제공 라이브러리인 AVFAudio
를 이용해 미디(확장자 *.mid
) 파일을 재생합니다. 사운드폰트(*.sf2
)를 이용해 플레이하므로 적절한 사운드폰트가 필요합니다. 오류가 발생하는 사운드폰트가 많으므로 다양하게 테스트하는 것을 권장합니다.
절차
1: 프로젝트에 사운드폰트 및 예제 미디파일 추가
2: 뷰모델(conductor) 및 뷰 만들기 (SwiftUI 기준)
- 이번 예제는
SwiftUI
를 사용하지만MIDIFilePlayConductor
만 활용해서UIKit
에서도 사용할 수 있습니다. - 미디 파일은
url
을 사용할 수 있으므로Document
폴더 등에서 불러올 수 있습니다. MIDIFilePlayConductor
는 뷰모델 역할을 하고, 사운드 로드, 재생 및 정지 역할 등을 수행합니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import AVFAudio | |
class MIDIFilePlayConductor: ObservableObject { | |
var midiPlayer: AVMIDIPlayer? | |
var soundfontURL: URL? = Bundle.main.url(forResource: "CT8MGM", withExtension: "sf2") | |
@Published var currentPosition: Double = 0 | |
@Published var duration: Double = 0 | |
@Published var isPlaying: Bool = false | |
init() { | |
guard let soundfontURL else { | |
return | |
} | |
guard let sampleMIDIFile = Bundle.main.url(forResource: "Human1", withExtension: "mid") else { | |
return | |
} | |
do { | |
midiPlayer = try AVMIDIPlayer(contentsOf: sampleMIDIFile, soundBankURL: soundfontURL) | |
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in | |
guard let self else { | |
return | |
} | |
currentPosition = midiPlayer?.currentPosition ?? 0 | |
duration = midiPlayer?.duration ?? 0 | |
isPlaying = midiPlayer?.isPlaying ?? false | |
} | |
} catch { | |
print(error) | |
} | |
} | |
func play() { | |
DispatchQueue.global().async { | |
self.midiPlayer?.play { | |
print("Music play completed.") | |
self.midiPlayer?.currentPosition = 0 | |
} | |
} | |
} | |
func stop() { | |
DispatchQueue.global().async { | |
self.midiPlayer?.stop() | |
} | |
} | |
func changePosition(_ position: Double) { | |
midiPlayer?.currentPosition = position | |
currentPosition = position | |
} | |
} | |
struct ContentView: View { | |
@StateObject var conductor = MIDIFilePlayConductor() | |
var body: some View { | |
VStack { | |
Button { | |
if conductor.isPlaying { | |
conductor.stop() | |
} else { | |
conductor.play() | |
} | |
} label: { | |
if conductor.isPlaying { | |
Image(systemName: "stop.fill") | |
} else { | |
Image(systemName: "play.fill") | |
} | |
} | |
Slider(value: $conductor.currentPosition, in: 0…conductor.duration) { result in | |
conductor.changePosition(conductor.currentPosition) | |
} | |
} | |
.padding() | |
} | |
} |
참고) 기본 라이브러리를 사용한 미디 재생은 시뮬레이터에서는 사운드폰트가 적용되지 않고 비프음만 나오므로 실제 기기에서 테스트해야 됩니다.
재생 파일 변경 방법
func changeAndPlayMIDIFile(_ fileNameWithoutExt: String) { DispatchQueue.global().async { [weak self] in guard let self, let soundfontURL else { return } if let midiFile = Bundle.main.url(forResource: fileNameWithoutExt, withExtension: "mid") { midiPlayer = try? .init(contentsOf: midiFile, soundBankURL: soundfontURL) self.fileNameWithoutExt = fileNameWithoutExt midiPlayer?.play { // 재생 완료되면 할 작업 핸들러 } } } }
0개의 댓글