Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Ruby_bindings/MDRubyTrack.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 178 - (show annotations) (download)
Mon Feb 24 02:00:55 2020 UTC (4 years, 3 months ago) by toshinagata1964
File size: 29177 byte(s)
Ruby: Sequence and Track objects now work as Enumerable
1 //
2 // MDRubyTrack.m
3 // Alchemusica
4 //
5 // Created by Toshi Nagata on 08/03/27.
6 // Copyright 2008-2017 Toshi Nagata. All rights reserved.
7 //
8 /*
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation version 2 of the License.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 */
18
19 #import "MyDocument.h"
20 #import "MyMIDISequence.h"
21 #import "MDObjects.h"
22 #import "MDRubyPointer.h"
23
24 #include "MDRuby.h"
25
26 // Track class
27 VALUE rb_cMRTrack = Qfalse;
28
29 static void
30 s_MRTrack_Release(void *p)
31 {
32 MyDocumentTrackInfo *ip = (MyDocumentTrackInfo *)p;
33 if (ip->track != NULL) {
34 MDTrackRelease(ip->track);
35 ip->track = NULL;
36 }
37 [MyDocument unregisterDocumentTrackInfo: ip];
38 free(ip);
39 }
40
41 static VALUE
42 s_MRTrack_Alloc(VALUE klass)
43 {
44 MyDocumentTrackInfo *ip = ALLOC(MyDocumentTrackInfo);
45 memset(ip, 0, sizeof(MyDocumentTrackInfo));
46 [MyDocument registerDocumentTrackInfo: ip];
47 return Data_Wrap_Struct(klass, 0, s_MRTrack_Release, ip);
48 }
49
50 /*
51 * call-seq:
52 * Track.new
53 * Track.new(doc, n)
54 *
55 * Returns a new Track object.
56 */
57 static VALUE
58 s_MRTrack_Initialize(int argc, VALUE *argv, VALUE self)
59 {
60 VALUE val1, val2;
61 MyDocumentTrackInfo *ip;
62 Data_Get_Struct(self, MyDocumentTrackInfo, ip);
63 rb_scan_args(argc, argv, "02", &val1, &val2);
64 if (NIL_P(val1)) {
65 ip->track = MDTrackNew();
66 } else if (rb_obj_is_kind_of(val1, rb_cMRTrack)) {
67 MyDocumentTrackInfo *ip2 = TrackInfoFromMRTrackValue(val1);
68 *ip = *ip2;
69 MDTrackRetain(ip->track);
70 } else if (rb_obj_is_kind_of(val1, rb_cMRSequence)) {
71 MyDocument *doc = MyDocumentFromMRSequenceValue(val1);
72 int n = NUM2INT(val2);
73 if (n < 0 || n >= [[doc myMIDISequence] trackCount])
74 rb_raise(rb_eRangeError, "track count (%d) out of range", n);
75 ip->track = [[doc myMIDISequence] getTrackAtIndex: n];
76 MDTrackRetain(ip->track);
77 ip->doc = doc;
78 ip->num = n;
79 } else {
80 rb_raise(rb_eTypeError, "invalid argument; the first argument must be either Sequence or Track");
81 }
82 return Qnil;
83 }
84
85 MDTrack *
86 MDTrackFromMRTrackValue(VALUE val)
87 {
88 MyDocumentTrackInfo *ip;
89 if (rb_obj_is_kind_of(val, rb_cMRTrack)) {
90 Data_Get_Struct(val, MyDocumentTrackInfo, ip);
91 if (ip->track != NULL)
92 return ip->track;
93 }
94 rb_raise(rb_eTypeError, "Cannot get MDTrack pointer from object");
95 }
96
97 MyDocumentTrackInfo *
98 TrackInfoFromMRTrackValue(VALUE val)
99 {
100 MyDocumentTrackInfo *ip;
101 if (rb_obj_is_kind_of(val, rb_cMRTrack)) {
102 Data_Get_Struct(val, MyDocumentTrackInfo, ip);
103 return ip;
104 }
105 rb_raise(rb_eTypeError, "Cannot get track information from object");
106 }
107
108 VALUE
109 MRTrackValueFromTrackInfo(MDTrack *track, void *myDocument, int num)
110 {
111 MyDocumentTrackInfo *ip;
112 MyDocument *doc = (MyDocument *)myDocument;
113 VALUE val = s_MRTrack_Alloc(rb_cMRTrack);
114 Data_Get_Struct(val, MyDocumentTrackInfo, ip);
115 ip->doc = doc;
116 if (num < 0 && doc != nil)
117 ip->num = [[doc myMIDISequence] lookUpTrack: track];
118 else ip->num = num;
119 ip->track = track;
120 MDTrackRetain(ip->track);
121 return val;
122 }
123
124 #pragma mark ====== Ruby methods ======
125
126 /*
127 * call-seq:
128 * track.index
129 *
130 * Index of the track in the parent Sequence. If the track is not
131 * associated with any Sequence, returns nil.
132 */
133 static VALUE
134 s_MRTrack_Index(VALUE self)
135 {
136 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
137 if (ip->doc == nil)
138 return Qnil;
139 else return FIX2INT(ip->num);
140 }
141
142 /*
143 * call-seq:
144 * track.duration
145 *
146 * Get the duration of the track in tick.
147 */
148 static VALUE
149 s_MRTrack_Duration(VALUE self)
150 {
151 MDTrack *track = MDTrackFromMRTrackValue(self);
152 MDTickType duration = MDTrackGetDuration(track);
153 return INT2NUM(duration);
154 }
155
156 /*
157 * call-seq:
158 * track.duration=
159 *
160 * Set the duration of the track in tick.
161 */
162 static VALUE
163 s_MRTrack_SetDuration(VALUE self, VALUE val)
164 {
165 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
166 MDTickType duration = (MDTickType)floor(NUM2DBL(val) + 0.5);
167 MDTickType largestTick = MDTrackGetLargestTick(ip->track);
168 if (duration <= largestTick)
169 rb_raise(rb_eRangeError, "duration value (%d) out of limit, must be >= %d", (int)duration, (int)largestTick + 1);
170 if (ip->doc != nil) {
171 [ip->doc changeTrackDuration: duration ofTrack: ip->num];
172 } else {
173 MDTrackSetDuration(ip->track, duration);
174 }
175 return val;
176 }
177
178 /*
179 * call-seq:
180 * track.count
181 * track.nevents
182 *
183 * Get the number of events in the track.
184 */
185 static VALUE
186 s_MRTrack_Count(VALUE self)
187 {
188 MDTrack *track = MDTrackFromMRTrackValue(self);
189 int count = MDTrackGetNumberOfEvents(track);
190 return INT2NUM(count);
191 }
192
193 /*
194 * call-seq:
195 * track.count_midi
196 * track.nmidievents
197 *
198 * Get the number of MIDI events in the track.
199 */
200 static VALUE
201 s_MRTrack_CountMIDI(VALUE self)
202 {
203 MDTrack *track = MDTrackFromMRTrackValue(self);
204 int count = MDTrackGetNumberOfChannelEvents(track, -1);
205 return INT2NUM(count);
206 }
207
208 /*
209 * call-seq:
210 * track.count_sysex
211 * track.nsysexevents
212 *
213 * Get the number of sysex events in the track.
214 */
215 static VALUE
216 s_MRTrack_CountSysex(VALUE self)
217 {
218 MDTrack *track = MDTrackFromMRTrackValue(self);
219 int count = MDTrackGetNumberOfSysexEvents(track);
220 return INT2NUM(count);
221 }
222
223 /*
224 * call-seq:
225 * track.count_meta
226 * track.nmetaevents
227 *
228 * Get the number of meta events in the track.
229 */
230 static VALUE
231 s_MRTrack_CountMeta(VALUE self)
232 {
233 MDTrack *track = MDTrackFromMRTrackValue(self);
234 int count = MDTrackGetNumberOfNonMIDIEvents(track);
235 return INT2NUM(count);
236 }
237
238 /*
239 * call-seq:
240 * track.pointer(n)
241 * track.event(n)
242 *
243 * Create a new Pointer object pointing to the n-th event in this track.
244 * (n is zero based, but -1 is also allowed)
245 */
246 static VALUE
247 s_MRTrack_Pointer(VALUE self, VALUE nval)
248 {
249 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
250 int pos;
251 if (nval == Qnil)
252 pos = -1;
253 else pos = NUM2INT(rb_Integer(nval));
254 return MRPointerValueFromTrackInfo(ip->track, ip->doc, ip->num, pos);
255 }
256
257 /*
258 * call-seq:
259 * track.channel
260 *
261 * Get the MIDI output channel of the track.
262 */
263 static VALUE
264 s_MRTrack_Channel(VALUE self)
265 {
266 MDTrack *track = MDTrackFromMRTrackValue(self);
267 int ch = MDTrackGetTrackChannel(track);
268 return INT2NUM(ch);
269 }
270
271 /*
272 * call-seq:
273 * track.channel=
274 *
275 * Set the MIDI output channel of the track.
276 */
277 static VALUE
278 s_MRTrack_SetChannel(VALUE self, VALUE val)
279 {
280 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
281 int ch = NUM2INT(val);
282 if (ch <= 0 || ch > 16)
283 rb_raise(rb_eRangeError, "MIDI channel (%d) must be between 1 and 16", ch);
284 if (ip->doc != nil) {
285 [ip->doc changeTrackChannel: ch forTrack: ip->num];
286 } else {
287 MDTrackSetTrackChannel(ip->track, ch);
288 }
289 return val;
290 }
291
292 /*
293 * call-seq:
294 * track.device
295 *
296 * Get the MIDI output device of the track.
297 */
298 static VALUE
299 s_MRTrack_Device(VALUE self)
300 {
301 MDTrack *track = MDTrackFromMRTrackValue(self);
302 char name[256];
303 MDTrackGetDeviceName(track, name, sizeof name);
304 return rb_str_new2(name);
305 }
306
307 /*
308 * call-seq:
309 * track.device=
310 *
311 * Set the MIDI output device of the track.
312 */
313 static VALUE
314 s_MRTrack_SetDevice(VALUE self, VALUE val)
315 {
316 char name[256];
317 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
318 char *p = StringValuePtr(val);
319 strncpy(name, p, 255);
320 name[255] = 0;
321 if (ip->doc != nil) {
322 [ip->doc changeDevice: [NSString stringWithUTF8String: name] forTrack: ip->num];
323 } else {
324 MDTrackSetDeviceName(ip->track, name);
325 }
326 return val;
327 }
328
329 /*
330 * call-seq:
331 * track.name
332 *
333 * Get the name of the track.
334 */
335 static VALUE
336 s_MRTrack_Name(VALUE self)
337 {
338 MDTrack *track = MDTrackFromMRTrackValue(self);
339 char name[256];
340 MDTrackGetName(track, name, sizeof name);
341 return rb_str_new2(name);
342 }
343
344 /*
345 * call-seq:
346 * track.name=
347 *
348 * Set the name of the track.
349 */
350 static VALUE
351 s_MRTrack_SetName(VALUE self, VALUE val)
352 {
353 char name[256];
354 MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
355 char *p = StringValuePtr(val);
356 strncpy(name, p, 255);
357 name[255] = 0;
358 if (ip->doc != nil) {
359 [ip->doc changeTrackName: [NSString stringWithUTF8String: name] forTrack: ip->num];
360 } else {
361 MDTrackSetName(ip->track, name);
362 }
363 return val;
364 }
365
366 /*
367 * call-seq:
368 * track.selection -> MREventSet
369 *
370 * Get the selection of the track.
371 */
372 static VALUE
373 s_MRTrack_Selection(VALUE self)
374 {
375 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
376 if (ip != NULL && ip->doc != nil) {
377 MDSelectionObject *sel = [ip->doc selectionOfTrack: ip->num];
378 if (sel != nil)
379 return MREventSetValueFromIntGroupAndTrackInfo([sel pointSet], ip->track, ip->doc, sel->isEndOfTrackSelected);
380 else return Qnil;
381 }
382 rb_raise(rb_eTypeError, "selection cannot be defined for non-document tracks");
383 return Qnil; /* not reached */
384 }
385
386 /*
387 * call-seq:
388 * track.selection=(pointset)
389 *
390 * Set the selection of the track.
391 */
392 static VALUE
393 s_MRTrack_SetSelection(VALUE self, VALUE sval)
394 {
395 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
396 if (ip != NULL && ip->doc != nil) {
397 MDSelectionObject *sel;
398 if (sval == Qnil)
399 sel = [[MDSelectionObject alloc] init];
400 else
401 sel = [[MDSelectionObject alloc] initWithMDPointSet: IntGroupFromValue(sval)];
402 if (rb_obj_is_kind_of(sval, rb_cMREventSet) && RTEST(MREventSet_EOTSelected(sval)))
403 sel->isEndOfTrackSelected = YES;
404 [ip->doc setSelection: sel inTrack: ip->num sender: nil];
405 [sel autorelease];
406 } else {
407 rb_raise(rb_eTypeError, "selection cannot be defined for non-document tracks");
408 }
409 return sval;
410 }
411
412 /*
413 * call-seq:
414 * track.eventset(*args, &block) -> eventset
415 *
416 * Create a new eventset associated to this track. The arguments are the same
417 * as in MRPointSet.new.
418 */
419 static VALUE
420 s_MRTrack_EventSet(int argc, VALUE *argv, VALUE self)
421 {
422 IntGroup *pset;
423 VALUE val, rval;
424 int32_t max, n;
425
426 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
427 if (ip == NULL || ip->track == NULL)
428 rb_raise(rb_eStandardError, "Internal error: track not defined");
429
430 n = MDTrackGetNumberOfEvents(ip->track);
431
432 /* Create a MREventSet object */
433 /* (without the accompanying block) */
434 if (argc == 0) {
435 /* The default set is (0...nevents) */
436 rval = rb_range_new(INT2NUM(0), INT2NUM(n), 1);
437 argc = 1;
438 argv = &rval;
439 }
440
441 val = rb_funcall2(rb_cMREventSet, rb_intern("new"), argc, argv);
442
443 /* Limit by the number of actual events */
444 pset = IntGroupFromValue(val);
445 max = IntGroupMaximum(pset);
446 if (n <= max)
447 IntGroupRemove(pset, n, max - n + 1);
448
449 if (rb_block_given_p()) {
450 VALUE pval;
451 MRPointerInfo *pvalinfo;
452 IntGroup *ps_to_remove = IntGroupNew();
453 int32_t i;
454 pval = s_MRTrack_Pointer(self, Qnil);
455 Data_Get_Struct(pval, MRPointerInfo, pvalinfo);
456 for (i = 0; (n = IntGroupGetNthPoint(pset, i)) >= 0; i++) {
457 MDPointerSetPosition(pvalinfo->pointer, n);
458 if (!RTEST(rb_yield(pval)))
459 IntGroupAdd(ps_to_remove, n, 1);
460 }
461 IntGroupRemoveIntGroup(pset, ps_to_remove);
462 IntGroupRelease(ps_to_remove);
463 }
464
465 /* Set track */
466 MREventSet_SetTrack(val, self);
467
468 return val;
469 }
470
471 /*
472 * call-seq:
473 * track.all_events -> eventset
474 *
475 * Create a new eventset containing all events in this track.
476 */
477 static VALUE
478 s_MRTrack_AllEvents(VALUE self)
479 {
480 IntGroup *pset;
481 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
482 if (ip == NULL || ip->track == NULL)
483 rb_raise(rb_eStandardError, "Internal error: track not defined");
484 pset = IntGroupNew();
485 IntGroupAdd(pset, 0, MDTrackGetNumberOfEvents(ip->track));
486 return MREventSetValueFromIntGroupAndTrackInfo(pset, ip->track, ip->doc, 1);
487 }
488
489 /*
490 * call-seq:
491 * track.selected?
492 *
493 * Return whether the track is selected (in the track list of the graphic window)
494 */
495 static VALUE
496 s_MRTrack_SelectedP(VALUE self)
497 {
498 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
499 if (ip != NULL && ip->doc != nil)
500 return ([ip->doc isTrackSelected: ip->num] ? Qtrue : Qfalse);
501 else return Qfalse;
502 }
503
504 /*
505 * call-seq:
506 * track.editable?
507 *
508 * Return whether the track is editable
509 */
510 static VALUE
511 s_MRTrack_EditableP(VALUE self)
512 {
513 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
514 if (ip != NULL && ip->doc != nil) {
515 MDTrackAttribute attr = [ip->doc trackAttributeForTrack: ip->num];
516 if (attr & kMDTrackAttributeEditable)
517 return Qtrue;
518 }
519 return Qfalse;
520 }
521
522 /*
523 * call-seq:
524 * track.each block
525 *
526 * Execute the block for each event; the block argument is a Pointer object.
527 */
528 static VALUE
529 s_MRTrack_Each(VALUE self)
530 {
531 VALUE pval = s_MRTrack_Pointer(self, Qnil);
532 while (MRPointer_Next(pval) != Qfalse)
533 rb_yield(pval);
534 return self;
535 }
536
537 /*
538 * call-seq:
539 * track.each_selected block
540 *
541 * Execute the block for each event in selection; the block argument is a Pointer object.
542 */
543 static VALUE
544 s_MRTrack_EachSelected(VALUE self)
545 {
546 VALUE pval = s_MRTrack_Pointer(self, Qnil);
547 while (MRPointer_NextInSelection(pval) != Qfalse)
548 rb_yield(pval);
549 return self;
550 }
551
552 static VALUE
553 s_call_block_with_MRPointer(VALUE yarg, VALUE oarg, int argc, VALUE *argv)
554 {
555 int pos = NUM2INT(rb_Integer(yarg));
556 MDPointer *pt = MDPointerFromMRPointerValue(oarg);
557 MDPointerSetPosition(pt, pos);
558 return rb_yield(oarg);
559 }
560
561 /*
562 * call-seq:
563 * track.each_in(enumerable) block
564 *
565 * Execute the block for each event pointed by the enumerable.
566 * The block argument is a Pointer object.
567 */
568 static VALUE
569 s_MRTrack_EachIn(VALUE self, VALUE enval)
570 {
571 VALUE pval = s_MRTrack_Pointer(self, Qnil);
572 rb_block_call(enval, rb_intern("each"), 0, NULL, s_call_block_with_MRPointer, pval);
573 // MRPointer_Flush(pval);
574 return self;
575 }
576
577 /*
578 * call-seq:
579 * track.reverse_each block
580 *
581 * Execute the block for each event with reverse order; the block argument is a Pointer object.
582 */
583 static VALUE
584 s_MRTrack_ReverseEach(VALUE self)
585 {
586 VALUE pval = s_MRTrack_Pointer(self, Qnil);
587 MRPointer_Bottom(pval);
588 while (MRPointer_Last(pval) != Qfalse)
589 rb_yield(pval);
590 return self;
591 }
592
593 /*
594 * call-seq:
595 * track.reverse_each_selected block
596 *
597 * Execute the block for each event in selection; the block argument is a Pointer object.
598 */
599 static VALUE
600 s_MRTrack_ReverseEachSelected(VALUE self)
601 {
602 VALUE pval = s_MRTrack_Pointer(self, Qnil);
603 MRPointer_Bottom(pval);
604 while (MRPointer_LastInSelection(pval) != Qfalse)
605 rb_yield(pval);
606 return self;
607 }
608
609 /*
610 * call-seq:
611 * track.reverse_each_in(enumerable) block
612 *
613 * Execute the block for each event pointed by the enumerable in reverse order.
614 * The block argument is a Pointer object.
615 */
616 static VALUE
617 s_MRTrack_ReverseEachIn(VALUE self, VALUE enval)
618 {
619 VALUE pval = s_MRTrack_Pointer(self, Qnil);
620 rb_block_call(enval, rb_intern("reverse_each"), 0, NULL, s_call_block_with_MRPointer, pval);
621 // MRPointer_Flush(pval);
622 return self;
623 }
624
625 /*
626 * call-seq:
627 * track.merge(trackarg[, pointset])
628 *
629 * Merge the events in trackarg, at positions in (optionally given) pointset.
630 */
631 static VALUE
632 s_MRTrack_Merge(int argc, VALUE *argv, VALUE self)
633 {
634 VALUE tval, pval, rval;
635 IntGroup *pset, *pset2;
636 MDTrack *tr;
637 int success;
638 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
639 rb_scan_args(argc, argv, "11", &tval, &pval);
640 if (pval != Qnil)
641 pset = IntGroupFromValue(pval);
642 else
643 pset = NULL;
644 tr = MDTrackFromMRTrackValue(tval);
645 if (tr == NULL)
646 rb_raise(rb_eArgError, "track is not given");
647 if (ip->doc != nil) {
648 IntGroupObject *psobj = (pset == NULL ? nil : [[[IntGroupObject alloc] initWithMDPointSet: pset] autorelease]);
649 MDTrackObject *trobj = [[[MDTrackObject alloc] initWithMDTrack: tr] autorelease];
650 success = [ip->doc insertMultipleEvents: trobj at: psobj toTrack: ip->num selectInsertedEvents: NO insertedPositions: &pset2];
651 } else {
652 success = (MDTrackMerge(ip->track, tr, &pset) == 0);
653 pset2 = pset;
654 }
655 if (!success) {
656 /* pset2 is undefined */
657 rb_raise(rb_eStandardError, "cannot merge events");
658 }
659 rval = ValueFromIntGroup(pset2);
660 IntGroupRelease(pset2);
661 return rval;
662 }
663
664 /*
665 * call-seq:
666 * track.copy(pointset) -> new_track
667 *
668 * Copy the events at pointset and create a new track from them.
669 */
670 static VALUE
671 s_MRTrack_Copy(VALUE self, VALUE sval)
672 {
673 IntGroup *pset;
674 MDTrack *track;
675 VALUE rval;
676 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
677 pset = IntGroupFromValue(sval);
678 if (MDTrackExtract(ip->track, &track, pset) != 0)
679 rb_raise(rb_eStandardError, "cannot copy events");
680 rval = MRTrackValueFromTrackInfo(track, nil, -1);
681 MDTrackRelease(track);
682 return rval;
683 }
684
685 /*
686 * call-seq:
687 * track.cut(pointset) -> new_track
688 *
689 * Cut the events at pointset and create a new track from them.
690 */
691 static VALUE
692 s_MRTrack_Cut(VALUE self, VALUE sval)
693 {
694 IntGroup *pset;
695 MDTrack *track;
696 VALUE rval;
697 int success;
698 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
699 pset = IntGroupFromValue(sval);
700 if (ip->doc != nil) {
701 IntGroupObject *psobj = [[[IntGroupObject alloc] initWithMDPointSet: pset] autorelease];
702 success = [ip->doc deleteMultipleEventsAt: psobj fromTrack: ip->num deletedEvents: &track];
703 } else {
704 success = (MDTrackUnmerge(ip->track, &track, pset) != 0);
705 }
706 if (!success)
707 rb_raise(rb_eStandardError, "cannot cut events");
708 rval = MRTrackValueFromTrackInfo(track, nil, -1);
709 MDTrackRelease(track);
710 return rval;
711 }
712
713 static void
714 s_raise_if_missing_parameter(int argc, int expect_argc, const char *msg)
715 {
716 if (argc < expect_argc) {
717 if (expect_argc == 1)
718 rb_raise(rb_eArgError, "parameter (%s) is required", msg);
719 else
720 rb_raise(rb_eArgError, "at least %d parameters (%s) are required", expect_argc, msg);
721 }
722 }
723
724 /*
725 * call-seq:
726 * track.add(tick, type, data,...) -> self
727 * track << [tick, type, data,...] -> self
728 *
729 * Create a new midi event. Type is an integer or a note-number string for note events,
730 * and a symbol for other events.
731 * Data is/are specified for each type of event as follows:
732 * note duration, velocity[, release-velocity]
733 * :tempo tempo (Float)
734 * :time_signature numerator, denominator[, 3rd-byte, 4th-byte] (as defined in SMF)
735 * :key key (-7 to 7), :major/:minor/0/1
736 * :smpte hr, min, sec, frame, frac_frame
737 * :port port_number
738 * :text code, String
739 * :copyright, :sequence, :instrument, :lyric, :marker, :cue, :progname, :devname String
740 * :meta code, String or Array
741 * :sysex String or Array
742 * :program program_no
743 * :control control_no, value (0..127)
744 * :bank_high, :modulation, etc. value (0..127)
745 * :pitch_bend value (-8192..8191)
746 * :channel_pressure value
747 * :key_pressure note_on (Integer) or note_name (String), value
748 */
749 static VALUE
750 s_MRTrack_Add(VALUE self, VALUE sval)
751 {
752 VALUE *argv, val;
753 int argc, kind, code, is_generic;
754 int n1, n2, n3;
755 MDTickType tick;
756 MDEventObject *eobj;
757 MDEvent *ep;
758 unsigned char *ucp;
759 MDSMPTERecord *smp;
760 char *p;
761 const MyDocumentTrackInfo *ip = TrackInfoFromMRTrackValue(self);
762 sval = rb_ary_to_ary(sval);
763 argc = (int)RARRAY_LEN(sval);
764 argv = RARRAY_PTR(sval);
765 if (argc < 2)
766 rb_raise(rb_eArgError, "at least 2 parameters should be present");
767 tick = NUM2INT(rb_Integer(argv[0]));
768 if (tick < 0)
769 rb_raise(rb_eArgError, "the tick value must be non-negative");
770 kind = MREventKindAndCodeFromEventSymbol(argv[1], &code, &is_generic);
771 if (kind < 0) {
772 volatile VALUE v = rb_inspect(argv[1]);
773 rb_raise(rb_eArgError, "unknown event type: %s", StringValuePtr(v));
774 }
775 eobj = [[[MDEventObject alloc] init] autorelease];
776 ep = [eobj eventPtr];
777 MDSetTick(ep, tick);
778 MDSetKind(ep, kind);
779 argc -= 2;
780 argv += 2;
781 switch (kind) {
782 case kMDEventNote:
783 if (is_generic) {
784 s_raise_if_missing_parameter(argc, 3, "note number, duration, velocity");
785 if (FIXNUM_P(argv[0]))
786 code = FIX2INT(argv[0]);
787 else
788 code = MDEventNoteNameToNoteNumber(StringValuePtr(argv[0]));
789 if (code < 0 || code >= 128)
790 rb_raise(rb_eArgError, "note number (%d) out of range", code);
791 argc--;
792 argv++;
793 } else s_raise_if_missing_parameter(argc, 2, "duration, velocity");
794 MDSetCode(ep, code);
795 n1 = NUM2INT(rb_Integer(argv[0]));
796 n2 = NUM2INT(rb_Integer(argv[1]));
797 if (n1 <= 0)
798 rb_raise(rb_eArgError, "note duration (%d) must be positive", n1);
799 if (n2 <= 0 || n2 >= 128)
800 rb_raise(rb_eArgError, "note velocity (%d) is out of range", n2);
801 MDSetDuration(ep, n1);
802 MDSetNoteOnVelocity(ep, n2);
803 if (argc > 2) {
804 n1 = NUM2INT(rb_Integer(argv[2]));
805 if (n1 < 0 || n1 >= 128)
806 rb_raise(rb_eArgError, "release velocity (%d) is out of range", n1);
807 MDSetNoteOffVelocity(ep, n1);
808 }
809 break;
810 case kMDEventTempo:
811 s_raise_if_missing_parameter(argc, 1, "tempo");
812 MDSetTempo(ep, (float)NUM2DBL(rb_Float(argv[0])));
813 break;
814 case kMDEventTimeSignature:
815 s_raise_if_missing_parameter(argc, 2, "n/m");
816 ucp = MDGetMetaDataPtr(ep);
817 ucp[0] = NUM2INT(rb_Integer(argv[0]));
818 ucp[1] = NUM2INT(rb_Integer(argv[1]));
819 if (argc > 2)
820 ucp[2] = NUM2INT(rb_Integer(argv[2]));
821 if (argc > 3)
822 ucp[3] = NUM2INT(rb_Integer(argv[3]));
823 break;
824 case kMDEventKey:
825 s_raise_if_missing_parameter(argc, 2, "number of accidentals, major/minor");
826 ucp = MDGetMetaDataPtr(ep);
827 ucp[0] = NUM2INT(rb_Integer(argv[0]));
828 if (ucp[0] + 7 > 14)
829 rb_raise(rb_eArgError, "number of accidentals (%d) is out of range", (int)(signed char)ucp[0]);
830 if ((n1 = TYPE(argv[1])) == T_STRING || n1 == T_SYMBOL) {
831 p = StringValuePtr(argv[1]);
832 if (strcasecmp(p, "major") == 0)
833 ucp[1] = 0;
834 else if (strcasecmp(p, "minor") == 0)
835 ucp[1] = 1;
836 else
837 rb_raise(rb_eArgError, "unknown word '%s' for key specification", p);
838 } else ucp[1] = (0 != NUM2INT(rb_Integer(argv[1])));
839 break;
840 case kMDEventSMPTE:
841 s_raise_if_missing_parameter(argc, 5, "hour, min, sec, frame, subframe");
842 smp = MDGetSMPTERecordPtr(ep);
843 smp->hour = NUM2INT(rb_Integer(argv[0]));
844 smp->min = NUM2INT(rb_Integer(argv[1]));
845 smp->sec = NUM2INT(rb_Integer(argv[2]));
846 smp->frame = NUM2INT(rb_Integer(argv[3]));
847 smp->subframe = NUM2INT(rb_Integer(argv[4]));
848 break;
849 case kMDEventPortNumber:
850 s_raise_if_missing_parameter(argc, 1, "port number");
851 MDSetData1(ep, NUM2INT(rb_Integer(argv[0])));
852 break;
853 case kMDEventMetaText:
854 if (is_generic) {
855 s_raise_if_missing_parameter(argc, 2, "text kind number (0-15), string");
856 code = NUM2INT(rb_Integer(argv[0]));
857 argc--;
858 argv++;
859 } else s_raise_if_missing_parameter(argc, 1, "string");
860 if (code < 0 || code > 15)
861 rb_raise(rb_eArgError, "text kind number (%d) is out of range", code);
862 MDSetCode(ep, code);
863 StringValue(argv[0]);
864 MDSetMessageLength(ep, (int)RSTRING_LEN(argv[0]));
865 MDSetMessage(ep, (unsigned char *)(RSTRING_PTR(argv[0])));
866 break;
867 case kMDEventMetaMessage:
868 case kMDEventSysex:
869 case kMDEventSysexCont:
870 if (kind == kMDEventMetaMessage) {
871 s_raise_if_missing_parameter(argc, 2, "code, string or array of integers");
872 code = NUM2INT(rb_Integer(argv[0]));
873 if (code < 16 || code > 127)
874 rb_raise(rb_eArgError, "meta code (%d) is out of range", code);
875 MDSetCode(ep, code);
876 argc--;
877 argv++;
878 } else {
879 s_raise_if_missing_parameter(argc, 1, "string or array of integers");
880 }
881 if ((n1 = TYPE(argv[0])) == T_STRING) {
882 n2 = (int)RSTRING_LEN(argv[0]);
883 ucp = (unsigned char *)(RSTRING_PTR(argv[0]));
884 } else {
885 val = rb_ary_to_ary(argv[0]);
886 n2 = (int)RARRAY_LEN(val);
887 ucp = (unsigned char *)malloc(n2);
888 for (n3 = 0; n3 < n2; n3++)
889 ucp[n3] = NUM2INT(rb_Integer(RARRAY_PTR(val)[n3]));
890 }
891 if (n2 > 0) {
892 if (kind == kMDEventSysex || kind == kMDEventSysexCont) {
893 if (kind == kMDEventSysex) {
894 if (ucp[0] != 0xf0)
895 rb_raise(rb_eArgError, "sysex must start with 0xf0");
896 n3 = 1;
897 } else n3 = 0;
898 for ( ; n3 < n2; n3++) {
899 if (ucp[n3] >= 128 && (n3 != n2 - 1 || ucp[n3] != 0xf7))
900 rb_raise(rb_eArgError, "sysex must not contain numbers >= 128");
901 }
902 }
903 }
904 MDSetMessageLength(ep, n2);
905 MDSetMessage(ep, ucp);
906 if (n1 != T_STRING)
907 free(ucp);
908 break;
909 case kMDEventProgram:
910 s_raise_if_missing_parameter(argc, 1, "program number");
911 n1 = NUM2INT(rb_Integer(argv[0]));
912 if (n1 < 0 || n1 >= 128)
913 rb_raise(rb_eArgError, "program number (%d) is out of range", n1);
914 MDSetData1(ep, n1);
915 break;
916 case kMDEventControl:
917 if (is_generic) {
918 s_raise_if_missing_parameter(argc, 2, "control number, value");
919 code = NUM2INT(rb_Integer(argv[0]));
920 argc--;
921 argv++;
922 } else s_raise_if_missing_parameter(argc, 1, "control value");
923 if (code < 0 || code >= 128)
924 rb_raise(rb_eArgError, "control code (%d) is out of range", code);
925 n1 = NUM2INT(rb_Integer(argv[0]));
926 if (n1 < 0 || n1 >= 128)
927 rb_raise(rb_eArgError, "control value (%d) is out of range", n1);
928 MDSetCode(ep, code);
929 MDSetData1(ep, n1);
930 break;
931 case kMDEventPitchBend:
932 s_raise_if_missing_parameter(argc, 1, "pitch bend");
933 n1 = NUM2INT(rb_Integer(argv[0]));
934 if (n1 < -8192 || n1 >= 8192)
935 rb_raise(rb_eArgError, "pitch bend value (%d) is out of range", n1);
936 MDSetData1(ep, n1);
937 break;
938 case kMDEventChanPres:
939 s_raise_if_missing_parameter(argc, 1, "channel pressure");
940 n1 = NUM2INT(rb_Integer(argv[0]));
941 if (n1 < 0 || n1 >= 128)
942 rb_raise(rb_eArgError, "channel pressure value (%d) is out of range", n1);
943 MDSetData1(ep, n1);
944 break;
945 case kMDEventKeyPres:
946 s_raise_if_missing_parameter(argc, 2, "note number, key pressure");
947 if (FIXNUM_P(argv[0]))
948 code = FIX2INT(argv[0]);
949 else
950 code = MDEventNoteNameToNoteNumber(StringValuePtr(argv[0]));
951 if (code < 0 || code >= 128)
952 rb_raise(rb_eArgError, "note number (%d) out of range", code);
953 n1 = NUM2INT(rb_Integer(argv[1]));
954 if (n1 < 0 || n1 >= 128)
955 rb_raise(rb_eArgError, "key pressure value (%d) is out of range", n1);
956 MDSetCode(ep, code);
957 MDSetData1(ep, n1);
958 break;
959 default:
960 rb_raise(rb_eArgError, "internal error? unknown event kind (%d)", kind);
961 }
962
963 /* Insert the new event to the track */
964 if (ip->doc != nil) {
965 [ip->doc insertEvent: eobj toTrack: ip->num];
966 } else {
967 MDPointer *pt = MDPointerNew(ip->track);
968 MDPointerInsertAnEvent(pt, ep);
969 MDPointerRelease(pt);
970 }
971
972 return self;
973 }
974
975 #pragma mark ====== Initialize class ======
976
977 void
978 MRTrackInitClass(void)
979 {
980 if (rb_cMRTrack != Qfalse)
981 return;
982
983 rb_cMRTrack = rb_define_class("Track", rb_cObject);
984 rb_include_module(rb_cMRTrack, rb_mEnumerable);
985 rb_define_alloc_func(rb_cMRTrack, s_MRTrack_Alloc);
986 rb_define_private_method(rb_cMRTrack, "initialize", s_MRTrack_Initialize, -1);
987 rb_define_method(rb_cMRTrack, "index", s_MRTrack_Index, 0);
988 rb_define_method(rb_cMRTrack, "duration", s_MRTrack_Duration, 0);
989 rb_define_method(rb_cMRTrack, "duration=", s_MRTrack_SetDuration, 1);
990 rb_define_method(rb_cMRTrack, "count", s_MRTrack_Count, 0);
991 rb_define_method(rb_cMRTrack, "nevents", s_MRTrack_Count, 0);
992 rb_define_method(rb_cMRTrack, "count_midi", s_MRTrack_CountMIDI, 0);
993 rb_define_method(rb_cMRTrack, "nmidievents", s_MRTrack_CountMIDI, 0);
994 rb_define_method(rb_cMRTrack, "count_sysex", s_MRTrack_CountSysex, 0);
995 rb_define_method(rb_cMRTrack, "nsysexevents", s_MRTrack_CountSysex, 0);
996 rb_define_method(rb_cMRTrack, "count_meta", s_MRTrack_CountMeta, 0);
997 rb_define_method(rb_cMRTrack, "nmetaevents", s_MRTrack_CountMeta, 0);
998 rb_define_method(rb_cMRTrack, "pointer", s_MRTrack_Pointer, 1);
999 rb_define_method(rb_cMRTrack, "event", s_MRTrack_Pointer, 1);
1000 rb_define_method(rb_cMRTrack, "channel", s_MRTrack_Channel, 0);
1001 rb_define_method(rb_cMRTrack, "channel=", s_MRTrack_SetChannel, 1);
1002 rb_define_method(rb_cMRTrack, "device", s_MRTrack_Device, 0);
1003 rb_define_method(rb_cMRTrack, "device=", s_MRTrack_SetDevice, 1);
1004 rb_define_method(rb_cMRTrack, "name", s_MRTrack_Name, 0);
1005 rb_define_method(rb_cMRTrack, "name=", s_MRTrack_SetName, 1);
1006 rb_define_method(rb_cMRTrack, "selection", s_MRTrack_Selection, 0);
1007 rb_define_method(rb_cMRTrack, "selection=", s_MRTrack_SetSelection, 1);
1008 rb_define_method(rb_cMRTrack, "eventset", s_MRTrack_EventSet, -1);
1009 rb_define_method(rb_cMRTrack, "all_events", s_MRTrack_AllEvents, 0);
1010 rb_define_method(rb_cMRTrack, "selected?", s_MRTrack_SelectedP, 0);
1011 rb_define_method(rb_cMRTrack, "editable?", s_MRTrack_EditableP, 0);
1012 rb_define_method(rb_cMRTrack, "each", s_MRTrack_Each, 0);
1013 rb_define_method(rb_cMRTrack, "each_selected", s_MRTrack_EachSelected, 0);
1014 rb_define_method(rb_cMRTrack, "each_in", s_MRTrack_EachIn, 1);
1015 rb_define_method(rb_cMRTrack, "reverse_each", s_MRTrack_ReverseEach, 0);
1016 rb_define_method(rb_cMRTrack, "reverse_each_selected", s_MRTrack_ReverseEachSelected, 0);
1017 rb_define_method(rb_cMRTrack, "reverse_each_in", s_MRTrack_ReverseEachIn, 1);
1018 rb_define_method(rb_cMRTrack, "merge", s_MRTrack_Merge, -1);
1019 rb_define_method(rb_cMRTrack, "copy", s_MRTrack_Copy, 1);
1020 rb_define_method(rb_cMRTrack, "cut", s_MRTrack_Cut, 1);
1021 rb_define_method(rb_cMRTrack, "add", s_MRTrack_Add, -2);
1022 rb_define_method(rb_cMRTrack, "<<", s_MRTrack_Add, 1);
1023 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26