소개

애플 기본 제공 라이브러리인 AVFAudio를 이용해 미디(확장자 *.mid) 파일을 재생합니다. 사운드폰트(*.sf2)를 이용해 플레이하므로 적절한 사운드폰트가 필요합니다. 오류가 발생하는 사운드폰트가 많으므로 다양하게 테스트하는 것을 권장합니다.

 

절차

1: 프로젝트에 사운드폰트 및 예제 미디파일 추가

 

2: 뷰모델(conductor) 및 뷰 만들기 (SwiftUI 기준)
  • 이번 예제는 SwiftUI를 사용하지만 MIDIFilePlayConductor 활용해서 UIKit에서도 사용할 수 있습니다.
  • 미디 파일은 url을 사용할 수 있으므로 Document 폴더 등에서 불러올 수 있습니다.
  • MIDIFilePlayConductor는 뷰모델 역할을 하고, 사운드 로드, 재생 및 정지 역할 등을 수행합니다.


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 {
                // 재생 완료되면 할 작업 핸들러
            }
        }
    }
}

문의 | 코멘트 또는 yoonbumtae@gmail.com


카테고리: Swift


0개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다