/
NoteManager.java
183 lines (154 loc) · 3.85 KB
/
NoteManager.java
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package ddf.minim;
import java.util.ArrayList;
import java.util.HashMap;
import ddf.minim.ugens.Instrument;
/**
*
* @author ddf
* @invisible
*/
public class NoteManager
{
// we use this do our timing, basically
private float sampleRate;
private float tempo;
private float noteOffset;
private float durationFactor;
private int now;
// our events are stored in a map.
// the keys in this map are the "now" that the events should
// occur at and the values are a list of events that occur
// at that time.
private HashMap<Integer, ArrayList<NoteEvent>> events;
// are we paused?
// pausing is important because if we're going to queue up
// a large number of notes, we want to make sure their timestamps
// are accurate. this won't be possible if the note manager
// is sending events because of ticks from the audio output.
private boolean paused;
private interface NoteEvent
{
void send();
}
private class NoteOnEvent implements NoteEvent
{
private Instrument instrument;
private float duration;
public NoteOnEvent(Instrument i, float dur)
{
instrument = i;
duration = dur;
}
public void send()
{
instrument.noteOn(duration);
}
}
private class NoteOffEvent implements NoteEvent
{
private Instrument instrument;
public NoteOffEvent(Instrument i)
{
instrument = i;
}
public void send()
{
instrument.noteOff();
}
}
public NoteManager( float sampleRate )
{
this.sampleRate = sampleRate;
events = new HashMap<Integer, ArrayList<NoteEvent>>();
tempo = 60f;
noteOffset = 0.0f;
durationFactor = 1.0f;
now = 0;
paused = false;
}
// events are always specified as happening some period of time from now.
// but we store them as taking place at a specific time, rather than a relative time.
public synchronized void addEvent(float startTime, float duration, Instrument instrument)
{
int on = now + (int)(sampleRate * ( startTime + noteOffset ) * 60f/tempo);
Integer onAt = new Integer( on );
float actualDuration = duration * durationFactor * 60f/tempo;
if ( events.containsKey(onAt) )
{
ArrayList<NoteEvent> eventsAtOn = events.get(onAt);
eventsAtOn.add( new NoteOnEvent(instrument, actualDuration) );
}
else
{
ArrayList<NoteEvent> eventsAtOn = new ArrayList<NoteEvent>();
eventsAtOn.add( new NoteOnEvent(instrument, actualDuration) );
events.put(onAt, eventsAtOn);
}
Integer offAt = new Integer( on + (int)(sampleRate * actualDuration) );
if ( events.containsKey(offAt) )
{
ArrayList<NoteEvent> eventsAtOff = events.get(offAt);
eventsAtOff.add( new NoteOffEvent(instrument) );
}
else
{
ArrayList<NoteEvent> eventsAtOff = new ArrayList<NoteEvent>();
eventsAtOff.add( new NoteOffEvent(instrument) );
events.put(offAt, eventsAtOff);
}
}
public void setTempo(float tempo)
{
this.tempo = tempo;
}
public float getTempo()
{
return tempo;
}
public void setNoteOffset(float noteOffset)
{
this.noteOffset = noteOffset;
}
public float getNoteOffset()
{
return noteOffset;
}
public void setDurationFactor(float durationFactor)
{
this.durationFactor = durationFactor;
}
public float getDurationFactor()
{
return durationFactor;
}
public void pause()
{
paused = true;
}
public void resume()
{
paused = false;
}
synchronized public void tick()
{
if ( paused == false )
{
// find the events we should trigger now.
Integer Now = new Integer(now);
if ( events.containsKey(Now) )
{
ArrayList<NoteEvent> eventsToSend = events.get(Now);
// ddf: change this to a for loop from an iterator so that
// this list can be safely concurrently modified.
for( int i = 0; i < eventsToSend.size(); ++i )
{
eventsToSend.get(i).send();
}
// remove this list because we've sent all the events
events.remove(Now);
}
// increment our now
++now;
}
}
}