adding packages
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
|
||||
//
|
||||
// AudioUnitMIDISynth.swift
|
||||
// MIDISynth
|
||||
//
|
||||
// Created by Gene De Lisa on 2/6/16.
|
||||
// Copyright © 2016 Gene De Lisa. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
import AudioToolbox
|
||||
import CoreAudio
|
||||
|
||||
// swiftlint:disable function_body_length
|
||||
// swiftlint:disable type_body_length
|
||||
// swiftlint:disable file_length
|
||||
// swiftlint:disable line_length
|
||||
/// # A Core Audio MIDISynth `AudioUnit` example.
|
||||
/// This will add a polyphonic `kAudioUnitSubType_MIDISynth` audio unit to the `AUGraph`.
|
||||
///
|
||||
/// - author: Gene De Lisa
|
||||
/// - copyright: 2016 Gene De Lisa
|
||||
/// - date: February 2016
|
||||
class AudioUnitMIDISynth: NSObject {
|
||||
|
||||
var processingGraph: AUGraph?
|
||||
var midisynthNode = AUNode()
|
||||
var ioNode = AUNode()
|
||||
var midisynthUnit: AudioUnit?
|
||||
var ioUnit: AudioUnit?
|
||||
var musicSequence: MusicSequence!
|
||||
var musicPlayer: MusicPlayer!
|
||||
let patch1 = UInt32(46)
|
||||
let patch2 = UInt32(0)
|
||||
var pitch = UInt32(60)
|
||||
|
||||
var bankUrl: URL!
|
||||
|
||||
init(soundfont: URL) {
|
||||
super.init()
|
||||
prepare(soundfont:soundfont)
|
||||
|
||||
}
|
||||
|
||||
func prepare(soundfont: URL) {
|
||||
bankUrl = soundfont;
|
||||
augraphSetup()
|
||||
loadMIDISynthSoundFont()
|
||||
initializeGraph()
|
||||
self.musicSequence = createMusicSequence()
|
||||
musicPlayer = createPlayer(musicSequence)
|
||||
loadPatches()
|
||||
startGraph()
|
||||
}
|
||||
|
||||
/// Create the `AUGraph`, the nodes and units, then wire them together.
|
||||
func augraphSetup() {
|
||||
var status = OSStatus(noErr)
|
||||
|
||||
status = NewAUGraph(&processingGraph)
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
createIONode()
|
||||
|
||||
createSynthNode()
|
||||
|
||||
// now do the wiring. The graph needs to be open before you call AUGraphNodeInfo
|
||||
status = AUGraphOpen(self.processingGraph!)
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
status = AUGraphNodeInfo(self.processingGraph!, self.midisynthNode, nil, &midisynthUnit)
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
status = AUGraphNodeInfo(self.processingGraph!, self.ioNode, nil, &ioUnit)
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
|
||||
let synthOutputElement: AudioUnitElement = 0
|
||||
let ioUnitInputElement: AudioUnitElement = 0
|
||||
|
||||
status = AUGraphConnectNodeInput(self.processingGraph!,
|
||||
self.midisynthNode, synthOutputElement, // srcnode, SourceOutputNumber
|
||||
self.ioNode, ioUnitInputElement) // destnode, DestInputNumber
|
||||
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
/// Create the Output Node and add it to the `AUGraph`.
|
||||
func createIONode() {
|
||||
var cd = AudioComponentDescription(
|
||||
componentType: OSType(kAudioUnitType_Output),
|
||||
componentSubType: OSType(kAudioUnitSubType_RemoteIO),
|
||||
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
|
||||
componentFlags: 0, componentFlagsMask: 0)
|
||||
let status = AUGraphAddNode(self.processingGraph!, &cd, &ioNode)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
/// Create the Synth Node and add it to the `AUGraph`.
|
||||
func createSynthNode() {
|
||||
var cd = AudioComponentDescription(
|
||||
componentType: OSType(kAudioUnitType_MusicDevice),
|
||||
componentSubType: OSType(kAudioUnitSubType_MIDISynth),
|
||||
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
|
||||
componentFlags: 0, componentFlagsMask: 0)
|
||||
let status = AUGraphAddNode(self.processingGraph!, &cd, &midisynthNode)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// This will load the default sound font and set the synth unit's property.
|
||||
/// - postcondition: `self.midisynthUnit` will have it's sound font url set.
|
||||
func loadMIDISynthSoundFont() {
|
||||
|
||||
if var bankURL = bankUrl {
|
||||
|
||||
let status = AudioUnitSetProperty(
|
||||
self.midisynthUnit!,
|
||||
AudioUnitPropertyID(kMusicDeviceProperty_SoundBankURL),
|
||||
AudioUnitScope(kAudioUnitScope_Global),
|
||||
0,
|
||||
&bankURL,
|
||||
UInt32(MemoryLayout<URL>.size))
|
||||
|
||||
AudioUtils.CheckError(status)
|
||||
print("loaded sound font")
|
||||
} else {
|
||||
print("Could not load sound font")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Pre-load the patches you will use.
|
||||
///
|
||||
/// Turn on `kAUMIDISynthProperty_EnablePreload` so the midisynth will load the patch data from the file into memory.
|
||||
/// You load the patches first before playing a sequence or sending messages.
|
||||
/// Then you turn `kAUMIDISynthProperty_EnablePreload` off. It is now in a state where it will respond to MIDI program
|
||||
/// change messages and switch to the already cached instrument data.
|
||||
///
|
||||
/// - precondition: the graph must be initialized
|
||||
///
|
||||
/// [Doug's post](http://prod.lists.apple.com/archives/coreaudio-api/2016/Jan/msg00018.html)
|
||||
func loadPatches() {
|
||||
|
||||
if !isGraphInitialized() {
|
||||
fatalError("initialize graph first")
|
||||
}
|
||||
|
||||
let channel = UInt32(0)
|
||||
var enabled = UInt32(1)
|
||||
|
||||
var status = AudioUnitSetProperty(
|
||||
self.midisynthUnit!,
|
||||
AudioUnitPropertyID(kAUMIDISynthProperty_EnablePreload),
|
||||
AudioUnitScope(kAudioUnitScope_Global),
|
||||
0,
|
||||
&enabled,
|
||||
UInt32(MemoryLayout<UInt32>.size))
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
// let bankSelectCommand = UInt32(0xB0 | 0)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit, bankSelectCommand, 0, 0, 0)
|
||||
|
||||
let pcCommand = UInt32(0xC0 | channel)
|
||||
status = MusicDeviceMIDIEvent(self.midisynthUnit!, pcCommand, patch1, 0, 0)
|
||||
AudioUtils.CheckError(status)
|
||||
status = MusicDeviceMIDIEvent(self.midisynthUnit!, pcCommand, patch2, 0, 0)
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
enabled = UInt32(0)
|
||||
status = AudioUnitSetProperty(
|
||||
self.midisynthUnit!,
|
||||
AudioUnitPropertyID(kAUMIDISynthProperty_EnablePreload),
|
||||
AudioUnitScope(kAudioUnitScope_Global),
|
||||
0,
|
||||
&enabled,
|
||||
UInt32(MemoryLayout<UInt32>.size))
|
||||
AudioUtils.CheckError(status)
|
||||
|
||||
// at this point the patches are loaded. You still have to send a program change at "play time" for the synth
|
||||
// to switch to that patch
|
||||
}
|
||||
|
||||
|
||||
/// Check to see if the `AUGraph` is Initialized.
|
||||
///
|
||||
/// - returns: `true` if it's running, `false` if not
|
||||
/// - seealso: [AUGraphIsInitialized](/https://developer.apple.com/library/prerelease/ios/documentation/AudioToolbox/Reference/AUGraphServicesReference/index.html#//apple_ref/c/func/AUGraphIsInitialized)
|
||||
func isGraphInitialized() -> Bool {
|
||||
var outIsInitialized = DarwinBoolean(false)
|
||||
let status = AUGraphIsInitialized(self.processingGraph!, &outIsInitialized)
|
||||
AudioUtils.CheckError(status)
|
||||
return outIsInitialized.boolValue
|
||||
}
|
||||
|
||||
/// Initializes the `AUGraph.
|
||||
func initializeGraph() {
|
||||
let status = AUGraphInitialize(self.processingGraph!)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
/// Starts the `AUGraph`
|
||||
func startGraph() {
|
||||
let status = AUGraphStart(self.processingGraph!)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
/// Check to see if the `AUGraph` is running.
|
||||
///
|
||||
/// - returns: `true` if it's running, `false` if not
|
||||
func isGraphRunning() -> Bool {
|
||||
var isRunning = DarwinBoolean(false)
|
||||
let status = AUGraphIsRunning(self.processingGraph!, &isRunning)
|
||||
AudioUtils.CheckError(status)
|
||||
return isRunning.boolValue
|
||||
}
|
||||
|
||||
/// Generate a random pitch between 36 (C below middle C) and 100.
|
||||
///
|
||||
/// - postcondition: self.pitch is modified
|
||||
func generateRandomPitch() {
|
||||
pitch = arc4random_uniform(64) + 36 // 36 - 100
|
||||
}
|
||||
|
||||
// /// Send a note on message using patch1 on channel 0
|
||||
// func playPatch1On() {
|
||||
//
|
||||
// let channel = UInt32(0)
|
||||
// let noteCommand = UInt32(0x90 | channel)
|
||||
// let pcCommand = UInt32(0xC0 | channel)
|
||||
// var status = OSStatus(noErr)
|
||||
//
|
||||
// generateRandomPitch()
|
||||
// print(pitch)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, pcCommand, patch1, 0, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, pitch, 64, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// }
|
||||
//
|
||||
// /// Send a note off message using patch1 on channel 0
|
||||
// func playPatch1Off() {
|
||||
// let channel = UInt32(0)
|
||||
// let noteCommand = UInt32(0x80 | channel)
|
||||
// var status = OSStatus(noErr)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, pitch, 0, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// }
|
||||
//
|
||||
// /// Send a note on message using patch2 on channel 0
|
||||
// func playPatch2On(midi: Int) {
|
||||
//
|
||||
// let channel = UInt32(0)
|
||||
// let noteCommand = UInt32(0x90 | channel)
|
||||
// let pcCommand = UInt32(0xC0 | channel)
|
||||
// var status = OSStatus(noErr)
|
||||
// pitch = UInt32(midi)
|
||||
// //generateRandomPitch()
|
||||
// print(pitch)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, pcCommand, patch2, 0, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, pitch, 64, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// }
|
||||
//
|
||||
// /// Send a note off message using patch2 on channel 0
|
||||
// func playPatch2Off() {
|
||||
// let channel = UInt32(0)
|
||||
// let noteCommand = UInt32(0x80 | channel)
|
||||
// var status = OSStatus(noErr)
|
||||
// status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, pitch, 0, 0)
|
||||
// AudioUtils.CheckError(status)
|
||||
// }
|
||||
//
|
||||
|
||||
/// Send a note on message using patch2 on channel 0
|
||||
func playPitch(midi: Int, velocity: Int) {
|
||||
|
||||
let channel = UInt32(0)
|
||||
let noteCommand = UInt32(0x90 | channel)
|
||||
let pcCommand = UInt32(0xC0 | channel)
|
||||
var status = OSStatus(noErr)
|
||||
|
||||
status = MusicDeviceMIDIEvent(self.midisynthUnit!, pcCommand, patch2, 0, 0)
|
||||
AudioUtils.CheckError(status)
|
||||
status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, UInt32(midi), UInt32(velocity), 0)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
/// Send a note off message using patch2 on channel 0
|
||||
func stopPitch(midi: Int, velocity: Int) {
|
||||
let channel = UInt32(0)
|
||||
let noteCommand = UInt32(0x80 | channel)
|
||||
var status = OSStatus(noErr)
|
||||
status = MusicDeviceMIDIEvent(self.midisynthUnit!, noteCommand, UInt32(midi), UInt32(velocity), 0)
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
|
||||
/// Create a test `MusicSequence`.
|
||||
///
|
||||
/// - throws: Nothing, but it should
|
||||
/// - todo: create an `ErrorType` ennum
|
||||
/// - returns: a `MusicSequence`
|
||||
func createMusicSequence() -> MusicSequence {
|
||||
|
||||
var musicSequence: MusicSequence?
|
||||
var status = NewMusicSequence(&musicSequence)
|
||||
if status != noErr {
|
||||
print("\(#line) bad status \(status) creating sequence")
|
||||
}
|
||||
|
||||
// add a track
|
||||
var track: MusicTrack?
|
||||
status = MusicSequenceNewTrack(musicSequence!, &track)
|
||||
if status != noErr {
|
||||
print("error creating track \(status)")
|
||||
}
|
||||
|
||||
var channel = UInt8(0)
|
||||
// bank select msb
|
||||
var chanmess = MIDIChannelMessage(status: 0xB0 | channel, data1: 0, data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating bank select event \(status)")
|
||||
}
|
||||
// bank select lsb
|
||||
chanmess = MIDIChannelMessage(status: 0xB0 | channel, data1: 32, data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating bank select event \(status)")
|
||||
}
|
||||
|
||||
// program change. first data byte is the patch, the second data byte is unused for program change messages.
|
||||
chanmess = MIDIChannelMessage(status: 0xC0 | channel, data1: UInt8(patch1), data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating program change event \(status)")
|
||||
}
|
||||
|
||||
// now make some notes and put them on the track
|
||||
var beat = MusicTimeStamp(0.0)
|
||||
for i: UInt8 in 60...72 {
|
||||
var mess = MIDINoteMessage(channel: channel,
|
||||
note: i,
|
||||
velocity: 64,
|
||||
releaseVelocity: 0,
|
||||
duration: 1.0 )
|
||||
status = MusicTrackNewMIDINoteEvent(track!, beat, &mess)
|
||||
if status != noErr {
|
||||
print("creating new midi note event \(status)")
|
||||
}
|
||||
beat += 1
|
||||
}
|
||||
|
||||
// another track
|
||||
|
||||
channel = UInt8(1)
|
||||
|
||||
track = nil
|
||||
status = MusicSequenceNewTrack(musicSequence!, &track)
|
||||
if status != noErr {
|
||||
print("error creating track \(status)")
|
||||
}
|
||||
|
||||
chanmess = MIDIChannelMessage(status: 0xB0 | channel, data1: 0, data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating bank select msb event \(status)")
|
||||
}
|
||||
|
||||
chanmess = MIDIChannelMessage(status: 0xB0 | channel, data1: 32, data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating bank select lsb event \(status)")
|
||||
}
|
||||
|
||||
chanmess = MIDIChannelMessage(status: 0xC0 | channel, data1: UInt8(patch2), data2: 0, reserved: 0)
|
||||
status = MusicTrackNewMIDIChannelEvent(track!, 0, &chanmess)
|
||||
if status != noErr {
|
||||
print("creating program change event \(status)")
|
||||
}
|
||||
|
||||
beat = MusicTimeStamp(3.0)
|
||||
for i: UInt8 in 60...72 {
|
||||
var mess = MIDINoteMessage(channel: channel,
|
||||
note: i,
|
||||
velocity: 36,
|
||||
releaseVelocity: 0,
|
||||
|
||||
duration: 1.0 )
|
||||
status = MusicTrackNewMIDINoteEvent(track!, beat, &mess)
|
||||
if status != OSStatus(noErr) {
|
||||
print("creating new midi note event \(status)")
|
||||
}
|
||||
beat += 1
|
||||
}
|
||||
|
||||
// associate the AUGraph with the sequence.
|
||||
status = MusicSequenceSetAUGraph(musicSequence!, self.processingGraph)
|
||||
|
||||
// Let's see it
|
||||
CAShow(UnsafeMutablePointer<MusicSequence>(musicSequence!))
|
||||
|
||||
return musicSequence!
|
||||
}
|
||||
|
||||
/// Create a `MusicPlayer` with the specified sequence.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - musicSequence: a valid `MusicSequence` instance
|
||||
/// - throws: Nothing, but it should
|
||||
/// - todo: create an `ErrorType` ennum
|
||||
/// - returns: a `MusicPlayer`
|
||||
func createPlayer(_ musicSequence: MusicSequence) -> MusicPlayer {
|
||||
var musicPlayer: MusicPlayer?
|
||||
|
||||
var status = NewMusicPlayer(&musicPlayer)
|
||||
if status != OSStatus(noErr) {
|
||||
print("bad status \(status) creating player")
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
|
||||
status = MusicPlayerSetSequence(musicPlayer!, musicSequence)
|
||||
if status != OSStatus(noErr) {
|
||||
print("setting sequence \(status)")
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
status = MusicPlayerPreroll(musicPlayer!)
|
||||
if status != OSStatus(noErr) {
|
||||
print("prerolling player \(status)")
|
||||
AudioUtils.CheckError(status)
|
||||
}
|
||||
return musicPlayer!
|
||||
}
|
||||
|
||||
/// Make the `MusicPlayer` play its sequence
|
||||
/// - throws: Nothing, but it should
|
||||
/// - todo: create an `ErrorType` ennum
|
||||
func musicPlayerPlay() {
|
||||
var status = noErr
|
||||
var playing: DarwinBoolean = false
|
||||
status = MusicPlayerIsPlaying(musicPlayer, &playing)
|
||||
if playing != false {
|
||||
status = MusicPlayerStop(musicPlayer)
|
||||
if status != noErr {
|
||||
print("Error stopping \(status)")
|
||||
AudioUtils.CheckError(status)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
status = MusicPlayerSetTime(musicPlayer, 0)
|
||||
if status != noErr {
|
||||
print("setting time \(status)")
|
||||
AudioUtils.CheckError(status)
|
||||
return
|
||||
}
|
||||
|
||||
status = MusicPlayerStart(musicPlayer)
|
||||
if status != noErr {
|
||||
print("Error starting \(status)")
|
||||
AudioUtils.CheckError(status)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
//
|
||||
// AudioUtils.swift
|
||||
// SwiftMusic
|
||||
//
|
||||
// Created by Gene De Lisa on 2/28/15.
|
||||
// Copyright (c) 2015 Gene De Lisa. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AudioToolbox
|
||||
import CoreAudio
|
||||
|
||||
// swiftlint:disable function_body_length
|
||||
// swiftlint:disable type_body_length
|
||||
// swiftlint:disable file_length
|
||||
|
||||
|
||||
#if os(OSX)
|
||||
// for UTCreateStringForOSType
|
||||
import CoreServices
|
||||
|
||||
#endif
|
||||
|
||||
#if os(tvOS)
|
||||
#elseif os(iOS)
|
||||
import CoreMIDI
|
||||
#endif
|
||||
|
||||
import AVFoundation
|
||||
|
||||
|
||||
/// # A few utilities for using Core Audio.
|
||||
///
|
||||
/// I started with [Adamson's](http://amzn.to/1KG0yWe) Objective-C CheckError method. The translation
|
||||
/// into Swift was a bit of a pain.
|
||||
///
|
||||
/// - author: Gene De Lisa
|
||||
/// - copyright: 2016 Gene De Lisa
|
||||
/// - date: February 2016
|
||||
open class AudioUtils {
|
||||
|
||||
fileprivate init() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Create a String from an encoded 4char.
|
||||
///
|
||||
/// - parameter n: The encoded 4char
|
||||
///
|
||||
/// - returns: The String representation.
|
||||
class func stringFrom4(_ n: Int) -> String {
|
||||
|
||||
var scalar = UnicodeScalar((n >> 24) & 255)
|
||||
if !scalar!.isASCII {
|
||||
return ""
|
||||
}
|
||||
var s = String(describing: scalar)
|
||||
|
||||
scalar = UnicodeScalar((n >> 16) & 255)
|
||||
if !scalar!.isASCII {
|
||||
return ""
|
||||
}
|
||||
s.append(String(describing: scalar))
|
||||
|
||||
scalar = UnicodeScalar((n >> 8) & 255)
|
||||
if !scalar!.isASCII {
|
||||
return ""
|
||||
}
|
||||
s.append(String(describing: scalar))
|
||||
|
||||
scalar = UnicodeScalar(n & 255)
|
||||
if !scalar!.isASCII {
|
||||
return ""
|
||||
}
|
||||
s.append(String(describing: scalar))
|
||||
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
/// Create a String from an encoded 4char.
|
||||
///
|
||||
/// - parameter status: an `OSStatus` containing the encoded 4char.
|
||||
///
|
||||
/// - returns: The String representation.
|
||||
class func stringFrom4(_ status: OSStatus) -> String {
|
||||
let n = Int(status)
|
||||
return stringFrom4(n)
|
||||
}
|
||||
|
||||
/// Create an encoded 4char from a String.
|
||||
///
|
||||
/// - parameter s: The String.
|
||||
///
|
||||
/// - returns: the encoded Int
|
||||
class func valueFromString4(_ s: String) -> Int {
|
||||
var n = 0
|
||||
var r = ""
|
||||
|
||||
if s.count > 4 {
|
||||
let startIndex = s.index(s.startIndex, offsetBy: 4)
|
||||
// r = s.substring(from: startIndex)
|
||||
r = String(s[startIndex...])
|
||||
} else {
|
||||
r = s + " "
|
||||
let startIndex = s.index(s.startIndex, offsetBy: 4)
|
||||
// r = r.substring(to: startIndex)
|
||||
r = String(r[...startIndex])
|
||||
|
||||
}
|
||||
|
||||
for UniCodeChar in r.unicodeScalars {
|
||||
n = (n << 8) + (Int(UniCodeChar.value) & 255)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
|
||||
// osx only
|
||||
//let s = UTCreateStringForOSType(error)
|
||||
//
|
||||
// let e = UInt32(error)
|
||||
//// let swapped = CFSwapInt32HostToBig(e)
|
||||
// let swapped = e
|
||||
//
|
||||
// let b1 = Character(UnicodeScalar(swapped & 0b00001))
|
||||
// let b2 = Character(UnicodeScalar(swapped & 0b00010))
|
||||
// let b3 = Character(UnicodeScalar(swapped & 0b00100))
|
||||
// let b4 = Character(UnicodeScalar(swapped & 0b01000))
|
||||
// print("check error string: \(b1) \(b2) \(b3) \(b4)")
|
||||
// func OSTypeFrom(string : String) -> UInt {
|
||||
// var result : UInt = 0
|
||||
// if let data = string.dataUsingEncoding(NSMacOSRomanStringEncoding) {
|
||||
// let bytes = UnsafePointer<UInt8>(data.bytes)
|
||||
// for i in 0..<data.length {
|
||||
// result = result << 8 + UInt(bytes[i])
|
||||
// }
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
|
||||
|
||||
// func stringValue(unicodeValue: Int) -> String {
|
||||
// var stringValue = ""
|
||||
// var value = unicodeValue
|
||||
// for _ in 0..<4 {
|
||||
// stringValue = String(UnicodeScalar(value & 255)) + stringValue
|
||||
// value = value / 256
|
||||
// }
|
||||
// return stringValue
|
||||
// }
|
||||
|
||||
|
||||
|
||||
#if os(tvOS)
|
||||
/// Check the status code
|
||||
///
|
||||
/// - parameter error: The status to check.
|
||||
/// - todo: Finish this for tvOS
|
||||
class func CheckError(status: OSStatus) {
|
||||
if status == noErr {
|
||||
print("no error")
|
||||
return
|
||||
}
|
||||
print("error \(status)")
|
||||
}
|
||||
#elseif os(iOS)
|
||||
/// Print a description of the status code to stdout if it's an error.
|
||||
///
|
||||
/// - parameter status: the status to check.
|
||||
class func CheckError(_ status: OSStatus) {
|
||||
|
||||
if status == noErr {
|
||||
return
|
||||
}
|
||||
|
||||
let s = stringFrom4(status)
|
||||
print("error chars '\(s)'")
|
||||
|
||||
|
||||
switch status {
|
||||
// AudioToolbox
|
||||
case kAUGraphErr_NodeNotFound:
|
||||
print("kAUGraphErr_NodeNotFound")
|
||||
|
||||
case kAUGraphErr_OutputNodeErr:
|
||||
print("kAUGraphErr_OutputNodeErr")
|
||||
|
||||
case kAUGraphErr_InvalidConnection:
|
||||
print("kAUGraphErr_InvalidConnection")
|
||||
|
||||
case kAUGraphErr_CannotDoInCurrentContext:
|
||||
print("kAUGraphErr_CannotDoInCurrentContext")
|
||||
|
||||
case kAUGraphErr_InvalidAudioUnit:
|
||||
print("kAUGraphErr_InvalidAudioUnit")
|
||||
|
||||
case kMIDIInvalidClient :
|
||||
print("kMIDIInvalidClient ")
|
||||
|
||||
|
||||
case kMIDIInvalidPort :
|
||||
print("kMIDIInvalidPort ")
|
||||
|
||||
|
||||
case kMIDIWrongEndpointType :
|
||||
print("kMIDIWrongEndpointType")
|
||||
|
||||
|
||||
case kMIDINoConnection :
|
||||
print("kMIDINoConnection ")
|
||||
|
||||
|
||||
case kMIDIUnknownEndpoint :
|
||||
print("kMIDIUnknownEndpoint ")
|
||||
|
||||
|
||||
case kMIDIUnknownProperty :
|
||||
print("kMIDIUnknownProperty ")
|
||||
|
||||
|
||||
case kMIDIWrongPropertyType :
|
||||
print("kMIDIWrongPropertyType ")
|
||||
|
||||
|
||||
case kMIDINoCurrentSetup :
|
||||
print("kMIDINoCurrentSetup ")
|
||||
|
||||
|
||||
case kMIDIMessageSendErr :
|
||||
print("kMIDIMessageSendErr ")
|
||||
|
||||
|
||||
case kMIDIServerStartErr :
|
||||
print("kMIDIServerStartErr ")
|
||||
|
||||
|
||||
case kMIDISetupFormatErr :
|
||||
print("kMIDISetupFormatErr ")
|
||||
|
||||
|
||||
case kMIDIWrongThread :
|
||||
print("kMIDIWrongThread ")
|
||||
|
||||
|
||||
case kMIDIObjectNotFound :
|
||||
print("kMIDIObjectNotFound ")
|
||||
|
||||
|
||||
case kMIDIIDNotUnique :
|
||||
print("kMIDIIDNotUnique ")
|
||||
|
||||
|
||||
case kAudioToolboxErr_InvalidSequenceType :
|
||||
print("kAudioToolboxErr_InvalidSequenceType ")
|
||||
|
||||
case kAudioToolboxErr_TrackIndexError :
|
||||
print("kAudioToolboxErr_TrackIndexError ")
|
||||
|
||||
case kAudioToolboxErr_TrackNotFound :
|
||||
print("kAudioToolboxErr_TrackNotFound ")
|
||||
|
||||
case kAudioToolboxErr_EndOfTrack :
|
||||
print("kAudioToolboxErr_EndOfTrack ")
|
||||
|
||||
case kAudioToolboxErr_StartOfTrack :
|
||||
print("kAudioToolboxErr_StartOfTrack ")
|
||||
|
||||
case kAudioToolboxErr_IllegalTrackDestination:
|
||||
print("kAudioToolboxErr_IllegalTrackDestination")
|
||||
|
||||
case kAudioToolboxErr_NoSequence :
|
||||
print("kAudioToolboxErr_NoSequence ")
|
||||
|
||||
case kAudioToolboxErr_InvalidEventType :
|
||||
print("kAudioToolboxErr_InvalidEventType")
|
||||
|
||||
case kAudioToolboxErr_InvalidPlayerState:
|
||||
print("kAudioToolboxErr_InvalidPlayerState")
|
||||
|
||||
case kAudioUnitErr_InvalidProperty :
|
||||
print("kAudioUnitErr_InvalidProperty")
|
||||
|
||||
case kAudioUnitErr_InvalidParameter :
|
||||
print("kAudioUnitErr_InvalidParameter")
|
||||
|
||||
case kAudioUnitErr_InvalidElement :
|
||||
print("kAudioUnitErr_InvalidElement")
|
||||
|
||||
case kAudioUnitErr_NoConnection :
|
||||
print("kAudioUnitErr_NoConnection")
|
||||
|
||||
case kAudioUnitErr_FailedInitialization :
|
||||
print("kAudioUnitErr_FailedInitialization")
|
||||
|
||||
case kAudioUnitErr_TooManyFramesToProcess:
|
||||
print("kAudioUnitErr_TooManyFramesToProcess")
|
||||
|
||||
case kAudioUnitErr_InvalidFile:
|
||||
print("kAudioUnitErr_InvalidFile")
|
||||
|
||||
case kAudioUnitErr_FormatNotSupported :
|
||||
print("kAudioUnitErr_FormatNotSupported")
|
||||
|
||||
case kAudioUnitErr_Uninitialized:
|
||||
print("kAudioUnitErr_Uninitialized")
|
||||
|
||||
case kAudioUnitErr_InvalidScope :
|
||||
print("kAudioUnitErr_InvalidScope")
|
||||
|
||||
case kAudioUnitErr_PropertyNotWritable :
|
||||
print("kAudioUnitErr_PropertyNotWritable")
|
||||
|
||||
case kAudioUnitErr_InvalidPropertyValue :
|
||||
print("kAudioUnitErr_InvalidPropertyValue")
|
||||
|
||||
case kAudioUnitErr_PropertyNotInUse :
|
||||
print("kAudioUnitErr_PropertyNotInUse")
|
||||
|
||||
case kAudioUnitErr_Initialized :
|
||||
print("kAudioUnitErr_Initialized")
|
||||
|
||||
case kAudioUnitErr_InvalidOfflineRender :
|
||||
print("kAudioUnitErr_InvalidOfflineRender")
|
||||
|
||||
case kAudioUnitErr_Unauthorized :
|
||||
print("kAudioUnitErr_Unauthorized")
|
||||
|
||||
case kAudioUnitErr_CannotDoInCurrentContext:
|
||||
print("kAudioUnitErr_CannotDoInCurrentContext")
|
||||
|
||||
case kAudioUnitErr_FailedInitialization:
|
||||
print("kAudioUnitErr_FailedInitialization")
|
||||
|
||||
case kAudioUnitErr_FileNotSpecified:
|
||||
print("kAudioUnitErr_FileNotSpecified")
|
||||
|
||||
case kAudioUnitErr_FormatNotSupported:
|
||||
print("kAudioUnitErr_FormatNotSupported")
|
||||
|
||||
case kAudioUnitErr_IllegalInstrument:
|
||||
print("kAudioUnitErr_IllegalInstrument")
|
||||
|
||||
case kAudioUnitErr_Initialized:
|
||||
print("kAudioUnitErr_Initialized")
|
||||
|
||||
case kAudioUnitErr_InstrumentTypeNotFound:
|
||||
print("kAudioUnitErr_InstrumentTypeNotFound")
|
||||
|
||||
case kAudioUnitErr_InvalidElement:
|
||||
print("kAudioUnitErr_InvalidElement")
|
||||
|
||||
case kAudioUnitErr_InvalidFile:
|
||||
print("kAudioUnitErr_InvalidFile")
|
||||
|
||||
case kAudioUnitErr_InvalidOfflineRender:
|
||||
print("kAudioUnitErr_InvalidOfflineRender")
|
||||
|
||||
case kAudioUnitErr_InvalidParameter:
|
||||
print("kAudioUnitErr_InvalidParameter")
|
||||
|
||||
case kAudioUnitErr_InvalidProperty:
|
||||
print("kAudioUnitErr_InvalidProperty")
|
||||
|
||||
case kAudioUnitErr_InvalidPropertyValue:
|
||||
print("kAudioUnitErr_InvalidPropertyValue")
|
||||
|
||||
case kAudioUnitErr_InvalidScope:
|
||||
print("kAudioUnitErr_InvalidScope")
|
||||
|
||||
case kAudioUnitErr_NoConnection:
|
||||
print("kAudioUnitErr_NoConnection")
|
||||
|
||||
case kAudioUnitErr_PropertyNotInUse:
|
||||
print("kAudioUnitErr_PropertyNotInUse")
|
||||
|
||||
case kAudioUnitErr_PropertyNotWritable:
|
||||
print("kAudioUnitErr_PropertyNotWritable")
|
||||
|
||||
case kAudioUnitErr_TooManyFramesToProcess:
|
||||
print("kAudioUnitErr_TooManyFramesToProcess")
|
||||
|
||||
case kAudioUnitErr_Unauthorized:
|
||||
print("kAudioUnitErr_Unauthorized")
|
||||
|
||||
case kAudioUnitErr_Uninitialized:
|
||||
print("kAudioUnitErr_Uninitialized")
|
||||
|
||||
case kAudioUnitErr_UnknownFileType:
|
||||
print("kAudioUnitErr_UnknownFileType")
|
||||
|
||||
case kAudioComponentErr_InstanceInvalidated:
|
||||
print("kAudioComponentErr_InstanceInvalidated ")
|
||||
|
||||
case kAudioComponentErr_DuplicateDescription:
|
||||
print("kAudioComponentErr_DuplicateDescription ")
|
||||
|
||||
case kAudioComponentErr_UnsupportedType:
|
||||
print("kAudioComponentErr_UnsupportedType ")
|
||||
|
||||
case kAudioComponentErr_TooManyInstances:
|
||||
print("kAudioComponentErr_TooManyInstances ")
|
||||
|
||||
case kAudioComponentErr_NotPermitted:
|
||||
print("kAudioComponentErr_NotPermitted ")
|
||||
|
||||
case kAudioComponentErr_InitializationTimedOut:
|
||||
print("kAudioComponentErr_InitializationTimedOut ")
|
||||
|
||||
case kAudioComponentErr_InvalidFormat:
|
||||
print("kAudioComponentErr_InvalidFormat ")
|
||||
|
||||
// in CoreAudioTypes
|
||||
case kAudio_UnimplementedError :
|
||||
print("kAudio_UnimplementedError")
|
||||
|
||||
case kAudio_FileNotFoundError :
|
||||
print("kAudio_FileNotFoundError")
|
||||
|
||||
case kAudio_FilePermissionError :
|
||||
print("kAudio_FilePermissionError")
|
||||
|
||||
case kAudio_TooManyFilesOpenError :
|
||||
print("kAudio_TooManyFilesOpenError")
|
||||
|
||||
case kAudio_BadFilePathError :
|
||||
print("kAudio_BadFilePathError")
|
||||
|
||||
case kAudio_ParamError :
|
||||
print("kAudio_ParamError") // the infamous -50
|
||||
|
||||
case kAudio_MemFullError :
|
||||
print("kAudio_MemFullError")
|
||||
|
||||
|
||||
default:
|
||||
print("huh?")
|
||||
print("bad status \(status)")
|
||||
//print("\(__LINE__) bad status \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface FlutterMidiPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "FlutterMidiPlugin.h"
|
||||
#import <flutter_midi/flutter_midi-Swift.h>
|
||||
|
||||
@implementation FlutterMidiPlugin
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
[SwiftFlutterMidiPlugin registerWithRegistrar:registrar];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,61 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public class SwiftFlutterMidiPlugin: NSObject, FlutterPlugin {
|
||||
var message = "Please Send Message"
|
||||
var _arguments = [String: Any]()
|
||||
var au: AudioUnitMIDISynth!
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "flutter_midi", binaryMessenger: registrar.messenger())
|
||||
let instance = SwiftFlutterMidiPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "prepare_midi":
|
||||
let map = call.arguments as? Dictionary<String, String>
|
||||
let data = map?["path"]
|
||||
let url = URL(fileURLWithPath: data!)
|
||||
au = AudioUnitMIDISynth(soundfont: url)
|
||||
print("Valid URL: \(url)")
|
||||
let message = "Prepared Sound Font"
|
||||
result(message)
|
||||
case "change_sound":
|
||||
let map = call.arguments as? Dictionary<String, String>
|
||||
let data = map?["path"]
|
||||
let url = URL(fileURLWithPath: data!)
|
||||
au.prepare(soundfont: url)
|
||||
print("Valid URL: \(url)")
|
||||
let message = "Prepared Sound Font"
|
||||
result(message)
|
||||
case "unmute":
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
let message = "unmuted Device"
|
||||
result(message)
|
||||
case "play_midi_note":
|
||||
_arguments = call.arguments as! [String : Any];
|
||||
let midi = _arguments["note"] as? Int
|
||||
let velocity = _arguments["velocity"] as? Int
|
||||
au.playPitch(midi: midi ?? 60, velocity: velocity ?? 64)
|
||||
let message = "Playing: \(String(describing: midi!))"
|
||||
result(message)
|
||||
case "stop_midi_note":
|
||||
_arguments = call.arguments as! [String : Any];
|
||||
let midi = _arguments["note"] as? Int
|
||||
let velocity = _arguments["velocity"] as? Int
|
||||
au.stopPitch(midi: midi ?? 60, velocity: velocity ?? 64)
|
||||
let message = "Stopped: \(String(describing: midi!))"
|
||||
result(message)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user