diff --git a/Sources/VLDocument.h b/Sources/VLDocument.h index 623853b..ba4f008 100644 --- a/Sources/VLDocument.h +++ b/Sources/VLDocument.h @@ -11,6 +11,7 @@ #import "VLModel.h" #import #import "VLKeyValueUndo.h" +#import @class VLSheetWindow; @@ -56,7 +57,7 @@ enum { NSMutableDictionary*validTmpFiles; int repeatVolta; bool brandNew; - bool hasMusicSequence; + MusicSequence musicSequence; VLSheetWindow * sheetWin; VLKeyValueUndo* undo; VLKeyValueUndo* staffMetrics; diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index d422131..40f5e24 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -102,7 +102,7 @@ vcsWrapper = nil; repeatVolta = 2; brandNew = true; - hasMusicSequence = false; + musicSequence = nil; playRate = 1.0; validTmpFiles = [[NSMutableDictionary alloc] initWithCapacity:10]; [self setHasUndoManager:YES]; @@ -138,7 +138,7 @@ - (void)updateChangeCount:(NSDocumentChangeType)changeType { - hasMusicSequence = false; + musicSequence = nil; [validTmpFiles removeAllObjects]; [super updateChangeCount:changeType]; } @@ -458,15 +458,16 @@ - (void) playSong { - if (hasMusicSequence) { + if (musicSequence) { + void (^finalizer)() = [sheetWin willPlaySequence:musicSequence]; VLSoundOut::Instance()->PlaySequence(NULL); + finalizer(); } else { [self createTmpFileWithExtension:@"mid" ofType:VLMIDIType]; - MusicSequence music; - NewMusicSequence(&music); + NewMusicSequence(&musicSequence); - MusicSequenceFileLoad(music, (CFURLRef)[self fileURLWithExtension:@"mid"], + MusicSequenceFileLoad(musicSequence, (CFURLRef)[self fileURLWithExtension:@"mid"], 0, 0); size_t countIn = 0; @@ -477,14 +478,14 @@ case 0x608: countIn = 2; } - VLMIDIWriter annotate(music, countIn); + VLMIDIWriter annotate(musicSequence, countIn); annotate.Visit(*song); - hasMusicSequence = true; - [sheetWin willPlaySequence:music]; baseTempo = songTempo; - VLSoundOut::Instance()->SetPlayRate(playRate); - VLSoundOut::Instance()->PlaySequence(music); + void (^finalizer)() = [sheetWin willPlaySequence:musicSequence]; + VLSoundOut::Instance()->SetPlayRate(playRate); + VLSoundOut::Instance()->PlaySequence(musicSequence); + finalizer(); } [self setPlayElements:[self playElements]]; } @@ -494,7 +495,7 @@ NSString * savedGroove = songGroove; [validTmpFiles removeObjectForKey:@"mma"]; [validTmpFiles removeObjectForKey:@"mid"]; - hasMusicSequence = false; + musicSequence = nil; songGroove = groove; previewRange = sections; playElements |= kVLPlayGroovePreview; @@ -503,7 +504,7 @@ songGroove = savedGroove; [validTmpFiles removeObjectForKey:@"mma"]; [validTmpFiles removeObjectForKey:@"mid"]; - hasMusicSequence = false; + musicSequence = nil; } - (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings diff --git a/Sources/VLMIDIWriter.cpp b/Sources/VLMIDIWriter.cpp index 69ecba9..4b55f6b 100644 --- a/Sources/VLMIDIWriter.cpp +++ b/Sources/VLMIDIWriter.cpp @@ -11,6 +11,50 @@ #include "VLMIDIWriter.h" #include +VLMIDIUtilities::VLMIDIUtilities(MusicSequence music) + : fMusic(music) +{ +} + +MusicTimeStamp VLMIDIUtilities::Length() +{ + UInt32 ntracks; + MusicSequenceGetTrackCount(fMusic, &ntracks); + MusicTimeStamp sequenceLength = 0; + for (UInt32 i = 0; i < ntracks; ++i) { + MusicTrack track; + MusicTimeStamp trackLength; + UInt32 propsize = sizeof(MusicTimeStamp); + MusicSequenceGetIndTrack(fMusic, i, &track); + MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, + &trackLength, &propsize); + sequenceLength = std::max(sequenceLength, trackLength); + } + return sequenceLength; +} + +MusicTimeStamp VLMIDIUtilities::Find(VLLocation at) +{ + UInt32 ntracks; + MusicSequenceGetTrackCount(fMusic, &ntracks); + MusicTrack track; + MusicSequenceGetIndTrack(fMusic, ntracks-1, &track); + MusicEventIterator iter; + NewMusicEventIterator(track, &iter); + Boolean hasEvent; + while (!MusicEventIteratorHasCurrentEvent(iter, &hasEvent) && hasEvent) { + MusicTimeStamp ts; + MusicEventType ty; + const VLMIDIUserEvent * data; + UInt32 sz; + MusicEventIteratorGetEventInfo(iter, &ts, &ty, (const void **)&data, &sz); + if (ty == kMusicEventType_User && data->fAt >= at) + return ts; + MusicEventIteratorNextEvent(iter); + } + return Length(); +} + struct VLMetaEvent : MIDIMetaEvent { char fPadding[32]; diff --git a/Sources/VLMIDIWriter.h b/Sources/VLMIDIWriter.h index b8fbd16..78d01ae 100644 --- a/Sources/VLMIDIWriter.h +++ b/Sources/VLMIDIWriter.h @@ -20,6 +20,16 @@ struct VLMIDIUserEvent { VLLocation fAt; }; +class VLMIDIUtilities { +public: + VLMIDIUtilities(MusicSequence music); + + MusicTimeStamp Length(); + MusicTimeStamp Find(VLLocation at); +public: + MusicSequence fMusic; +}; + class VLMIDIWriter: public VLSongVisitor { public: VLMIDIWriter(MusicSequence music, size_t countIn) diff --git a/Sources/VLModel.h b/Sources/VLModel.h index b3e94cd..7e093f8 100644 --- a/Sources/VLModel.h +++ b/Sources/VLModel.h @@ -13,6 +13,9 @@ #include #include +#ifndef __VLMODEL__ +#define __VLMODEL__ + #pragma mark - #pragma mark class VLFraction @@ -497,6 +500,8 @@ protected: void VisitChords(VLMeasure & measure); }; +#endif + // Local Variables: // mode:C++ // End: diff --git a/Sources/VLSheetViewSelection.h b/Sources/VLSheetViewSelection.h index 3e0ef80..c6f13b8 100644 --- a/Sources/VLSheetViewSelection.h +++ b/Sources/VLSheetViewSelection.h @@ -36,7 +36,8 @@ - (void)updateMenus; -- (void) willPlaySequence:(MusicSequence)music; +- (void (^)()) willPlaySequence:(MusicSequence)music; +- (void) playWasPaused; @end diff --git a/Sources/VLSheetViewSelection.mm b/Sources/VLSheetViewSelection.mm index d4d104a..b21cbb9 100644 --- a/Sources/VLSheetViewSelection.mm +++ b/Sources/VLSheetViewSelection.mm @@ -17,6 +17,8 @@ #import "VLSheetWindow.h" #import "VLDocument.h" #import "VLPitchGrid.h" +#import "VLSoundOut.h" +#import "VLMIDIWriter.h" #pragma mark VLMeasureEditable @@ -126,6 +128,11 @@ return self; } +- (void)dealloc +{ + [fView setNeedsDisplay:YES]; +} + - (void) userEvent:(const VLMIDIUserEvent *) event { if (event->fPitch) { @@ -583,12 +590,28 @@ inline int TimeTag(const VLProperties & prop) [self updateGrooveMenu]; } -- (void) willPlaySequence:(MusicSequence)music +- (void (^)()) willPlaySequence:(MusicSequence)music { + uint32_t selStart = fSelStart; + uint32_t selEnd = fSelEnd; + VLEditable * e = [[VLPlaybackEditable alloc] initWithView:self]; [self setEditTarget:e]; MusicSequenceSetUserCallback(music, VLSequenceCallback, e); + + return [Block_copy(^{ + if (selEnd != kNoMeasure) { + VLMIDIUtilities locator(music); + VLLocation start = {selStart, VLFraction(0)}; + VLLocation end = {selEnd, VLFraction(0)}; + VLSoundOut::Instance()->SetStart(locator.Find(start)); + if (selEnd > selStart) { + VLSoundOut::Instance()->SetEnd(locator.Find(end)); + [self selectMeasure:selStart to:selEnd]; + } + } + }) autorelease]; } @end diff --git a/Sources/VLSheetWindow.h b/Sources/VLSheetWindow.h index 7b1a685..a4708f3 100644 --- a/Sources/VLSheetWindow.h +++ b/Sources/VLSheetWindow.h @@ -64,7 +64,7 @@ - (void) setEditTarget:(VLEditable *)editable; - (void) startAnimation; - (void) stopAnimation; -- (void) willPlaySequence:(MusicSequence)music; +- (void (^)()) willPlaySequence:(MusicSequence)music; - (void) showLogAndBeep; @end diff --git a/Sources/VLSheetWindow.mm b/Sources/VLSheetWindow.mm index f98ac98..c6ff39d 100644 --- a/Sources/VLSheetWindow.mm +++ b/Sources/VLSheetWindow.mm @@ -77,6 +77,8 @@ soundStopObserver = [nc addObserverForName:(NSString*)kVLSoundStoppedNotification object:nil queue:oq usingBlock:^(NSNotification *note) { [[[self window] toolbar] validateVisibleItems]; + if (VLSoundOut::Instance()->AtBeginning()) + [self setEditTarget:nil]; // Kill note cursor if we ran to end }]; } return self; @@ -96,8 +98,10 @@ - (void)setEditTarget:(VLEditable *)editable { - [editTarget autorelease]; - editTarget = editable; + if (editTarget != editable) { + [editTarget release]; + editTarget = editable; + } } - (void) startAnimation @@ -125,9 +129,9 @@ [sheetView mouseMoved:event]; } -- (void) willPlaySequence:(MusicSequence)music +- (void (^)()) willPlaySequence:(MusicSequence)music { - [sheetView willPlaySequence:music]; + return [sheetView willPlaySequence:music]; } - (IBAction) togglePlayElements:(id)sender @@ -183,7 +187,7 @@ - (IBAction) playStop:(id)sender { - if (VLSoundOut::Instance()->Playing()) + if (VLSoundOut::Instance()->Playing()) [self stop:sender]; else [[self document] playSong]; diff --git a/Sources/VLSoundOut.cpp b/Sources/VLSoundOut.cpp index 1780b31..7581860 100644 --- a/Sources/VLSoundOut.cpp +++ b/Sources/VLSoundOut.cpp @@ -9,6 +9,7 @@ // #include "VLSoundOut.h" +#include "VLMIDIWriter.h" #include @@ -31,9 +32,12 @@ public: virtual void PlayNote(const VLNote & note); virtual void PlayChord(const VLChord & chord); virtual void PlaySequence(MusicSequence music); + virtual void SetStart(MusicTimeStamp start); + virtual void SetEnd(MusicTimeStamp end); virtual void Stop(bool pause); virtual bool Playing(); virtual bool AtEnd(); + virtual bool AtBeginning(); virtual void SetPlayRate(float rate); virtual void Fwd(); virtual void Bck(); @@ -47,16 +51,16 @@ protected: void InitSoundOutput(bool fileOutput); virtual void SetupOutput(AUNode outputNode); - MusicTimeStamp SequenceLength(MusicSequence music); void SkipTimeInterval(); AUGraph fGraph; MusicPlayer fPlayer; private: MusicSequence fMusic; - MusicTimeStamp fMusicLength; + MusicTimeStamp fMusicEnd; bool fRunning; bool fForward; + bool fWasAtEnd; float fPlayRate; dispatch_source_t fMusicPoll; @@ -157,7 +161,7 @@ VLSoundOut::~VLSoundOut() } VLAUSoundOut::VLAUSoundOut() - : fMusic(0), fRunning(false), fForward(true) + : fMusic(0), fRunning(false), fForward(true), fWasAtEnd(true) { InitSoundOutput(false); fMusicPoll = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); @@ -245,8 +249,9 @@ void VLAUSoundOut::PlaySequence(MusicSequence music) Stop(false); fMusic = music; - fMusicLength = SequenceLength(music); + fMusicEnd = VLMIDIUtilities(music).Length(); fPlayRate = 1.0; + fWasAtEnd = true; R(MusicSequenceSetAUGraph(fMusic, fGraph)); R(MusicPlayerSetSequence(fPlayer, fMusic)); @@ -256,7 +261,19 @@ void VLAUSoundOut::PlaySequence(MusicSequence music) fRunning = true; CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), kVLSoundStartedNotification, NULL, NULL, false); - dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_NOW, 500*NSEC_PER_MSEC, 200*NSEC_PER_MSEC); + dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_NOW, 10*NSEC_PER_MSEC, 200*NSEC_PER_MSEC); +} + +void VLAUSoundOut::SetStart(MusicTimeStamp start) +{ + if (fWasAtEnd) + MusicPlayerSetTime(fPlayer, start); +} + +void VLAUSoundOut::SetEnd(MusicTimeStamp end) +{ + if (fWasAtEnd) + fMusicEnd = end; } void VLAUSoundOut::SetMelodyState(VLSoundOut::MelodyState state) @@ -282,7 +299,7 @@ void VLAUSoundOut::SetPlayRate(float rate) MusicTimeStamp rightNow; MusicPlayerGetTime(fPlayer, &rightNow); MusicSequenceReverse(fMusic); - MusicPlayerSetTime(fPlayer, fMusicLength - rightNow); + MusicPlayerSetTime(fPlayer, fMusicEnd - rightNow); } fPlayRate = fabsf(rate); MusicPlayerSetPlayRateScalar(fPlayer, fPlayRate); @@ -343,8 +360,12 @@ void VLAUSoundOut::Slow(float rate) void VLAUSoundOut::Stop(bool pause) { + if (!fRunning) + return; + MusicPlayerStop(fPlayer); fRunning = false; + fWasAtEnd = false; if (!pause && fMusic) { MusicPlayerSetSequence(fPlayer, NULL); DisposeMusicSequence(fMusic); @@ -364,7 +385,14 @@ bool VLAUSoundOut::AtEnd() { MusicTimeStamp time; - return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicLength; + return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicEnd; +} + +bool VLAUSoundOut::AtBeginning() +{ + MusicTimeStamp time; + + return MusicPlayerGetTime(fPlayer, &time) || !time; } void VLAUSoundOut::PollMusic() @@ -372,6 +400,7 @@ void VLAUSoundOut::PollMusic() if (fRunning && AtEnd()) { MusicPlayerSetTime(fPlayer, 0); Stop(true); + fWasAtEnd = true; } } @@ -412,23 +441,6 @@ void VLAUSoundOut::Play(const int8_t * note, size_t numNotes) PlaySequence(music); } -MusicTimeStamp VLAUSoundOut::SequenceLength(MusicSequence music) -{ - UInt32 ntracks; - MusicSequenceGetTrackCount(music, &ntracks); - MusicTimeStamp sequenceLength = 0; - for (UInt32 i = 0; i < ntracks; ++i) { - MusicTrack track; - MusicTimeStamp trackLength; - UInt32 propsize = sizeof(MusicTimeStamp); - MusicSequenceGetIndTrack(music, i, &track); - MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, - &trackLength, &propsize); - sequenceLength = std::max(sequenceLength, trackLength); - } - return sequenceLength; -} - VLAUFileSoundOut::VLAUFileSoundOut(CFURLRef file, OSType dataFormat) : VLAUSoundOut(true), fFile(file), fDataFormat(dataFormat) { @@ -459,7 +471,7 @@ void VLAUFileSoundOut::PlaySequence(MusicSequence music) UInt32 size; UInt32 numFrames = 512; - MusicTimeStamp musicLen = SequenceLength(music)+8; + MusicTimeStamp musicLen = VLMIDIUtilities(music).Length()+8; CFStringRef name = CFURLCopyLastPathComponent(fFile); CAAudioFileFormats * formats = CAAudioFileFormats::Instance(); diff --git a/Sources/VLSoundOut.h b/Sources/VLSoundOut.h index e4686b9..1a6c773 100644 --- a/Sources/VLSoundOut.h +++ b/Sources/VLSoundOut.h @@ -42,9 +42,12 @@ public: virtual void PlayChord(const VLChord & chord) = 0; void PlayFile(CFDataRef file); virtual void PlaySequence(MusicSequence music) = 0; + virtual void SetStart(MusicTimeStamp start) = 0; + virtual void SetEnd(MusicTimeStamp end) = 0; virtual void Stop(bool pause=true) = 0; virtual bool Playing() = 0; virtual bool AtEnd() = 0; + virtual bool AtBeginning() = 0; virtual void SetPlayRate(float rate) = 0; virtual void Fwd() = 0; virtual void Bck() = 0;