forked from laurentVeliscek/SelectorClock
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSelectorClock.swift
164 lines (127 loc) · 3.58 KB
/
SelectorClock.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//
// SelectorClock.swift
//
// Created by Laurent Veliscek on 17/05/2016.
// Copyright © 2016 Laurent Veliscek. All rights reserved.
//
import Foundation
import AudioKit
/******************************************************************************************
SelectorClock is a wrapper for SelectorMidiInstrument.
You have access to it's sequencer (SelectorClock.sequencer: AKsequencer) so you can add tracks,
and use it like a master sequencer that will record/play midi events.
If you use it as a master sequencer, functions will be triggered in perfect sync.
*******************************************************************************************/
class SelectorClock{
let midi = AKMIDI()
private var seq = AKSequencer()
private var clicker:SelectorMidiInstrument?
private var bpm:Double
private var _isRunning = false
private var _loopLength = 4000
var output:AKNode?
init(tempo: Double = 120, division: Int = 4)
{
bpm = tempo
clicker = SelectorMidiInstrument()
output = clicker
clicker?.enableMIDI(midi.midiClient, name: "clicker midi in")
clicker?.clickPitch = 60
clicker?.clickVolume = 0.1
// We build the track that will act as a metronome
let clickTrack = seq.newTrack()
for i in 0 ..< (division * _loopLength) {
clickTrack?.addNote(60, velocity: 100, position: Double(i ) / Double(division) , duration: Double(1.0 / Double(division)))
}
// Our clickTrack is targeted to our clicker instrument
clickTrack?.setMIDIOutput((clicker?.midiIn)!)
clickTrack?.setLoopInfo(Double(_loopLength), numberOfLoops: 0)
seq.setBPM(bpm)
}
// Play from beginning
func start()
{
seq.rewind()
clicker?.reset()
seq.play()
_isRunning = true
}
// Pause without rewind
func pause()
{
seq.stop()
_isRunning = false
}
// Stop and rewind
func stop()
{
seq.stop()
seq.rewind()
clicker?.reset()
_isRunning = false
}
// Continue without rewind
func play()
{
seq.play()
_isRunning = true
}
// Here you hook any function to be triggered
func addClient(f: Void -> Void){
clicker?.addClient(f)
}
// Default is true
var silent:Bool{
get{
return (clicker?.silent)!
}
set{
clicker?.silent = newValue
}
}
// Sets click volume when .silent is false
var volume:Double{
get{
return (clicker?.clickVolume)!
}
set {
clicker?.clickVolume = newValue
}
}
// Midi Note Pitch of the click (from 20 to 120)
var pitch:Int{
get{
return (clicker?.clickPitch)!
}
set {
clicker?.clickPitch = newValue
}
}
// Return the current Tick number ( = how many times we have triggered...)
var currentTick: Int{
get{
return (clicker?.tickNumber)!
}
}
// Return the current Tick number ( = how many times we have triggered...)
var isRunning: Bool{
get{
return _isRunning
}
}
// You can adjust Tempo in real time
var tempo:Double{
get{
return self.bpm
}
set{
seq.setBPM(newValue)
}
}
// You can access the internal sequence freely to add tracks and events like any AKSequencer
var sequence:AKSequencer{
get{
return seq
}
}
}