music_player 0.9.0-universal-darwin-9

Sign up to get free protection for your applications and to get access to all the features.
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
+ }