Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/PlayingViewController.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 170 - (show annotations) (download)
Sun Oct 27 13:34:53 2019 UTC (4 years, 7 months ago) by toshinagata1964
File size: 25880 byte(s)
Count-off for MIDI recording is improved
1 //
2 // PlayingViewCotroller.m
3 //
4 // Created by Toshi Nagata.
5 /*
6 Copyright (c) 2000-2016 Toshi Nagata. All rights reserved.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 */
18
19 #import "PlayingViewController.h"
20 #import "MyDocument.h"
21 #import "MyMIDISequence.h"
22 #import "RecordPanelController.h"
23 #import "GraphicWindowController.h"
24 #import "AudioSettingsPanelController.h"
25
26 @implementation PlayingViewController
27
28 - (id)init
29 {
30 self = [super init];
31 if (self != nil) {
32 // status = kMDPlayer_idle;
33 // docArray = [[NSMutableArray allocWithZone: [self zone]] initWithCapacity: 16];
34 tickArray = [[NSMutableArray allocWithZone: [self zone]] initWithCapacity: 16];
35 calibrator = NULL;
36 totalTime = currentTime = 0;
37 timer = nil;
38 // resumeTimer = nil;
39 // shouldContinuePlay = NO;
40 isRecording = NO;
41 // MDPlayerInitMIDIDevices();
42 // [[self window] makeKeyAndOrderFront: self];
43 }
44 return self;
45 }
46
47 - (void)dealloc
48 {
49 if (timer != nil) {
50 [timer invalidate];
51 [timer release];
52 timer = nil;
53 }
54 if (calibrator != NULL)
55 MDCalibratorRelease(calibrator);
56 // [timer autorelease];
57 [tickArray release];
58 // [docArray release];
59 [[NSNotificationCenter defaultCenter]
60 removeObserver:self];
61 [super dealloc];
62 }
63
64 - (void)windowDidLoad
65 {
66 NSFont *font;
67 MDTrack *track;
68 MDSequence *sequence;
69 MDStatus sts;
70
71 if (parentController == nil) {
72 NSLog(@"Internal error: parentController in PlayingViewController is not connected to any NSWindowController. Examine the nib file.");
73 return;
74 }
75
76 myDocument = (MyDocument *)[parentController document];
77
78 /* Initialize the calibrator */
79 sequence = [[myDocument myMIDISequence] mySequence];
80 track = MDSequenceGetTrack(sequence, 0); /* the conductor track */
81 calibrator = MDCalibratorNew(sequence, track, kMDEventTempo, -1); /* create a new calibrator */
82 if (calibrator == NULL) {
83 NSLog(@"Internal error: cannot allocate calibrator for PlayingViewController");
84 return;
85 }
86 sts = MDCalibratorAppend(calibrator, track, kMDEventTimeSignature, -1);
87 if (sts == kMDNoError)
88 sts = MDCalibratorAppend(calibrator, track, kMDEventMetaText, kMDMetaMarker);
89
90 [markerPopup removeAllItems];
91 // [tunePopup removeAllItems];
92 [markerPopup setEnabled: NO];
93 // [tunePopup setEnabled: NO];
94 font = [NSFont userFixedPitchFontOfSize: 10.0f];
95 [timeField setFont: font];
96 [countField setFont: font];
97 [[NSNotificationCenter defaultCenter]
98 addObserver: self
99 selector: @selector(trackModified:)
100 name: MyDocumentTrackModifiedNotification
101 object: myDocument];
102 [[NSNotificationCenter defaultCenter]
103 addObserver: self
104 selector: @selector(trackInserted:)
105 name: MyDocumentTrackInsertedNotification
106 object: myDocument];
107 [[NSNotificationCenter defaultCenter]
108 addObserver: self
109 selector: @selector(trackDeleted:)
110 name: MyDocumentTrackDeletedNotification
111 object: myDocument];
112 [self updateMarkerList];
113 [self refreshTimeDisplay];
114 }
115
116 - (void)refreshTimeDisplay
117 {
118 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
119 MDTickType tick;
120 MDTimeType time;
121 double d, slider;
122 int32_t bar, beat, count, marker, ntime;
123 int hour, min, sec, status;
124 NSString *countString, *timeString;
125 BOOL playingOrRecording = NO;
126
127 if (player == NULL) {
128 currentTime = 0;
129 countString = @"----:--:----";
130 timeString = @"--:--:--";
131 marker = -1;
132 slider = 0.0;
133 // status = kMDPlayer_idle;
134 [recordButton setEnabled: NO];
135 [stopButton setEnabled: NO];
136 [playButton setEnabled: NO];
137 [pauseButton setEnabled: NO];
138 [ffButton setEnabled: NO];
139 [rewindButton setEnabled: NO];
140 } else {
141 status = MDPlayerGetStatus(player);
142 [stopButton setEnabled: YES];
143 [playButton setEnabled: YES];
144 [pauseButton setEnabled: YES];
145 [ffButton setEnabled: YES];
146 [rewindButton setEnabled: YES];
147 if (isRecording)
148 [recordButton setState:NSOnState];
149 else
150 [recordButton setState:NSOffState];
151 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
152 playingOrRecording = YES;
153 [playButton setState: NSOnState];
154 } else [playButton setState: NSOffState];
155
156 [pauseButton setState:(status == kMDPlayer_suspended ? NSOnState : NSOffState)];
157 /* Display tick and time */
158 if (currentTime == 0) {
159 time = totalTime;
160 countString = @"----:--:----";
161 marker = -1;
162 slider = 0.0;
163 } else {
164 time = currentTime;
165 tick = MDCalibratorTimeToTick(calibrator, time);
166 MDCalibratorTickToMeasure(calibrator, tick, &bar, &beat, &count);
167 countString = [NSString stringWithFormat: @"%4d:%2d:%4d", bar, beat, count];
168 if (totalTime > 0) {
169 slider = (double)time / totalTime * 100.0;
170 } else slider = 0.0;
171 marker = (int)[tickArray count];
172 if (marker >= 1) {
173 d = (double)tick;
174 while (--marker >= 0) {
175 if ([[tickArray objectAtIndex: marker] doubleValue] <= d)
176 break;
177 }
178 if (marker < 0)
179 marker = 0;
180 } else marker = -1;
181 }
182 ntime = (int32_t)(time / 1000000);
183 hour = ntime / 3600;
184 min = (ntime / 60) % 60;
185 sec = ntime % 60;
186 timeString = [NSString stringWithFormat: @"%02d:%02d:%02d", hour, min, sec];
187 [myDocument postPlayPositionNotification: (playingOrRecording ? MDCalibratorTimeToTick(calibrator, currentTime) : -1.0f)];
188 }
189 [timeField setStringValue: timeString];
190 [countField setStringValue: countString];
191 [positionSlider setDoubleValue: slider];
192 if (marker >= 0)
193 [markerPopup selectItemAtIndex: marker];
194 }
195
196 /*
197 - (void)resumeTimerCallback: (NSTimer *)timer
198 {
199 MDPlayer *player;
200 if (activeIndex >= 0) {
201 player = [[[docArray objectAtIndex: activeIndex] myMIDISequence] myPlayer];
202 MDPlayerPreroll(player, MDCalibratorTimeToTick(calibrator, currentTime));
203 status = kMDPlayer_suspended;
204 if (shouldContinuePlay)
205 [self pressPlayButton: self];
206 else {
207 [playButton setState: NSOffState];
208 [pauseButton setState: NSOnState];
209 }
210 }
211 [resumeTimer invalidate];
212 [resumeTimer release];
213 resumeTimer = nil;
214 [timeField setBackgroundColor: [NSColor whiteColor]];
215 shouldContinuePlay = NO;
216 }
217
218 - (void)setResumeTimer
219 {
220 if (resumeTimer == nil)
221 [timeField setBackgroundColor: [NSColor lightGrayColor]];
222 else
223 [resumeTimer invalidate];
224
225 // For better user experience, the play button is left pressed during
226 // FF/Rew/Slider action
227 if (shouldContinuePlay)
228 [playButton setState: NSOnState];
229
230 resumeTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector:@selector(resumeTimerCallback:) userInfo:nil repeats:NO] retain];
231 }
232 */
233
234 - (void)updateMarkerList
235 {
236 MDPointer *pos;
237 MDTrack *track;
238 MDSequence *sequence;
239
240 [markerPopup removeAllItems];
241 [markerPopup setEnabled: NO];
242 [tickArray removeAllObjects];
243 // totalTime = currentTime = 0;
244
245 sequence = [[myDocument myMIDISequence] mySequence];
246 if (sequence == NULL)
247 return;
248
249 /* Total playing time */
250 totalTime = MDCalibratorTickToTime(calibrator, MDSequenceGetDuration(sequence));
251
252 /* The marker list */
253 track = MDSequenceGetTrack(sequence, 0);
254 pos = MDPointerNew(track);
255 if (pos != NULL) {
256 /* Search all the markers in the conductor track */
257 int n;
258 MDEvent *ep;
259 MDTickType tick;
260 NSString *name;
261 int32_t length;
262 n = 0;
263 while ((ep = MDPointerForward(pos)) != NULL) {
264 if (MDIsTextMetaEvent(ep) && MDGetCode(ep) == kMDMetaMarker) {
265 tick = MDGetTick(ep);
266 name = [NSString stringWithFormat: @"%09d: %s", n++, MDGetMessageConstPtr(ep, &length)]; /* Prefix the name with a serial number to ensure uniqueness of the titles */
267 [tickArray addObject: [NSNumber numberWithDouble: (double)tick]];
268 [markerPopup addItemWithTitle: name];
269 }
270 }
271 while (--n >= 0) {
272 /* Remove the serial numbers from the menu titles, and set tag */
273 NSMenuItem *item = [(NSPopUpButton *)markerPopup itemAtIndex: n];
274 if (item) {
275 [item setTitle: [[item title] substringFromIndex: 11]];
276 [item setTag: n];
277 }
278 }
279 [markerPopup setEnabled: ([markerPopup numberOfItems] > 0)];
280 MDPointerRelease(pos);
281 }
282 }
283
284 /*
285 - (void)selectTuneAtIndex:(int)index
286 {
287 MDSequence *sequence;
288 MDTrack *track;
289 MDStatus sts = kMDNoError;
290 // MDPointer *pos;
291 // MDEvent *ep;
292 // MDTickType tick;
293 // int32_t length;
294 // NSString *name;
295 // int n;
296
297 if (index < 0 || index >= [tunePopup numberOfItems]) {
298 activeIndex = -1;
299 status = kMDPlayer_idle;
300 sequence = NULL;
301 } else {
302 [tunePopup selectItemAtIndex: index];
303 if (index == activeIndex)
304 return;
305 if (status == kMDPlayer_playing || status == kMDPlayer_suspended)
306 [self pressStopButton: self];
307 status = kMDPlayer_ready;
308 activeIndex = index;
309 sequence = [[[docArray objectAtIndex: index] myMIDISequence] mySequence];
310 }
311 isRecording = NO;
312
313 // Remove old information
314 if (calibrator != NULL) {
315 MDCalibratorRelease(calibrator);
316 calibrator = NULL;
317 }
318
319 if (sequence != NULL) {
320 track = MDSequenceGetTrack(sequence, 0); // the conductor track
321 calibrator = MDCalibratorNew(sequence, track, kMDEventTempo, -1); // create a new calibrator
322 if (calibrator != NULL)
323 sts = MDCalibratorAppend(calibrator, track, kMDEventTimeSignature, -1);
324 if (sts == kMDNoError)
325 sts = MDCalibratorAppend(calibrator, track, kMDEventMetaText, kMDMetaMarker);
326 }
327
328 [self updateMarkerList];
329 [self refreshTimeDisplay];
330 }
331 */
332
333 /*
334 - (int)refreshMIDIDocument: (MyDocument *)document
335 {
336 unsigned int n;
337
338 n = [docArray indexOfObject: document];
339 if (n == NSNotFound) {
340 [docArray addObject: document];
341 n = [docArray count] - 1;
342 // Create a menu item with a dummy name that is unlikely to conflict with the existing name
343 [[tunePopup menu] addItemWithTitle: [document tuneName] action: nil keyEquivalent: @""];
344 [tunePopup setEnabled: YES];
345 if (activeIndex == -1)
346 [self selectTuneAtIndex: n];
347 } else {
348 // Update the item name
349 [[tunePopup itemAtIndex: n] setTitle: [document tuneName]];
350 // if ([tunePopup indexOfSelectedItem] == n)
351 // [self selectTuneAtIndex: n]; // Refresh the internal information
352 }
353 return n;
354 }
355 */
356 /*
357 - (void)removeMIDIDocument: (MyDocument *)document
358 {
359 unsigned int n;
360 n = [docArray indexOfObject: document];
361 if (n != NSNotFound) {
362 if (n == activeIndex)
363 [self pressStopButton: self];
364 [docArray removeObjectAtIndex: n];
365 [tunePopup removeItemAtIndex: n];
366 if ([tunePopup numberOfItems] == 0) {
367 [tunePopup setEnabled: NO];
368 [self selectTuneAtIndex: -1];
369 } else {
370 if (n > 0)
371 n--;
372 [self selectTuneAtIndex: n];
373 }
374 }
375 }
376 */
377
378 - (void)timerCallback: (NSTimer *)timer
379 {
380 int status;
381 BOOL redrawRecordTrack = NO;
382 MyMIDISequence *seq = [myDocument myMIDISequence];
383 MDPlayer *player = [seq myPlayer];
384 callbackCount++;
385 if (player == NULL)
386 return;
387 status = MDPlayerGetStatus(player);
388 if (status == kMDPlayer_playing) {
389 currentTime = MDPlayerGetTime(player); /* Update the current time */
390 [self refreshTimeDisplay];
391 if (isRecording) {
392 NSDictionary *info = [seq recordingInfo];
393 if (callbackCount % 10 == 0)
394 redrawRecordTrack = YES;
395 // Check if recording should be stopped
396 if ([[info valueForKey: MyRecordingInfoStopFlagKey] boolValue]) {
397 MDTickType currentTick = MDCalibratorTimeToTick(calibrator, currentTime);
398 if (currentTick >= [[info valueForKey: MyRecordingInfoStopTickKey] doubleValue]) {
399 // Stop recording (but continue to play)
400 if (isAudioRecording) {
401 // Audio data can be retrieved at this time
402 [myDocument finishAudioRecording];
403 isAudioRecording = NO;
404 } else {
405 // MIDI data will be retrieved when the stop button is pressed
406 // (It is a bad idea to insert a new track during playing)
407 // At this time, only flush the buffer and put the events to
408 // the recordTrack (in MyMIDISequence object)
409 MDPlayerStopRecording(player);
410 redrawRecordTrack = YES;
411 }
412 isRecording = NO;
413 [recordButton setState:NSOffState];
414 }
415 }
416 }
417 } else if (status == kMDPlayer_exhausted) {
418 // Player stopped playing
419 [self pressStopButton: self];
420 }
421 if (redrawRecordTrack) {
422 [seq collectRecordedEvents];
423 [parentController reloadClientViews];
424 }
425 }
426
427 #pragma mark ====== Action methods ======
428
429 - (void)setCurrentTime: (MDTimeType)newTime
430 {
431 int status;
432 MDTickType newTick, duration;
433 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
434 if (player == NULL || (status = MDPlayerGetStatus(player)) == kMDPlayer_playing || status == kMDPlayer_exhausted)
435 return; /* Do nothing */
436 newTick = MDCalibratorTimeToTick(calibrator, newTime);
437 duration = [[myDocument myMIDISequence] sequenceDuration];
438 if (newTick > duration) {
439 newTick = duration;
440 newTime = MDCalibratorTickToTime(calibrator, newTick);
441 } else if (newTick < 0) {
442 newTick = 0;
443 newTime = 0;
444 }
445 currentTime = newTime;
446 if (status == kMDPlayer_suspended)
447 [self pressStopButton: self];
448 [self refreshTimeDisplay];
449 }
450
451 - (void)setCurrentTick: (MDTickType)newTick
452 {
453 [self setCurrentTime: MDCalibratorTickToTime(calibrator, newTick)];
454 }
455
456 - (void)prerollWithFeedback
457 {
458 MDPlayer *player;
459 player = [[myDocument myMIDISequence] myPlayer];
460 if (player != NULL) {
461 [progressIndicator startAnimation: self];
462 MDPlayerPreroll(player, MDCalibratorTimeToTick(calibrator, currentTime), 1);
463 [progressIndicator stopAnimation: self];
464 }
465 }
466
467 - (void)restartAfterManualMovement
468 {
469 int status;
470 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
471 if (player == NULL)
472 return;
473 status = MDPlayerGetStatus(player);
474 MDPlayerJumpToTick(player, MDCalibratorTimeToTick(calibrator, currentTime));
475 if (status == kMDPlayer_suspended || shouldContinuePlay) {
476 if (shouldContinuePlay) {
477 /* Note: recording will be stopped and does not recover automatically */
478 // MDPlayerJumpToTick(player, MDCalibratorTimeToTick(calibrator, currentTime));
479 [self pressPlayButton: self];
480 } else {
481 [self prerollWithFeedback];
482 }
483 }
484 shouldContinuePlay = NO;
485 }
486
487 - (IBAction)moveSlider:(id)sender
488 {
489 int status;
490 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
491 if (player == NULL)
492 return;
493 status = MDPlayerGetStatus(player);
494 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
495 [self pressStopButton: self];
496 shouldContinuePlay = YES;
497 }
498 currentTime = totalTime * ([sender doubleValue] / 100.0);
499 [self refreshTimeDisplay];
500 if ([[NSApp currentEvent] type] == NSLeftMouseUp)
501 [self restartAfterManualMovement];
502 }
503
504 - (IBAction)pressFFButton:(id)sender
505 {
506 int status;
507 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
508 if (player == NULL)
509 return;
510 status = MDPlayerGetStatus(player);
511 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
512 [self pressStopButton: self];
513 shouldContinuePlay = YES;
514 }
515 currentTime += 1000000;
516 if (currentTime > totalTime)
517 currentTime = totalTime;
518 [self refreshTimeDisplay];
519 if ([[NSApp currentEvent] type] == NSLeftMouseUp)
520 [self restartAfterManualMovement];
521 }
522
523 - (IBAction)pressRewindButton:(id)sender
524 {
525 int status;
526 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
527 if (player == NULL)
528 return;
529 status = MDPlayerGetStatus(player);
530 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
531 [self pressStopButton: self];
532 shouldContinuePlay = YES;
533 }
534 currentTime -= 1000000;
535 if (currentTime < 0)
536 currentTime = 0;
537 [self refreshTimeDisplay];
538 if ([[NSApp currentEvent] type] == NSLeftMouseUp)
539 [self restartAfterManualMovement];
540 }
541
542 - (IBAction)pressPauseButton:(id)sender
543 {
544 int status;
545 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
546 if (player == NULL)
547 return;
548 status = MDPlayerGetStatus(player);
549 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
550 MDPlayerSuspend(player);
551 status = kMDPlayer_suspended;
552 [playButton setState:NSOffState];
553 } else if (status == kMDPlayer_ready || status == kMDPlayer_idle) {
554 /* Jump to the "current" time, send MIDI events before that time, and wait for play */
555 [self prerollWithFeedback];
556 status = kMDPlayer_suspended;
557 } else return;
558 [pauseButton setState:(status == kMDPlayer_suspended ? NSOnState : NSOffState)];
559 /* [[docArray objectAtIndex: activeIndex] postPlayPositionNotification]; */
560 }
561
562 - (void)pressPlayButtonWithRecording:(BOOL)isRecordButton
563 {
564 int status;
565 MDTickType currentTick;
566 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
567 if (player == NULL)
568 return;
569 status = MDPlayerGetStatus(player);
570 [playButton setState:NSOnState];
571 currentTick = MDCalibratorTimeToTick(calibrator, currentTime);
572 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted)
573 return;
574 else if (status == kMDPlayer_suspended) {
575 [pauseButton setState:NSOnState];
576 } else if (status == kMDPlayer_ready || status == kMDPlayer_idle) {
577 MDPlayerJumpToTick(player, currentTick);
578 }
579 if (isRecordButton) {
580 BOOL flag;
581 int countOffNumber;
582 NSDictionary *info = [[myDocument myMIDISequence] recordingInfo];
583 countOffNumber = [[info valueForKey: MyRecordingInfoCountOffNumberKey] intValue];
584 if (countOffNumber > 0) {
585 /* Handle metronome count-off */
586 int bar, beat, barBeatFlag, barTime, beatTime, tickInBeatTime;
587 int32_t currentBar, currentBeat, currentTickInBeat;
588 int countOffDuration;
589 float timebase = [myDocument timebase];
590 MDEvent *ep = MDCalibratorGetEvent(calibrator, NULL, kMDEventTimeSignature, -1);
591 float tempo = MDCalibratorGetTempo(calibrator);
592 barBeatFlag = [[info valueForKey: MyRecordingInfoBarBeatFlagKey] intValue];
593 MDEventCalculateMetronomeBarAndBeat(ep, (int32_t)timebase, &bar, &beat);
594 MDCalibratorTickToMeasure(calibrator, currentTick, &currentBar, &currentBeat, &currentTickInBeat);
595 barTime = (int)floor(bar * 60000000.0 / (tempo * timebase) + 0.5);
596 beatTime = (int)ceil(beat * 60000000.0 / (tempo * timebase));
597 tickInBeatTime = (int)floor(currentTickInBeat * 60000000.0 / (tempo * timebase) + 0.5);
598 if (barBeatFlag) {
599 countOffDuration = barTime * countOffNumber;
600 /* Add ticks between the beginning of the bar and currentTick */
601 countOffDuration += (currentBeat - 1) * beatTime + tickInBeatTime;
602 MDPlayerSetCountOffSettings(player, countOffDuration, barTime, beatTime);
603 } else {
604 countOffDuration = beatTime * countOffNumber;
605 /* Add ticks between the nearest beat and currentTick */
606 countOffDuration += tickInBeatTime;
607 MDPlayerSetCountOffSettings(player, countOffDuration, 0, beatTime);
608 }
609 } else MDPlayerSetCountOffSettings(player, 0, 0, 0);
610 if (isAudioRecording)
611 flag = [myDocument startAudioRecording];
612 else
613 flag = [myDocument startRecording];
614 if (!flag) {
615 NSRunAlertPanel(@"Recording error", @"Cannot start recording", nil, nil, nil);
616 return;
617 }
618 isRecording = YES;
619 [recordButton setState:NSOnState];
620 } else
621 MDPlayerStart(player);
622
623 /* Enable timer for updating displays */
624 /* (Add for three modes, so that display is updated during modal loop or dragging) */
625 timer = [[NSTimer allocWithZone:[self zone]] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.1] interval:0.1 target:self selector:@selector(timerCallback:) userInfo:nil repeats:YES];
626 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
627 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
628 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
629
630 callbackCount = 0;
631 status = kMDPlayer_playing;
632 }
633
634 - (IBAction)pressPlayButton:(id)sender
635 {
636 [self pressPlayButtonWithRecording: NO];
637 }
638
639 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
640 {
641 RecordPanelController *cont = (RecordPanelController *)[sheet windowController];
642 NSDictionary *info;
643 [cont saveInfoToDocument];
644 [cont close];
645 if (returnCode == 1) {
646 info = [[myDocument myMIDISequence] recordingInfo];
647 currentTime = MDCalibratorTickToTime(calibrator, (float)[[info valueForKey: MyRecordingInfoStartTickKey] doubleValue]);
648 isAudioRecording = [[info valueForKey: MyRecordingInfoIsAudioKey] boolValue];
649 [self pressPlayButtonWithRecording: YES];
650 } else {
651 [recordButton setState:NSOffState];
652 }
653 [cont release];
654 }
655
656 - (void)recordButtonPressed: (id)sender audioFlag: (BOOL)audioFlag
657 {
658 RecordPanelController *controller;
659
660 controller = [[RecordPanelController allocWithZone: [self zone]] initWithDocument: myDocument audio: audioFlag];
661 [controller reloadInfoFromDocument];
662 if (audioFlag)
663 [AudioSettingsPanelController openAudioSettingsPanel];
664 [[NSApplication sharedApplication] beginSheet: [controller window]
665 modalForWindow: [parentController window]
666 modalDelegate: self
667 didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
668 contextInfo: nil];
669 }
670
671 - (IBAction)pressRecordButton:(id)sender
672 {
673 // If "option" key is pressed then start audio recording
674 // (This is very ugly and needs update later)
675 BOOL audioFlag = NO;
676 if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
677 audioFlag = YES;
678 [self recordButtonPressed: sender audioFlag: audioFlag];
679 }
680
681 - (IBAction)pressStopButton:(id)sender
682 {
683 int status;
684 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
685 if (player == NULL)
686 return;
687 status = MDPlayerGetStatus(player);
688 if (status == kMDPlayer_ready || status == kMDPlayer_idle) {
689 MDPlayerJumpToTick(player, 0); /* Rewind to the beginning of the tune */
690 currentTime = 0;
691 } else {
692 MDTimeType maxTime;
693 currentTime = MDPlayerGetTime(player);
694 if (timer != nil) {
695 [timer invalidate];
696 [timer autorelease];
697 timer = nil;
698 }
699 MDPlayerStop(player);
700
701 // Finish recording
702 // MIDI recording may have finished before (see timerCallback:), so
703 // we need to check the presence of [myMIDISequence recordTrack]
704 if ([[myDocument myMIDISequence] recordTrack] != NULL) {
705 [myDocument finishRecording];
706 } else if (isRecording && isAudioRecording) {
707 [myDocument finishAudioRecording];
708 }
709 isRecording = NO;
710 isAudioRecording = NO;
711 [recordButton setState:NSOnState];
712
713 /* Limit currentTime by sequence duration */
714 maxTime = MDCalibratorTickToTime(calibrator, MDSequenceGetDuration([[myDocument myMIDISequence] mySequence]));
715 if (currentTime > maxTime)
716 currentTime = maxTime;
717 }
718 status = kMDPlayer_ready;
719 [self refreshTimeDisplay];
720 [playButton setState:NSOffState];
721 [pauseButton setState:NSOffState];
722 [recordButton setState:NSOffState];
723 }
724
725 - (IBAction)selectMarker:(id)sender
726 {
727 int status;
728 int index;
729 MDTickType tick;
730 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
731 if (player == NULL)
732 return;
733 status = MDPlayerGetStatus(player);
734 index = (int)[sender indexOfSelectedItem];
735 if (index >= 0 && index < [tickArray count]) {
736 if (status == kMDPlayer_playing || status == kMDPlayer_exhausted) {
737 shouldContinuePlay = YES;
738 [self pressStopButton: self];
739 }
740 tick = (MDTickType)[[tickArray objectAtIndex: index] doubleValue];
741 currentTime = MDCalibratorTickToTime(calibrator, tick);
742 MDPlayerJumpToTick(player, tick);
743 if (shouldContinuePlay)
744 [self pressPlayButton: self];
745 else if (status == kMDPlayer_suspended) {
746 MDPlayerPreroll(player, tick, 1);
747 [pauseButton setState:NSOnState];
748 }
749 [self refreshTimeDisplay];
750 [myDocument postPlayPositionNotification:tick];
751 shouldContinuePlay = NO;
752 }
753 }
754
755 - (IBAction)tickTextEdited: (id)sender
756 {
757 int32_t bar, beat, subtick;
758 MDTickType tick;
759 if (MDEventParseTickString([[sender stringValue] UTF8String], &bar, &beat, &subtick) < 3)
760 return;
761 tick = MDCalibratorMeasureToTick(calibrator, bar, beat, subtick);
762 [self setCurrentTick: tick];
763 }
764
765 - (IBAction)timeTextEdited: (id)sender
766 {
767 int hour, min, sec;
768 MDTimeType time;
769 const char *s;
770 int n;
771 s = [[sender stringValue] UTF8String];
772 n = sscanf(s, "%d%*[^-0-9]%d%*[^-0-9]%d", &hour, &min, &sec);
773 switch (n) {
774 case 1: hour = min = 0; break;
775 case 2: hour = 0; break;
776 case 3: break;
777 default: return;
778 }
779 time = (((MDTimeType)hour * 60 + (MDTimeType)min) * 60 + (MDTimeType)sec) * 1000000;
780 [self setCurrentTime: time];
781 }
782
783 /*
784 - (IBAction)selectTune:(id)sender
785 {
786 [self selectTuneAtIndex: [tunePopup indexOfSelectedItem]];
787 }
788 */
789
790 #pragma mark ====== Notification Handler ======
791
792 - (void)trackModified: (NSNotification *)notification
793 {
794 [self updateMarkerList];
795 [self refreshTimeDisplay];
796 }
797
798 - (void)trackInserted: (NSNotification *)notification
799 {
800 MDPlayer *player = [[myDocument myMIDISequence] myPlayer];
801 if (player != NULL)
802 MDPlayerRefreshTrackDestinations(player); /* Refresh internal track list */
803 [self updateMarkerList];
804 [self refreshTimeDisplay];
805 }
806
807 - (void)trackDeleted: (NSNotification *)notification
808 {
809 [self trackInserted:notification];
810 }
811
812 @end

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