music_player 0.9.0-universal-darwin-9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 Jeremy Voorhis <jvoorhis@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require 'rbconfig'
5
+
6
+ def rb_cmd
7
+ @rb_cmd ||= File.join(Config::CONFIG['bindir'],
8
+ Config::CONFIG['ruby_install_name']
9
+ ).sub(/.*\s.*/m, '"\&"')
10
+ end
11
+
12
+ task :build do
13
+ Dir.chdir('ext') do
14
+ system(rb_cmd, 'music_player/extconf.rb')
15
+ system('make')
16
+ end
17
+ end
18
+
19
+ task :clean do
20
+ Dir['ext/*{Makefile,.o,.bundle}'].each do |path|
21
+ FileUtils.rm_rf(path)
22
+ end
23
+ end
24
+
25
+ Rake::TestTask.new do |t|
26
+ t.libs << 'ext'
27
+ t.test_files = FileList['test/*_test.rb']
28
+ t.verbose = true
29
+ end
30
+
31
+ task :test => :build # Always test the latest build.
32
+
33
+ spec = eval open('music_player.gemspec').read
34
+ Rake::GemPackageTask.new spec do |pkg| end
35
+
36
+ task :default => :test
@@ -0,0 +1,64 @@
1
+ require 'music_player'
2
+
3
+ class DrumMachine
4
+ include AudioToolbox
5
+
6
+ def initialize
7
+ @player = MusicPlayer.new
8
+ @sequence = MusicSequence.new
9
+ @track = @sequence.tracks.new
10
+ @track.add(0.0, MIDIProgramChangeMessage.new(:channel => 10, :program => 26))
11
+ @track.add(0.0, MIDIControlChangeMessage.new(:channel => 10, :number => 32, :value => 1))
12
+ # Use the following call sequence to use an alternate midi destination.
13
+ # Hopefully a more complete interface will be implemented soon. MIDI
14
+ # destinations are referenced by their index beginning at 0.
15
+ # See also CoreMIDI::get_number_of_destinations().
16
+ #
17
+ # @sequence.midi_endpoint = CoreMIDI.get_destination(ARGV.shift.to_i)
18
+ @player.sequence = @sequence
19
+ build_track
20
+ end
21
+
22
+ def drum(beat, note)
23
+ @track.add(beat,
24
+ MIDINoteMessage.new(:note => note,
25
+ :velocity => 80,
26
+ :channel => 10,
27
+ :duration => 0.1))
28
+ end
29
+
30
+ def kick1(beat)
31
+ drum(beat, 35)
32
+ end
33
+
34
+ def kick2(beat)
35
+ drum(beat, 36)
36
+ end
37
+
38
+ def snare(beat)
39
+ drum(beat, 40)
40
+ end
41
+
42
+ def build_track
43
+ 0.upto(15) do |beat|
44
+ kick1(beat) # every downbeat
45
+ kick2(beat+0.5) # every upbeat
46
+ if beat % 4 == 0
47
+ extra = (beat % 8)/4
48
+ snare(beat+extra)
49
+ end
50
+ end
51
+
52
+ @track.length = 16
53
+ @track.loop_info = { :duration => @track.length, :number => 0 }
54
+ end
55
+
56
+ def run
57
+ @player.start
58
+ puts "Press return to exit."
59
+ gets
60
+ @player.stop
61
+ end
62
+ end
63
+
64
+ DrumMachine.new.run
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS << ' -Wall -Werror -O3 '
4
+ $LDFLAGS = '-framework AudioToolbox -framework CoreMIDI'
5
+
6
+ extname = 'music_player'
7
+
8
+ dir_config(extname)
9
+ create_makefile(extname)
@@ -0,0 +1,1440 @@
1
+ /*
2
+ * Copyright (c) 2009 Jeremy Voorhis <jvoorhis@gmail.com>
3
+ */
4
+
5
+ #include <ruby.h>
6
+ #include "util.h"
7
+ #include <AudioToolbox/MusicPlayer.h>
8
+ #include <CoreMIDI/MIDIServices.h>
9
+
10
+ /* Ruby type decls */
11
+
12
+ static VALUE rb_mCoreMIDI;
13
+
14
+ static VALUE rb_mAudioToolbox;
15
+
16
+ static VALUE rb_eTrackNotFound;
17
+ static VALUE rb_eEndOfTrack;
18
+ static VALUE rb_eStartOfTrack;
19
+ static VALUE rb_eNoSequence;
20
+ static VALUE rb_eIllegalTrackDestination;
21
+
22
+ static VALUE rb_cMusicPlayer;
23
+ static VALUE rb_cMusicSequence;
24
+ static VALUE rb_cMusicTrack;
25
+ static VALUE rb_cMusicTrackCollection;
26
+ static VALUE rb_cMIDINoteMessage;
27
+ static VALUE rb_cMIDIChannelMessage;
28
+ static VALUE rb_cMIDIKeyPressureMessage;
29
+ static VALUE rb_cMIDIControlChangeMessage;
30
+ static VALUE rb_cMIDIProgramChangeMessage;
31
+ static VALUE rb_cMIDIChannelPressureMessage;
32
+ static VALUE rb_cMIDIPitchBendMessage;
33
+ static VALUE rb_cExtendedTempoEvent;
34
+ static VALUE rb_cMusicEventIterator;
35
+
36
+ /* Ruby symbols */
37
+ static VALUE rb_sBeat;
38
+ static VALUE rb_sBpm;
39
+ static VALUE rb_sChannel;
40
+ static VALUE rb_sData1;
41
+ static VALUE rb_sData2;
42
+ static VALUE rb_sDuration;
43
+ static VALUE rb_sLength;
44
+ static VALUE rb_sLoopInfo;
45
+ static VALUE rb_sMute;
46
+ static VALUE rb_sNote;
47
+ static VALUE rb_sNumber;
48
+ static VALUE rb_sPressure;
49
+ static VALUE rb_sProgram;
50
+ static VALUE rb_sReleaseVelocity;
51
+ static VALUE rb_sSamp;
52
+ static VALUE rb_sSecs;
53
+ static VALUE rb_sSolo;
54
+ static VALUE rb_sStatus;
55
+ static VALUE rb_sValue;
56
+ static VALUE rb_sVelocity;
57
+
58
+ /* Utils */
59
+
60
+ #define RAISE_OSSTATUS(error,what) \
61
+ if (error == kAudioToolboxErr_TrackIndexError) {\
62
+ rb_raise(rb_eRangeError, "Index is out of range.");\
63
+ } else if (error == kAudioToolboxErr_TrackNotFound) {\
64
+ rb_raise(rb_eTrackNotFound, "Track not found.");\
65
+ } else if (error == kAudioToolboxErr_EndOfTrack) {\
66
+ rb_raise(rb_eEndOfTrack, "Reached end of track.");\
67
+ } else if (error == kAudioToolboxErr_StartOfTrack) {\
68
+ rb_raise(rb_eStartOfTrack, "Reached start of track.");\
69
+ } else if (error == kAudioToolboxErr_IllegalTrackDestination) {\
70
+ rb_raise(rb_eIllegalTrackDestination, "Illegal track destination.");\
71
+ } else if (error == kAudioToolboxErr_NoSequence) {\
72
+ rb_raise(rb_eNoSequence, "No sequence was given.");\
73
+ } else {\
74
+ rb_raise(rb_eRuntimeError, "%s failed with OSStatus %i.", what, (int)error);\
75
+ }
76
+
77
+ /* CoreMIDI defns */
78
+
79
+ static VALUE
80
+ core_midi_get_number_of_destinations (VALUE mod)
81
+ {
82
+ return UINT2NUM(MIDIGetNumberOfDestinations());
83
+ }
84
+
85
+ static VALUE
86
+ core_midi_get_destination (VALUE mod, VALUE idx)
87
+ {
88
+ ItemCount ic = NUM2UINT(idx);
89
+ MIDIEndpointRef ref = MIDIGetDestination(ic);
90
+ if (NULL == ref) { return Qnil; }
91
+ return ULONG2NUM((UInt32) ref);
92
+ }
93
+
94
+ /* MusicPlayer defns */
95
+
96
+ static void
97
+ player_free (MusicPlayer *player)
98
+ {
99
+ OSStatus err;
100
+ if (player) {
101
+ require_noerr( err = DisposeMusicPlayer(*player), fail );
102
+ free(player);
103
+ }
104
+ return;
105
+
106
+ fail:
107
+ rb_warning("DisposeMusicPlayer() failed with OSStatus %i.", (int) err);
108
+ }
109
+
110
+ static VALUE
111
+ player_alloc (VALUE class)
112
+ {
113
+ MusicPlayer *player;
114
+ return Data_Make_Struct(rb_cMusicPlayer, MusicPlayer, 0, player_free, player);
115
+ }
116
+
117
+ static VALUE
118
+ player_init (VALUE self)
119
+ {
120
+ MusicPlayer *player;
121
+ OSStatus err;
122
+ Data_Get_Struct(self, MusicPlayer, player);
123
+ require_noerr( err = NewMusicPlayer(player), fail );
124
+ return self;
125
+
126
+ fail:
127
+ RAISE_OSSTATUS(err, "NewMusicPlayer()");
128
+ }
129
+
130
+ static VALUE
131
+ player_is_playing (VALUE self)
132
+ {
133
+ MusicPlayer *player;
134
+ Boolean playing;
135
+ OSStatus err;
136
+
137
+ Data_Get_Struct(self, MusicPlayer, player);
138
+ require_noerr( err = MusicPlayerIsPlaying(*player, &playing), fail );
139
+ return playing ? Qtrue : Qfalse;
140
+
141
+ fail:
142
+ RAISE_OSSTATUS(err, "MusicPlayerIsPlaying()");
143
+ }
144
+
145
+ static VALUE
146
+ player_get_sequence (VALUE self)
147
+ {
148
+ return rb_iv_get(self, "@sequence");
149
+ }
150
+
151
+ static VALUE
152
+ player_set_sequence (VALUE self, VALUE rb_seq)
153
+ {
154
+ MusicPlayer *player;
155
+ MusicSequence *seq;
156
+ OSStatus err;
157
+
158
+ Data_Get_Struct(self, MusicPlayer, player);
159
+ Data_Get_Struct(rb_seq, MusicSequence, seq);
160
+ rb_iv_set(self, "@sequence", rb_seq);
161
+
162
+ require_noerr( err = MusicPlayerSetSequence(*player, *seq), fail );
163
+ return rb_seq;
164
+
165
+ fail:
166
+ RAISE_OSSTATUS(err, "MusicPlayerSetSequence()");
167
+ }
168
+
169
+ static VALUE
170
+ player_start (VALUE self)
171
+ {
172
+ MusicPlayer *player;
173
+ OSStatus err;
174
+
175
+ Data_Get_Struct(self, MusicPlayer, player);
176
+ require_noerr( err = MusicPlayerStart(*player), fail );
177
+ return Qnil;
178
+
179
+ fail:
180
+ RAISE_OSSTATUS(err, "MusicPlayerStart()");
181
+ }
182
+
183
+ static VALUE
184
+ player_stop (VALUE self)
185
+ {
186
+ MusicPlayer *player;
187
+ OSStatus err;
188
+
189
+ Data_Get_Struct(self, MusicPlayer, player);
190
+ require_noerr( err = MusicPlayerStop(*player), fail );
191
+ return Qnil;
192
+
193
+ fail:
194
+ RAISE_OSSTATUS(err, "MusicPlayerStop()");
195
+ }
196
+
197
+ static VALUE
198
+ player_get_time (VALUE self)
199
+ {
200
+ MusicPlayer *player;
201
+ MusicTimeStamp ts;
202
+ OSStatus err;
203
+
204
+ Data_Get_Struct(self, MusicPlayer, player);
205
+ require_noerr( err = MusicPlayerGetTime(*player, &ts), fail );
206
+ return rb_float_new((Float64) ts);
207
+
208
+ fail:
209
+ RAISE_OSSTATUS(err, "MusicPlayerGetTime()");
210
+ }
211
+
212
+ static VALUE
213
+ player_set_time (VALUE self, VALUE rb_ts)
214
+ {
215
+ if (!PRIM_NUM_P(rb_ts))
216
+ rb_raise(rb_eArgError, "Expected argument to be a number.");
217
+
218
+ MusicPlayer *player;
219
+ MusicTimeStamp ts;
220
+ OSStatus err;
221
+
222
+ ts = rb_num2dbl(rb_ts);
223
+ Data_Get_Struct(self, MusicPlayer, player);
224
+ require_noerr( err = MusicPlayerSetTime(*player, ts), fail );
225
+ return Qnil;
226
+
227
+ fail:
228
+ RAISE_OSSTATUS(err, "MusicPlayerSetTime()");
229
+ }
230
+
231
+ static VALUE
232
+ player_get_play_rate_scalar (VALUE self)
233
+ {
234
+ MusicPlayer *player;
235
+ Float64 scalar;
236
+ OSStatus err;
237
+
238
+ Data_Get_Struct(self, MusicPlayer, player);
239
+ require_noerr( err = MusicPlayerGetPlayRateScalar(*player, &scalar), fail );
240
+ return rb_float_new(scalar);
241
+
242
+ fail:
243
+ RAISE_OSSTATUS(err, "MusicPlayerGetPlayRateScalar()");
244
+ }
245
+
246
+ static VALUE
247
+ player_set_play_rate_scalar (VALUE self, VALUE rb_scalar)
248
+ {
249
+ if (!PRIM_NUM_P(rb_scalar))
250
+ rb_raise(rb_eArgError, "Expected scalar to be a number.");
251
+
252
+ MusicPlayer *player;
253
+ Float64 scalar;
254
+ OSStatus err;
255
+
256
+ scalar = NUM2DBL(rb_scalar);
257
+ Data_Get_Struct(self, MusicPlayer, player);
258
+ require_noerr( err = MusicPlayerSetPlayRateScalar(*player, scalar), fail );
259
+ return Qnil;
260
+
261
+ fail:
262
+ RAISE_OSSTATUS(err, "MusicPlayerSetPlayRateScalar()");
263
+ }
264
+
265
+ static VALUE
266
+ player_host_time_for_beats (VALUE self, VALUE rb_beats)
267
+ {
268
+ MusicTimeStamp beats = NUM2ULONG(rb_beats);
269
+
270
+ MusicPlayer *player;
271
+ Data_Get_Struct(self, MusicPlayer, player);
272
+
273
+ UInt64 host_time = 0;
274
+ OSStatus err;
275
+
276
+ require_noerr( err = MusicPlayerGetHostTimeForBeats(*player, beats, &host_time), fail );
277
+ return rb_float_new(host_time);
278
+
279
+ fail:
280
+ RAISE_OSSTATUS(err, "MusicPlayerGetHostTimeForBeats()");
281
+ }
282
+
283
+ /* Sequence defns */
284
+
285
+ static void
286
+ sequence_free (MusicSequence *seq)
287
+ {
288
+ OSStatus err;
289
+ if (seq) {
290
+ require_noerr( err = DisposeMusicSequence(*seq), fail );
291
+ free(seq);
292
+ }
293
+ return;
294
+
295
+ fail:
296
+ rb_warning("DisposeMusicSequence() failed with %i.", (int) err);
297
+ }
298
+
299
+ static VALUE
300
+ sequence_alloc (VALUE class)
301
+ {
302
+ MusicSequence *seq;
303
+ return Data_Make_Struct(rb_cMusicSequence, MusicSequence, 0, sequence_free, seq);
304
+ }
305
+
306
+ static VALUE
307
+ sequence_init (VALUE self)
308
+ {
309
+ MusicSequence *seq;
310
+ OSStatus err;
311
+ Data_Get_Struct(self, MusicSequence, seq);
312
+ require_noerr( err = NewMusicSequence(seq), fail );
313
+ rb_iv_set(self, "@tracks",
314
+ rb_funcall(rb_cMusicTrackCollection, rb_intern("new"), 1, self));
315
+
316
+ return self;
317
+
318
+ fail:
319
+ RAISE_OSSTATUS(err, "NewMusicSequence()");
320
+ }
321
+
322
+ static VALUE
323
+ sequence_set_midi_endpoint (VALUE self, VALUE rb_endpoint_ref)
324
+ {
325
+ MusicSequence *seq;
326
+ UInt32 ref = NUM2ULONG(rb_funcall(rb_mKernel, rb_intern("Integer"), 1, rb_endpoint_ref));
327
+ OSStatus err;
328
+
329
+ Data_Get_Struct(self, MusicSequence, seq);
330
+ require_noerr( err = MusicSequenceSetMIDIEndpoint(*seq, (MIDIEndpointRef) ref), fail);
331
+ return Qnil;
332
+
333
+ fail:
334
+ RAISE_OSSTATUS(err, "MusicSequenceSetMIDIEndpoint()");
335
+ }
336
+
337
+ static VALUE
338
+ sequence_get_type (VALUE self)
339
+ {
340
+ MusicSequence *seq;
341
+ MusicSequenceType type;
342
+ OSStatus err;
343
+
344
+ Data_Get_Struct(self, MusicSequence, seq);
345
+ require_noerr( err = MusicSequenceGetSequenceType(*seq, &type), fail );
346
+
347
+ switch (type) {
348
+ case kMusicSequenceType_Beats:
349
+ return rb_sBeat;
350
+ case kMusicSequenceType_Seconds:
351
+ return rb_sSecs;
352
+ case kMusicSequenceType_Samples:
353
+ return rb_sSamp;
354
+ default:
355
+ rb_raise(rb_eRuntimeError, "Unrecognized sequence type.");
356
+ }
357
+
358
+ fail:
359
+ RAISE_OSSTATUS(err, "MusicSequenceGetSequenceType()");
360
+ }
361
+
362
+ static VALUE
363
+ sequence_set_type (VALUE self, VALUE rb_type)
364
+ {
365
+ MusicSequence *seq;
366
+ MusicSequenceType type;
367
+ if (rb_type == rb_sBeat)
368
+ type = kMusicSequenceType_Beats;
369
+ else if (rb_type == rb_sSecs)
370
+ type = kMusicSequenceType_Seconds;
371
+ else if (rb_type == rb_sSamp)
372
+ type = kMusicSequenceType_Samples;
373
+ else
374
+ rb_raise(rb_eArgError, "Expected :type to be one of :beat, :secs, :samp.");
375
+
376
+ Data_Get_Struct(self, MusicSequence, seq);
377
+ OSStatus err;
378
+ require_noerr( err = MusicSequenceSetSequenceType(*seq, type), fail );
379
+ return Qnil;
380
+
381
+ fail:
382
+ RAISE_OSSTATUS(err, "MusicSequenceSetSequenceType()");
383
+ }
384
+
385
+ static VALUE
386
+ sequence_save (VALUE self, VALUE rb_path)
387
+ {
388
+ CFURLRef url = PATH2CFURL(rb_funcall(rb_path, rb_intern("to_s"), 0));
389
+ MusicSequence *seq;
390
+ OSStatus err;
391
+
392
+ Data_Get_Struct(self, MusicSequence, seq);
393
+ require_noerr( err = MusicSequenceFileCreate(*seq, url, kMusicSequenceFile_MIDIType, kMusicSequenceFileFlags_EraseFile, 0), fail );
394
+ CFRelease(url);
395
+
396
+ return Qnil;
397
+
398
+ fail:
399
+ CFRelease(url);
400
+ RAISE_OSSTATUS(err, "MusicSequenceFileCreate()");
401
+ }
402
+
403
+ static VALUE
404
+ sequence_load (VALUE self, VALUE rb_path)
405
+ {
406
+ CFURLRef url = PATH2CFURL(StringValue(rb_path));
407
+ MusicSequence *seq;
408
+ OSStatus err;
409
+
410
+ Data_Get_Struct(self, MusicSequence, seq);
411
+ require_noerr( err = MusicSequenceFileLoad(*seq, url, kMusicSequenceFile_MIDIType, kMusicSequenceLoadSMF_ChannelsToTracks), fail );
412
+ CFRelease(url);
413
+
414
+ return Qnil;
415
+
416
+ fail:
417
+ CFRelease(url);
418
+ RAISE_OSSTATUS(err, "MusicSequenceFileLoad()");
419
+ }
420
+
421
+ /* Track defns */
422
+
423
+ static void
424
+ track_free (MusicTrack *track)
425
+ {
426
+ if(track) free(track);
427
+ }
428
+
429
+ static VALUE
430
+ track_init (int argc, VALUE *argv, VALUE self)
431
+ {
432
+ VALUE rb_seq, rb_options;
433
+ rb_scan_args(argc, argv, "11", &rb_seq, &rb_options);
434
+
435
+ rb_iv_set(self, "@sequence", rb_seq);
436
+
437
+ if (T_HASH == TYPE(rb_options)) {
438
+ VALUE loop_info = rb_hash_aref(rb_options, rb_sLoopInfo),
439
+ mute = rb_hash_aref(rb_options, rb_sMute),
440
+ solo = rb_hash_aref(rb_options, rb_sSolo),
441
+ length = rb_hash_aref(rb_options, rb_sLength);
442
+
443
+ if (!NIL_P(loop_info))
444
+ rb_funcall(self, rb_intern("loop_info="), 1, loop_info);
445
+ if (!NIL_P(mute))
446
+ rb_funcall(self, rb_intern("mute="), 1, mute);
447
+ if (!NIL_P(solo))
448
+ rb_funcall(self, rb_intern("solo="), 1, solo);
449
+ if (!NIL_P(length))
450
+ rb_funcall(self, rb_intern("length="), 1, length);
451
+ }
452
+
453
+ return self;
454
+ }
455
+
456
+ static VALUE
457
+ track_internal_new (VALUE rb_seq, MusicTrack *track)
458
+ {
459
+ VALUE rb_track, argv[1];
460
+ rb_track = Data_Wrap_Struct(rb_cMusicTrack, 0, track_free, track);
461
+ argv[0] = rb_seq;
462
+ rb_obj_call_init(rb_track, 1, argv);
463
+ return rb_track;
464
+ }
465
+
466
+ static VALUE
467
+ track_new (int argc, VALUE *argv, VALUE class)
468
+ {
469
+ VALUE rb_seq, rb_options, rb_track, init_argv[2];
470
+ MusicSequence *seq;
471
+ MusicTrack *track;
472
+ OSStatus err;
473
+
474
+ rb_scan_args(argc, argv, "11", &rb_seq, &rb_options);
475
+ Data_Get_Struct(rb_seq, MusicSequence, seq);
476
+
477
+ rb_track = Data_Make_Struct(rb_cMusicTrack, MusicTrack, 0, track_free, track);
478
+ require_noerr( err = MusicSequenceNewTrack(*seq, track), fail );
479
+ init_argv[0] = rb_seq;
480
+ init_argv[1] = rb_options;
481
+ rb_obj_call_init(rb_track, 2, init_argv);
482
+ return rb_track;
483
+
484
+ fail:
485
+ RAISE_OSSTATUS(err, "MusicSequenceNewTrack()");
486
+ }
487
+
488
+ static VALUE
489
+ track_add_midi_note_message (VALUE self, VALUE rb_at, VALUE rb_msg)
490
+ {
491
+ MusicTrack *track;
492
+ MIDINoteMessage *msg;
493
+ MusicTimeStamp ts = (MusicTimeStamp) NUM2DBL(rb_at);
494
+ OSStatus err;
495
+
496
+ Data_Get_Struct(self, MusicTrack, track);
497
+ Data_Get_Struct(rb_msg, MIDINoteMessage, msg);
498
+ require_noerr( err = MusicTrackNewMIDINoteEvent(*track, ts, msg), fail );
499
+ return Qnil;
500
+
501
+ fail:
502
+ RAISE_OSSTATUS(err, "MusicTrackNewMIDINoteEvent()");
503
+ }
504
+
505
+ static VALUE
506
+ track_add_midi_channel_message (VALUE self, VALUE rb_at, VALUE rb_msg)
507
+ {
508
+ MusicTrack *track;
509
+ MIDIChannelMessage *msg;
510
+ MusicTimeStamp ts = (MusicTimeStamp) NUM2DBL(rb_at);
511
+ OSStatus err;
512
+
513
+ Data_Get_Struct(self, MusicTrack, track);
514
+ Data_Get_Struct(rb_msg, MIDIChannelMessage, msg);
515
+ require_noerr( err = MusicTrackNewMIDIChannelEvent(*track, ts, msg), fail );
516
+ return Qnil;
517
+
518
+ fail:
519
+ RAISE_OSSTATUS(err, "MusicTrackNewMIDIChannelEvent()");
520
+ }
521
+
522
+ static VALUE
523
+ track_add_extended_tempo_event (VALUE self, VALUE rb_at, VALUE rb_bpm)
524
+ {
525
+ MusicTrack *track;
526
+ MusicTimeStamp ts;
527
+ Float64 bpm;
528
+ OSStatus err;
529
+
530
+ Data_Get_Struct(self, MusicTrack, track);
531
+
532
+ if (PRIM_NUM_P(rb_at))
533
+ ts = NUM2DBL(rb_at);
534
+ else
535
+ rb_raise(rb_eArgError, "Expected first arg to be a number.");
536
+
537
+ if (PRIM_NUM_P(rb_bpm))
538
+ bpm = NUM2DBL(rb_bpm);
539
+ else
540
+ rb_raise(rb_eArgError, "Expected second arg to be a number.");
541
+
542
+ require_noerr( err = MusicTrackNewExtendedTempoEvent(*track, ts, bpm), fail );
543
+ return Qnil;
544
+
545
+ fail:
546
+ RAISE_OSSTATUS(err, "MusicTrackNewExtendedTempoEvent()");
547
+ }
548
+
549
+ static VALUE
550
+ track_get_loop_info (VALUE self)
551
+ {
552
+ MusicTrack *track;
553
+ UInt32 sz;
554
+ MusicTrackLoopInfo loop_info;
555
+ OSStatus err;
556
+ Data_Get_Struct(self, MusicTrack, track);
557
+ require_noerr( err = MusicTrackGetProperty(*track, kSequenceTrackProperty_LoopInfo, &loop_info, &sz), fail );
558
+
559
+ if (sz == sizeof(MusicTrackLoopInfo)) {
560
+ VALUE rb_loop_info = rb_hash_new();
561
+ rb_hash_aset(rb_loop_info, rb_sDuration, rb_float_new(loop_info.loopDuration));
562
+ rb_hash_aset(rb_loop_info, rb_sNumber, INT2NUM(loop_info.numberOfLoops));
563
+ return rb_loop_info;
564
+ } else {
565
+ return Qnil;
566
+ }
567
+
568
+ fail:
569
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
570
+ }
571
+
572
+ static VALUE
573
+ track_set_loop_info (VALUE self, VALUE rb_loop_info)
574
+ {
575
+ Check_Type(rb_loop_info, T_HASH);
576
+ MusicTrack *track;
577
+ MusicTrackLoopInfo loop_info;
578
+ OSStatus err;
579
+ Data_Get_Struct(self, MusicTrack, track);
580
+ loop_info.loopDuration = NUM2DBL(rb_hash_aref(rb_loop_info, rb_sDuration));
581
+ loop_info.numberOfLoops = NUM2DBL(rb_hash_aref(rb_loop_info, rb_sNumber));
582
+
583
+ require_noerr(
584
+ err = MusicTrackSetProperty(*track, kSequenceTrackProperty_LoopInfo,
585
+ &loop_info, sizeof(MusicTrackLoopInfo)),
586
+ fail);
587
+
588
+ return Qnil;
589
+
590
+ fail:
591
+ RAISE_OSSTATUS(err, "MusicTrackSetProperty()");
592
+ }
593
+
594
+ static VALUE
595
+ track_get_offset (VALUE self)
596
+ {
597
+ MusicTrack *track;
598
+ UInt32 sz;
599
+ MusicTimeStamp offset;
600
+ OSStatus err;
601
+ Data_Get_Struct(self, MusicTrack, track);
602
+ require_noerr( err = MusicTrackGetProperty(*track, kSequenceTrackProperty_OffsetTime, &offset, &sz), fail );
603
+
604
+ if (sz == sizeof(MusicTimeStamp))
605
+ return rb_float_new(offset);
606
+ else
607
+ return Qnil;
608
+
609
+ fail:
610
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
611
+ }
612
+
613
+ static VALUE
614
+ track_set_offset (VALUE self, VALUE rb_offset)
615
+ {
616
+ if (!PRIM_NUM_P(rb_offset))
617
+ rb_raise(rb_eTypeError, "Expected offset to be a number.");
618
+ MusicTrack *track;
619
+ MusicTimeStamp offset = NUM2DBL(rb_offset);
620
+ OSStatus err;
621
+ Data_Get_Struct(self, MusicTrack, track);
622
+
623
+ require_noerr(
624
+ err = MusicTrackSetProperty(*track, kSequenceTrackProperty_OffsetTime,
625
+ &offset, sizeof(MusicTimeStamp)),
626
+ fail);
627
+
628
+ return Qnil;
629
+
630
+ fail:
631
+ RAISE_OSSTATUS(err, "MusicTrackSetProperty()");
632
+ }
633
+
634
+ static VALUE
635
+ track_get_mute (VALUE self)
636
+ {
637
+ MusicTrack *track;
638
+ UInt32 sz;
639
+ Boolean status;
640
+ OSStatus err;
641
+ Data_Get_Struct(self, MusicTrack, track);
642
+ require_noerr( err = MusicTrackGetProperty(*track, kSequenceTrackProperty_MuteStatus, &status, &sz), fail );
643
+
644
+ if (sz == sizeof(Boolean))
645
+ return status ? Qtrue : Qfalse;
646
+ else
647
+ return Qnil;
648
+
649
+ fail:
650
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
651
+ }
652
+
653
+ static VALUE
654
+ track_set_mute (VALUE self, VALUE rb_status)
655
+ {
656
+ MusicTrack *track;
657
+ Boolean status = RTEST(rb_status);
658
+ OSStatus err;
659
+ Data_Get_Struct(self, MusicTrack, track);
660
+
661
+ require_noerr(
662
+ err = MusicTrackSetProperty(*track, kSequenceTrackProperty_MuteStatus,
663
+ &status, sizeof(Boolean)),
664
+ fail);
665
+
666
+ return Qnil;
667
+
668
+ fail:
669
+ RAISE_OSSTATUS(err, "MusicTrackSetProperty()");
670
+ }
671
+
672
+ static VALUE
673
+ track_get_solo (VALUE self)
674
+ {
675
+ MusicTrack *track;
676
+ UInt32 sz;
677
+ Boolean status;
678
+ OSStatus err;
679
+ Data_Get_Struct(self, MusicTrack, track);
680
+ require_noerr(
681
+ err = MusicTrackGetProperty(*track, kSequenceTrackProperty_SoloStatus,
682
+ &status, &sz),
683
+ fail );
684
+
685
+ if (sz == sizeof(Boolean))
686
+ return status ? Qtrue : Qfalse;
687
+ else
688
+ return Qnil;
689
+
690
+ fail:
691
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
692
+ }
693
+
694
+ static VALUE
695
+ track_set_solo (VALUE self, VALUE rb_status)
696
+ {
697
+ MusicTrack *track;
698
+ Boolean status = RTEST(rb_status);
699
+ OSStatus err;
700
+ Data_Get_Struct(self, MusicTrack, track);
701
+
702
+ require_noerr(
703
+ err = MusicTrackSetProperty(*track, kSequenceTrackProperty_SoloStatus,
704
+ &status, sizeof(Boolean)),
705
+ fail);
706
+
707
+ return Qnil;
708
+
709
+ fail:
710
+ RAISE_OSSTATUS(err, "MusicTrackSetProperty()");
711
+ }
712
+
713
+ static VALUE
714
+ track_get_length (VALUE self)
715
+ {
716
+ MusicTrack *track;
717
+ MusicTimeStamp length;
718
+ UInt32 sz;
719
+ OSStatus err;
720
+ Data_Get_Struct(self, MusicTrack, track);
721
+
722
+ require_noerr(
723
+ err = MusicTrackGetProperty(*track, kSequenceTrackProperty_TrackLength,
724
+ &length, &sz),
725
+ fail);
726
+
727
+ if (sz == sizeof(MusicTimeStamp))
728
+ return rb_float_new(length);
729
+ else
730
+ return Qnil;
731
+
732
+ fail:
733
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
734
+ }
735
+
736
+ static VALUE
737
+ track_set_length (VALUE self, VALUE rb_length)
738
+ {
739
+ if (!PRIM_NUM_P(rb_length))
740
+ rb_raise(rb_eTypeError, "Expected length to be a number.");
741
+ MusicTrack *track;
742
+ MusicTimeStamp length = NUM2DBL(rb_length);
743
+ OSStatus err;
744
+ Data_Get_Struct(self, MusicTrack, track);
745
+
746
+ require_noerr(
747
+ err = MusicTrackSetProperty(*track, kSequenceTrackProperty_TrackLength,
748
+ &length, sizeof(MusicTimeStamp)),
749
+ fail);
750
+
751
+ return Qnil;
752
+
753
+ fail:
754
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
755
+ }
756
+
757
+ static VALUE
758
+ track_get_resolution (VALUE self)
759
+ {
760
+ MusicTrack *track;
761
+ SInt16 res;
762
+ UInt32 sz;
763
+ OSStatus err;
764
+ Data_Get_Struct(self, MusicTrack, track);
765
+
766
+ require_noerr(
767
+ err = MusicTrackGetProperty(*track, kSequenceTrackProperty_TimeResolution, &res, &sz),
768
+ fail);
769
+
770
+ if (sz == sizeof(SInt16))
771
+ return INT2FIX(res);
772
+ else
773
+ return Qnil;
774
+
775
+ fail:
776
+ if (paramErr == err)
777
+ rb_raise(rb_eArgError, "Resolution is only available to the tempo track.");
778
+ else
779
+ RAISE_OSSTATUS(err, "MusicTrackGetProperty()");
780
+ }
781
+
782
+ /* TrackCollection defns */
783
+
784
+ static MusicSequence*
785
+ tracks_get_seq (VALUE rb_tracks)
786
+ {
787
+ MusicSequence *seq;
788
+ VALUE rb_seq = rb_iv_get(rb_tracks, "@sequence");
789
+ Data_Get_Struct(rb_seq, MusicSequence, seq);
790
+ return seq;
791
+ }
792
+
793
+ static VALUE
794
+ tracks_size (VALUE self)
795
+ {
796
+ MusicSequence *seq = tracks_get_seq(self);
797
+ UInt32 track_count;
798
+ OSStatus err;
799
+
800
+ require_noerr( err = MusicSequenceGetTrackCount(*seq, &track_count), fail );
801
+ return UINT2NUM(track_count);
802
+
803
+ fail:
804
+ RAISE_OSSTATUS(err, "MusicSequenceGetTrackCount()");
805
+ }
806
+
807
+ static VALUE
808
+ tracks_get_ind_track_internal (VALUE self, VALUE rb_key)
809
+ {
810
+ if (!FIXNUM_P(rb_key)) rb_raise(rb_eArgError, "Expected key to be a Fixnum.");
811
+ MusicSequence *seq = tracks_get_seq(self);
812
+ MusicTrack *track = ALLOC(MusicTrack);
813
+ VALUE rb_seq = rb_iv_get(self, "@sequence");
814
+ OSStatus err;
815
+
816
+ require_noerr( err = MusicSequenceGetIndTrack(*seq, FIX2INT(rb_key), track), fail );
817
+ return track_internal_new(rb_seq, track);
818
+
819
+ fail:
820
+ if (err == kAudioToolboxErr_TrackIndexError) {
821
+ return Qnil;
822
+ } else {
823
+ RAISE_OSSTATUS(err, "MusicSequenceGetIndTrack()");
824
+ }
825
+ }
826
+
827
+ static VALUE
828
+ tracks_index (VALUE self, VALUE rb_track)
829
+ {
830
+ if (rb_cMusicTrack != rb_class_of(rb_track))
831
+ rb_raise(rb_eArgError, "Expected arg to be a MusicTrack.");
832
+
833
+ MusicSequence *seq = tracks_get_seq(self);
834
+ MusicTrack *track;
835
+ UInt32 i;
836
+ OSStatus err;
837
+
838
+ Data_Get_Struct(rb_track, MusicTrack, track);
839
+ require_noerr( err = MusicSequenceGetTrackIndex(*seq, *track, &i), fail );
840
+ return UINT2NUM(i);
841
+
842
+ fail:
843
+ RAISE_OSSTATUS(err, "MusicSequenceGetTrackIndex()");
844
+ }
845
+
846
+ static VALUE
847
+ tracks_tempo_internal (VALUE self)
848
+ {
849
+ MusicSequence *seq = tracks_get_seq(self);
850
+ VALUE rb_seq = rb_iv_get(self, "@sequence");
851
+ MusicTrack *track = ALLOC(MusicTrack);
852
+ OSStatus err;
853
+
854
+ require_noerr( err = MusicSequenceGetTempoTrack(*seq, track), fail );
855
+ return track_internal_new(rb_seq, track);
856
+
857
+ fail:
858
+ RAISE_OSSTATUS(err, "MusicSequenceGetTempoTrack()");
859
+ }
860
+
861
+ static VALUE
862
+ tracks_delete_internal (VALUE self, VALUE rb_track)
863
+ {
864
+ MusicSequence *seq = tracks_get_seq(self);
865
+ MusicTrack *track;
866
+ OSStatus err;
867
+
868
+ Data_Get_Struct(rb_track, MusicTrack, track);
869
+ require_noerr( err = MusicSequenceDisposeTrack(*seq, *track), fail );
870
+ return Qnil;
871
+
872
+ fail:
873
+ RAISE_OSSTATUS(err, "MusicSequenceDisposeTrack()");
874
+ }
875
+
876
+ /* MIDINoteMessage */
877
+
878
+ static void
879
+ midi_note_message_free (MIDINoteMessage *msg)
880
+ {
881
+ if (msg) free(msg);
882
+ }
883
+
884
+ static VALUE
885
+ midi_note_message_alloc (VALUE class)
886
+ {
887
+ MIDINoteMessage *msg;
888
+ return Data_Make_Struct(class, MIDINoteMessage, 0, midi_note_message_free, msg);
889
+ }
890
+
891
+ static VALUE
892
+ midi_note_message_init (VALUE self, VALUE rb_opts)
893
+ {
894
+ Check_Type(rb_opts, T_HASH);
895
+ MIDINoteMessage *msg;
896
+ VALUE rb_chn, rb_note, rb_vel, rb_rel_vel, rb_dur;
897
+
898
+ Data_Get_Struct(self, MIDINoteMessage, msg);
899
+
900
+ rb_chn = rb_hash_aref(rb_opts, rb_sChannel);
901
+ msg->channel = FIXNUM_P(rb_chn) ? FIX2UINT(rb_chn) : 1;
902
+
903
+ rb_note = rb_hash_aref(rb_opts, rb_sNote);
904
+ if (FIXNUM_P(rb_note))
905
+ msg->note = FIX2UINT(rb_note);
906
+ else
907
+ rb_raise(rb_eArgError, ":note is required.");
908
+
909
+ rb_vel = rb_hash_aref(rb_opts, rb_sVelocity);
910
+ msg->velocity = FIXNUM_P(rb_vel) ? FIX2UINT(rb_vel) : 64;
911
+
912
+ rb_rel_vel = rb_hash_aref(rb_opts, rb_sReleaseVelocity);
913
+ msg->releaseVelocity = FIXNUM_P(rb_rel_vel) ? FIX2UINT(rb_rel_vel) : 0;
914
+
915
+ rb_dur = rb_hash_aref(rb_opts, rb_sDuration);
916
+ msg->duration = (MusicTimeStamp) (PRIM_NUM_P(rb_dur)) ? NUM2DBL(rb_dur) : 1.0;
917
+
918
+ return self;
919
+ }
920
+
921
+ static VALUE
922
+ midi_note_message_channel (VALUE self)
923
+ {
924
+ MIDINoteMessage *msg;
925
+ Data_Get_Struct(self, MIDINoteMessage, msg);
926
+ return UINT2NUM(msg->channel);
927
+ }
928
+
929
+ static VALUE
930
+ midi_note_message_note (VALUE self)
931
+ {
932
+ MIDINoteMessage *msg;
933
+ Data_Get_Struct(self, MIDINoteMessage, msg);
934
+ return UINT2NUM(msg->note);
935
+ }
936
+
937
+ static VALUE
938
+ midi_note_message_velocity (VALUE self)
939
+ {
940
+ MIDINoteMessage *msg;
941
+ Data_Get_Struct(self, MIDINoteMessage, msg);
942
+ return UINT2NUM(msg->velocity);
943
+ }
944
+
945
+ static VALUE
946
+ midi_note_message_release_velocity (VALUE self)
947
+ {
948
+ MIDINoteMessage *msg;
949
+ Data_Get_Struct(self, MIDINoteMessage, msg);
950
+ return UINT2NUM(msg->releaseVelocity);
951
+ }
952
+
953
+ static VALUE
954
+ midi_note_message_duration (VALUE self)
955
+ {
956
+ MIDINoteMessage *msg;
957
+ Data_Get_Struct(self, MIDINoteMessage, msg);
958
+ return UINT2NUM(msg->duration);
959
+ }
960
+
961
+ static VALUE
962
+ midi_note_message_from_const (MIDINoteMessage *msg)
963
+ {
964
+ VALUE rb_opts;
965
+ rb_opts = rb_hash_new();
966
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->channel));
967
+ rb_hash_aset(rb_opts, rb_sNote, INT2NUM(msg->note));
968
+ rb_hash_aset(rb_opts, rb_sVelocity, INT2NUM(msg->velocity));
969
+ rb_hash_aset(rb_opts, rb_sReleaseVelocity, INT2NUM(msg->releaseVelocity));
970
+ rb_hash_aset(rb_opts, rb_sDuration, rb_float_new(msg->duration));
971
+ return rb_funcall(rb_cMIDINoteMessage, rb_intern("new"), 1, rb_opts);
972
+ }
973
+
974
+ /* MIDIChannelMessage */
975
+
976
+ static void
977
+ midi_channel_message_free (MIDIChannelMessage *msg)
978
+ {
979
+ if (msg) free(msg);
980
+ }
981
+
982
+ static VALUE
983
+ midi_channel_message_alloc (VALUE class)
984
+ {
985
+ MIDIChannelMessage *msg;
986
+ return Data_Make_Struct(class, MIDIChannelMessage, 0, midi_channel_message_free, msg);
987
+ }
988
+
989
+ static VALUE
990
+ midi_channel_message_init (VALUE self, VALUE rb_opts)
991
+ {
992
+ Check_Type(rb_opts, T_HASH);
993
+ MIDIChannelMessage *msg;
994
+ VALUE rb_status, rb_data1, rb_data2;
995
+
996
+ Data_Get_Struct(self, MIDIChannelMessage, msg);
997
+
998
+ rb_status = rb_hash_aref(rb_opts, rb_sStatus);
999
+ if (!FIXNUM_P(rb_status))
1000
+ rb_raise(rb_eArgError, ":status is required.");
1001
+ else
1002
+ msg->status = NUM2DBL(rb_status);
1003
+
1004
+ rb_data1 = rb_hash_aref(rb_opts, rb_sData1);
1005
+ if (!NIL_P(rb_data1)) msg->data1 = (UInt8) FIX2INT(rb_data1);
1006
+
1007
+ rb_data2 = rb_hash_aref(rb_opts, rb_sData2);
1008
+ if (!NIL_P(rb_data2)) msg->data2 = (UInt8) FIX2INT(rb_data2);
1009
+
1010
+ return self;
1011
+ }
1012
+
1013
+ static VALUE
1014
+ midi_channel_message_status (VALUE self)
1015
+ {
1016
+ MIDIChannelMessage *msg;
1017
+ Data_Get_Struct(self, MIDIChannelMessage, msg);
1018
+ return UINT2NUM(msg->status);
1019
+ }
1020
+
1021
+ static VALUE
1022
+ midi_channel_message_data1 (VALUE self)
1023
+ {
1024
+ MIDIChannelMessage *msg;
1025
+ Data_Get_Struct(self, MIDIChannelMessage, msg);
1026
+ return UINT2NUM(msg->data1);
1027
+ }
1028
+
1029
+ static VALUE
1030
+ midi_channel_message_data2 (VALUE self)
1031
+ {
1032
+ MIDIChannelMessage *msg;
1033
+ Data_Get_Struct(self, MIDIChannelMessage, msg);
1034
+ return UINT2NUM(msg->data2);
1035
+ }
1036
+
1037
+ static VALUE
1038
+ midi_channel_message_from_const (MIDIChannelMessage *msg)
1039
+ {
1040
+ VALUE rb_opts = rb_hash_new();
1041
+ switch(msg->status >> 4) {
1042
+ case 0xA: // key pressure
1043
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->status ^ 0xA0));
1044
+ rb_hash_aset(rb_opts, rb_sNote, INT2NUM(msg->data1));
1045
+ rb_hash_aset(rb_opts, rb_sPressure, INT2NUM(msg->data2));
1046
+ return rb_funcall(rb_cMIDIKeyPressureMessage, rb_intern("new"), 1, rb_opts);
1047
+ case 0xB: // control change
1048
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->status ^ 0xB0));
1049
+ rb_hash_aset(rb_opts, rb_sNumber, INT2NUM(msg->data1));
1050
+ rb_hash_aset(rb_opts, rb_sValue, INT2NUM(msg->data2));
1051
+ return rb_funcall(rb_cMIDIControlChangeMessage, rb_intern("new"), 1, rb_opts);
1052
+ case 0xC: // program change
1053
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->status ^ 0xC0));
1054
+ rb_hash_aset(rb_opts, rb_sProgram, INT2NUM(msg->data1));
1055
+ return rb_funcall(rb_cMIDIProgramChangeMessage, rb_intern("new"), 1, rb_opts);
1056
+ case 0xD: // channel pressure
1057
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->status ^ 0xD0));
1058
+ rb_hash_aset(rb_opts, rb_sPressure, INT2NUM(msg->data1));
1059
+ return rb_funcall(rb_cMIDIChannelPressureMessage, rb_intern("new"), 1, rb_opts);
1060
+ case 0xE: // pitch bend
1061
+ rb_hash_aset(rb_opts, rb_sChannel, INT2NUM(msg->status ^ 0xE0));
1062
+ rb_hash_aset(rb_opts, rb_sValue, INT2NUM(msg->data1));
1063
+ return rb_funcall(rb_cMIDIPitchBendMessage, rb_intern("new"), 1, rb_opts);
1064
+ default:
1065
+ rb_raise(rb_eRuntimeError, "Unrecognized message type.");
1066
+ }
1067
+ }
1068
+
1069
+ /* ExtendedTempoEvent defns */
1070
+ static VALUE
1071
+ tempo_from_const (ExtendedTempoEvent *ev)
1072
+ {
1073
+ VALUE rb_opts = rb_hash_new();
1074
+ rb_hash_aset(rb_opts, rb_sBpm, rb_float_new(ev->bpm));
1075
+ return rb_funcall(rb_cExtendedTempoEvent, rb_intern("new"), 1, rb_opts);
1076
+ }
1077
+
1078
+ /* MusicEventIterator defns */
1079
+ static void
1080
+ iter_free (MusicEventIterator *iter)
1081
+ {
1082
+ OSStatus err;
1083
+ require_noerr( err = DisposeMusicEventIterator(*iter), fail );
1084
+ return;
1085
+
1086
+ fail:
1087
+ rb_warning("DisposeMusicEventIterator() failed with OSStatus %i.", (int) err);
1088
+ }
1089
+
1090
+ static VALUE
1091
+ iter_alloc (VALUE class)
1092
+ {
1093
+ MusicEventIterator *iter;
1094
+ return Data_Make_Struct(rb_cMusicEventIterator, MusicEventIterator, 0, iter_free, iter);
1095
+ }
1096
+
1097
+ static VALUE
1098
+ iter_init (VALUE self, VALUE rb_track)
1099
+ {
1100
+ MusicTrack *track;
1101
+ MusicEventIterator *iter;
1102
+ OSStatus err;
1103
+ Data_Get_Struct(rb_track, MusicTrack, track);
1104
+ Data_Get_Struct(self, MusicEventIterator, iter);
1105
+ require_noerr( err = NewMusicEventIterator(*track, iter), fail );
1106
+ return self;
1107
+
1108
+ fail:
1109
+ RAISE_OSSTATUS(err, "NewMusicEventIterator()");
1110
+ }
1111
+
1112
+ static VALUE
1113
+ iter_seek (VALUE self, VALUE rb_time)
1114
+ {
1115
+ MusicEventIterator *iter;
1116
+ MusicTimeStamp ts;
1117
+ OSStatus err;
1118
+ Data_Get_Struct(self, MusicEventIterator, iter);
1119
+ if (PRIM_NUM_P(rb_time))
1120
+ ts = NUM2DBL(rb_time);
1121
+ else
1122
+ rb_raise(rb_eArgError, "Expected first arg to be a number.");
1123
+ require_noerr( err = MusicEventIteratorSeek(*iter, ts), fail );
1124
+ return Qnil;
1125
+
1126
+ fail:
1127
+ RAISE_OSSTATUS(err, "MusicEventIteratorSeek()");
1128
+ }
1129
+
1130
+ static VALUE
1131
+ iter_next (VALUE self)
1132
+ {
1133
+ MusicEventIterator *iter;
1134
+ OSStatus err;
1135
+ Data_Get_Struct(self, MusicEventIterator, iter);
1136
+ require_noerr( err = MusicEventIteratorNextEvent(*iter), fail );
1137
+ return Qnil;
1138
+
1139
+ fail:
1140
+ RAISE_OSSTATUS(err, "MusicEventIteratorNextEvent()");
1141
+ }
1142
+
1143
+ static VALUE
1144
+ iter_prev (VALUE self)
1145
+ {
1146
+ MusicEventIterator *iter;
1147
+ OSStatus err;
1148
+ Data_Get_Struct(self, MusicEventIterator, iter);
1149
+ require_noerr( err = MusicEventIteratorPreviousEvent(*iter), fail );
1150
+ return Qnil;
1151
+
1152
+ fail:
1153
+ RAISE_OSSTATUS(err, "MusicEventIteratorPreviousEvent()");
1154
+ }
1155
+
1156
+ static VALUE
1157
+ iter_has_current (VALUE self)
1158
+ {
1159
+ MusicEventIterator *iter;
1160
+ Boolean has_cur;
1161
+ OSStatus err;
1162
+ Data_Get_Struct(self, MusicEventIterator, iter);
1163
+ require_noerr( err = MusicEventIteratorHasCurrentEvent(*iter, &has_cur), fail );
1164
+ return has_cur ? Qtrue : Qfalse;
1165
+
1166
+ fail:
1167
+ RAISE_OSSTATUS(err, "MusicEventIteratorHasCurrentEvent()");
1168
+ }
1169
+
1170
+ static VALUE
1171
+ iter_has_prev (VALUE self)
1172
+ {
1173
+ MusicEventIterator *iter;
1174
+ Boolean has_prev;
1175
+ OSStatus err;
1176
+ Data_Get_Struct(self, MusicEventIterator, iter);
1177
+ require_noerr( err = MusicEventIteratorHasPreviousEvent(*iter, &has_prev), fail );
1178
+ return has_prev ? Qtrue : Qfalse;
1179
+
1180
+ fail:
1181
+ RAISE_OSSTATUS(err, "MusicEventIteratorHasPreviousEvent()");
1182
+ }
1183
+
1184
+ static VALUE
1185
+ iter_has_next (VALUE self)
1186
+ {
1187
+ MusicEventIterator *iter;
1188
+ Boolean has_next;
1189
+ OSStatus err;
1190
+ Data_Get_Struct(self, MusicEventIterator, iter);
1191
+ require_noerr( err = MusicEventIteratorHasNextEvent(*iter, &has_next), fail );
1192
+ return has_next ? Qtrue : Qfalse;
1193
+
1194
+ fail:
1195
+ RAISE_OSSTATUS(err, "MusicEventIteratorHasNextEvent()");
1196
+ }
1197
+
1198
+ static VALUE
1199
+ iter_get_time (VALUE self)
1200
+ {
1201
+ MusicEventIterator *iter;
1202
+ MusicTimeStamp ts;
1203
+ OSStatus err;
1204
+ Data_Get_Struct(self, MusicEventIterator, iter);
1205
+ require_noerr( err = MusicEventIteratorGetEventInfo(*iter, &ts, NULL, NULL, NULL), fail );
1206
+ return rb_float_new(ts);
1207
+
1208
+ fail:
1209
+ RAISE_OSSTATUS(err, "MusicEventIteratorGetEventInfo()");
1210
+ }
1211
+
1212
+ static VALUE
1213
+ iter_set_time (VALUE self, VALUE rb_time)
1214
+ {
1215
+ MusicEventIterator *iter;
1216
+ MusicTimeStamp ts = NUM2DBL(rb_time);
1217
+ OSStatus err;
1218
+ Data_Get_Struct(self, MusicEventIterator, iter);
1219
+ require_noerr( err = MusicEventIteratorSetEventTime(*iter, ts), fail );
1220
+ return Qnil;
1221
+
1222
+ fail:
1223
+ RAISE_OSSTATUS(err, "MusicEventIteratorSetEventTime()");
1224
+ }
1225
+
1226
+ static VALUE
1227
+ iter_get_event (VALUE self)
1228
+ {
1229
+ MusicEventIterator *iter;
1230
+ MusicEventType type;
1231
+ const void *data;
1232
+ OSStatus err;
1233
+ Data_Get_Struct(self, MusicEventIterator, iter);
1234
+ require_noerr( err = MusicEventIteratorGetEventInfo(*iter, NULL, &type, &data, NULL), fail );
1235
+
1236
+ switch(type) {
1237
+ case kMusicEventType_NULL:
1238
+ return Qnil;
1239
+ case kMusicEventType_MIDINoteMessage:
1240
+ return midi_note_message_from_const((MIDINoteMessage*) data);
1241
+ case kMusicEventType_MIDIChannelMessage:
1242
+ return midi_channel_message_from_const((MIDIChannelMessage*) data);
1243
+ case kMusicEventType_ExtendedTempo:
1244
+ return tempo_from_const((ExtendedTempoEvent*) data);
1245
+ default:
1246
+ rb_raise(rb_eNotImpError, "Unsupported event type.");
1247
+ break;
1248
+ }
1249
+
1250
+ fail:
1251
+ RAISE_OSSTATUS(err, "MusicEventIteratorGetEventInfo()");
1252
+ }
1253
+
1254
+ static VALUE
1255
+ iter_set_event (VALUE self, VALUE rb_msg)
1256
+ {
1257
+ MusicEventIterator *iter;
1258
+ MusicEventType type;
1259
+ const void *data;
1260
+ OSStatus err;
1261
+
1262
+ Data_Get_Struct(self, MusicEventIterator, iter);
1263
+
1264
+ if (THRQL(rb_cMIDINoteMessage, rb_msg)) {
1265
+ type = kMusicEventType_MIDINoteMessage;
1266
+ Data_Get_Struct(rb_msg, MIDINoteMessage, data);
1267
+ } else if (THRQL(rb_cMIDIChannelMessage, rb_msg)) {
1268
+ type = kMusicEventType_MIDIChannelMessage;
1269
+ Data_Get_Struct(rb_msg, MIDIChannelMessage, data);
1270
+ } else if (THRQL(rb_cExtendedTempoEvent, rb_msg)) {
1271
+ type = kMusicEventType_ExtendedTempo;
1272
+ ExtendedTempoEvent tmp;
1273
+ tmp.bpm = NUM2DBL(rb_funcall(rb_msg, rb_intern("bpm"), 0));
1274
+ data = &tmp;
1275
+ } else {
1276
+ rb_raise(rb_eTypeError, "Unrecognized event type");
1277
+ }
1278
+
1279
+ require_noerr( err = MusicEventIteratorSetEventInfo(*iter, type, data), fail );
1280
+ return Qnil;
1281
+
1282
+ fail:
1283
+ RAISE_OSSTATUS(err, "MusicEventIteratorSetEventInfo()");
1284
+ }
1285
+
1286
+ static VALUE
1287
+ iter_delete_event (VALUE self)
1288
+ {
1289
+ MusicEventIterator *iter;
1290
+ OSStatus err;
1291
+ Data_Get_Struct(self, MusicEventIterator, iter);
1292
+ require_noerr( err = MusicEventIteratorDeleteEvent(*iter), fail );
1293
+ return Qnil;
1294
+
1295
+ fail:
1296
+ RAISE_OSSTATUS(err, "MusicEventIteratorDeleteEvent()");
1297
+ }
1298
+
1299
+ /* Initialize extension */
1300
+
1301
+ void
1302
+ Init_music_player ()
1303
+ {
1304
+ /*
1305
+ * CoreMIDI
1306
+ */
1307
+ rb_mCoreMIDI = rb_define_module("CoreMIDI");
1308
+ rb_define_module_function(rb_mCoreMIDI, "get_number_of_destinations", core_midi_get_number_of_destinations, 0);
1309
+ rb_define_module_function(rb_mCoreMIDI, "get_destination", core_midi_get_destination, 1);
1310
+
1311
+ /*
1312
+ * AudioToolbox
1313
+ */
1314
+ rb_mAudioToolbox = rb_define_module("AudioToolbox");
1315
+
1316
+ /*
1317
+ * AudioToolbox exceptions
1318
+ */
1319
+ rb_eTrackNotFound = rb_define_class_under(rb_mAudioToolbox, "TrackNotFound", rb_eStandardError);
1320
+ rb_eEndOfTrack = rb_define_class_under(rb_mAudioToolbox, "EndOfTrack", rb_eStandardError);
1321
+ rb_eStartOfTrack = rb_define_class_under(rb_mAudioToolbox, "StartOfTrack", rb_eStandardError);
1322
+ rb_eNoSequence = rb_define_class_under(rb_mAudioToolbox, "NoSequence", rb_eStandardError);
1323
+ rb_eIllegalTrackDestination = rb_define_class_under(rb_mAudioToolbox, "IllegalTrackDestination", rb_eStandardError);
1324
+
1325
+ /* AudioToolbox::MusicPlayer */
1326
+ rb_cMusicPlayer = rb_define_class_under(rb_mAudioToolbox, "MusicPlayer", rb_cObject);
1327
+ rb_define_alloc_func(rb_cMusicPlayer, player_alloc);
1328
+ rb_define_method(rb_cMusicPlayer, "initialize", player_init, 0);
1329
+ rb_define_method(rb_cMusicPlayer, "playing?", player_is_playing, 0);
1330
+ rb_define_method(rb_cMusicPlayer, "sequence", player_get_sequence, 0);
1331
+ rb_define_method(rb_cMusicPlayer, "sequence=", player_set_sequence, 1);
1332
+ rb_define_method(rb_cMusicPlayer, "start", player_start, 0);
1333
+ rb_define_method(rb_cMusicPlayer, "stop", player_stop, 0);
1334
+ rb_define_method(rb_cMusicPlayer, "time", player_get_time, 0);
1335
+ rb_define_method(rb_cMusicPlayer, "time=", player_set_time, 1);
1336
+ rb_define_method(rb_cMusicPlayer, "play_rate_scalar", player_get_play_rate_scalar, 0);
1337
+ rb_define_method(rb_cMusicPlayer, "play_rate_scalar=", player_set_play_rate_scalar, 1);
1338
+ rb_define_method(rb_cMusicPlayer, "host_time_for_beats", player_host_time_for_beats, 1);
1339
+
1340
+ /* AudioToolbox::MusicSequence */
1341
+ rb_cMusicSequence = rb_define_class_under(rb_mAudioToolbox, "MusicSequence", rb_cObject);
1342
+ rb_define_alloc_func(rb_cMusicSequence, sequence_alloc);
1343
+ rb_define_method(rb_cMusicSequence, "initialize", sequence_init, 0);
1344
+ rb_define_private_method(rb_cMusicSequence, "load_internal", sequence_load, 1);
1345
+ rb_define_method(rb_cMusicSequence, "midi_endpoint=", sequence_set_midi_endpoint, 1);
1346
+ rb_define_method(rb_cMusicSequence, "type", sequence_get_type, 0);
1347
+ rb_define_method(rb_cMusicSequence, "type=", sequence_set_type, 1);
1348
+ rb_define_method(rb_cMusicSequence, "save", sequence_save, 1);
1349
+
1350
+ /* AudioToolbox::MusicTrack */
1351
+ rb_cMusicTrack = rb_define_class_under(rb_mAudioToolbox, "MusicTrack", rb_cObject);
1352
+ rb_define_singleton_method(rb_cMusicTrack, "new", track_new, -1);
1353
+ rb_define_method(rb_cMusicTrack, "initialize", track_init, -1);
1354
+ rb_define_method(rb_cMusicTrack, "add_midi_note_message", track_add_midi_note_message, 2);
1355
+ rb_define_method(rb_cMusicTrack, "add_midi_channel_message", track_add_midi_channel_message, 2);
1356
+ rb_define_method(rb_cMusicTrack, "add_extended_tempo_event", track_add_extended_tempo_event, 2);
1357
+ rb_define_method(rb_cMusicTrack, "loop_info", track_get_loop_info, 0);
1358
+ rb_define_method(rb_cMusicTrack, "loop_info=", track_set_loop_info, 1);
1359
+ rb_define_method(rb_cMusicTrack, "offset", track_get_offset, 0);
1360
+ rb_define_method(rb_cMusicTrack, "offset=", track_set_offset, 1);
1361
+ rb_define_method(rb_cMusicTrack, "mute", track_get_mute, 0);
1362
+ rb_define_method(rb_cMusicTrack, "mute=", track_set_mute, 1);
1363
+ rb_define_method(rb_cMusicTrack, "solo", track_get_solo, 0);
1364
+ rb_define_method(rb_cMusicTrack, "solo=", track_set_solo, 1);
1365
+ rb_define_method(rb_cMusicTrack, "length", track_get_length, 0);
1366
+ rb_define_method(rb_cMusicTrack, "length=", track_set_length, 1);
1367
+ rb_define_method(rb_cMusicTrack, "resolution", track_get_resolution, 0);
1368
+
1369
+ /* AudioToolbox::MusicSequence#tracks proxy */
1370
+ rb_cMusicTrackCollection = rb_define_class_under(rb_mAudioToolbox, "MusicTrackCollection", rb_cObject);
1371
+ rb_define_method(rb_cMusicTrackCollection, "size", tracks_size, 0);
1372
+ rb_define_method(rb_cMusicTrackCollection, "index", tracks_index, 1);
1373
+ rb_define_private_method(rb_cMusicTrackCollection, "delete_internal", tracks_delete_internal, 1);
1374
+ rb_define_private_method(rb_cMusicTrackCollection, "tempo_internal", tracks_tempo_internal, 0);
1375
+ rb_define_private_method(rb_cMusicTrackCollection, "ind_internal", tracks_get_ind_track_internal, 1);
1376
+
1377
+ /* AudioToolbox::MIDINoteMessage */
1378
+ rb_cMIDINoteMessage = rb_define_class_under(rb_mAudioToolbox, "MIDINoteMessage", rb_cObject);
1379
+ rb_define_alloc_func(rb_cMIDINoteMessage, midi_note_message_alloc);
1380
+ rb_define_method(rb_cMIDINoteMessage, "initialize", midi_note_message_init, 1);
1381
+ rb_define_method(rb_cMIDINoteMessage, "channel", midi_note_message_channel, 0);
1382
+ rb_define_method(rb_cMIDINoteMessage, "note", midi_note_message_note, 0);
1383
+ rb_define_method(rb_cMIDINoteMessage, "velocity", midi_note_message_velocity, 0);
1384
+ rb_define_method(rb_cMIDINoteMessage, "release_velocity", midi_note_message_release_velocity, 0);
1385
+ rb_define_method(rb_cMIDINoteMessage, "duration", midi_note_message_duration, 0);
1386
+
1387
+ /* AudioToolbox::MIDIChannelMessage */
1388
+ rb_cMIDIChannelMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIChannelMessage", rb_cObject);
1389
+ rb_cMIDIKeyPressureMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIKeyPressureMessage", rb_cMIDIChannelMessage);
1390
+ rb_cMIDIControlChangeMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIControlChangeMessage", rb_cMIDIChannelMessage);
1391
+ rb_cMIDIProgramChangeMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIProgramChangeMessage", rb_cMIDIChannelMessage);
1392
+ rb_cMIDIChannelPressureMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIChannelPressureMessage", rb_cMIDIChannelMessage);
1393
+ rb_cMIDIPitchBendMessage = rb_define_class_under(rb_mAudioToolbox, "MIDIPitchBendMessage", rb_cMIDIChannelMessage);
1394
+ rb_define_alloc_func(rb_cMIDIChannelMessage, midi_channel_message_alloc);
1395
+ rb_define_method(rb_cMIDIChannelMessage, "initialize", midi_channel_message_init, 1);
1396
+ rb_define_method(rb_cMIDIChannelMessage, "status", midi_channel_message_status, 0);
1397
+ rb_define_method(rb_cMIDIChannelMessage, "data1", midi_channel_message_data1, 0);
1398
+ rb_define_method(rb_cMIDIChannelMessage, "data2", midi_channel_message_data2, 0);
1399
+
1400
+ /* AudioToolbox::ExtendedTempoEvent */
1401
+ rb_cExtendedTempoEvent = rb_define_class_under(rb_mAudioToolbox, "ExtendedTempoEvent", rb_cObject);
1402
+
1403
+ /* AudioToolbox::MusicEventIterator */
1404
+ rb_cMusicEventIterator = rb_define_class_under(rb_mAudioToolbox, "MusicEventIterator", rb_cObject);
1405
+ rb_define_alloc_func(rb_cMusicEventIterator, iter_alloc);
1406
+ rb_define_method(rb_cMusicEventIterator, "initialize", iter_init, 1);
1407
+ rb_define_method(rb_cMusicEventIterator, "seek", iter_seek, 1);
1408
+ rb_define_method(rb_cMusicEventIterator, "next", iter_next, 0);
1409
+ rb_define_method(rb_cMusicEventIterator, "prev", iter_prev, 0);
1410
+ rb_define_method(rb_cMusicEventIterator, "current?", iter_has_current, 0);
1411
+ rb_define_method(rb_cMusicEventIterator, "next?", iter_has_next, 0);
1412
+ rb_define_method(rb_cMusicEventIterator, "prev?", iter_has_prev, 0);
1413
+ rb_define_method(rb_cMusicEventIterator, "time", iter_get_time, 0);
1414
+ rb_define_method(rb_cMusicEventIterator, "time=", iter_set_time, 1);
1415
+ rb_define_method(rb_cMusicEventIterator, "event", iter_get_event, 0);
1416
+ rb_define_method(rb_cMusicEventIterator, "event=", iter_set_event, 1);
1417
+ rb_define_method(rb_cMusicEventIterator, "delete", iter_delete_event, 0);
1418
+
1419
+ /* Symbols */
1420
+ rb_sBeat = CSTR2SYM("beat");
1421
+ rb_sBpm = CSTR2SYM("bpm");
1422
+ rb_sChannel = CSTR2SYM("channel");
1423
+ rb_sData1 = CSTR2SYM("data1");
1424
+ rb_sData2 = CSTR2SYM("data2");
1425
+ rb_sDuration = CSTR2SYM("duration");
1426
+ rb_sNote = CSTR2SYM("note");
1427
+ rb_sLength = CSTR2SYM("length");
1428
+ rb_sLoopInfo = CSTR2SYM("loop_info");
1429
+ rb_sMute = CSTR2SYM("mute");
1430
+ rb_sNumber = CSTR2SYM("number");
1431
+ rb_sPressure = CSTR2SYM("pressure");
1432
+ rb_sProgram = CSTR2SYM("program");
1433
+ rb_sReleaseVelocity = CSTR2SYM("release_velocity");
1434
+ rb_sSamp = CSTR2SYM("samp");
1435
+ rb_sSecs = CSTR2SYM("secs");
1436
+ rb_sSolo = CSTR2SYM("solo");
1437
+ rb_sStatus = CSTR2SYM("status");
1438
+ rb_sValue = CSTR2SYM("value");
1439
+ rb_sVelocity = CSTR2SYM("velocity");
1440
+ }