Refactor music playing code

This commit is contained in:
Matthias Neeracher 2011-09-04 21:48:57 +02:00
parent ab32c3cc12
commit 273ee8ee73
11 changed files with 136 additions and 84 deletions

View File

@ -41,7 +41,7 @@ enum {
NSString * songComposer; NSString * songComposer;
NSString * songArranger; NSString * songArranger;
NSString * songGroove; NSString * songGroove;
NSNumber * songTempo; float songTempo;
float chordSize; float chordSize;
float lyricSize; float lyricSize;
float staffSize; float staffSize;
@ -65,6 +65,7 @@ enum {
float baseTempo; float baseTempo;
} }
@property (nonatomic) float songTempo;
@property (nonatomic) int playElements; @property (nonatomic) int playElements;
- (VLSong *) song; - (VLSong *) song;
@ -83,12 +84,8 @@ enum {
- (void) setRepeatVolta:(int)repeatVolta; - (void) setRepeatVolta:(int)repeatVolta;
- (IBAction) play:(id)sender; - (void) playSong;
- (IBAction) stop:(id)sender;
- (IBAction) playStop:(id)sender;
- (IBAction) playMusic:(id)sender;
- (IBAction) adjustTempo:(id)sender;
- (NSURL *) tmpURL; - (NSURL *) tmpURL;
- (NSURL *) workURL; - (NSURL *) workURL;
- (NSString *) baseName; - (NSString *) baseName;

View File

@ -68,7 +68,7 @@
@implementation VLDocument @implementation VLDocument
@synthesize playElements; @synthesize songTempo, playElements;
+ (BOOL)autosavesInPlace + (BOOL)autosavesInPlace
{ {
@ -87,7 +87,7 @@
songArranger = @""; songArranger = @"";
songGroove = @"Swing"; songGroove = @"Swing";
playElements = kVLPlayAccompaniment|kVLPlayMelody|kVLPlayCountIn; playElements = kVLPlayAccompaniment|kVLPlayMelody|kVLPlayCountIn;
songTempo = [[NSNumber alloc] initWithInt:120]; songTempo = 120.0f;
baseTempo = 120.0f; baseTempo = 120.0f;
chordSize = 6.0f; chordSize = 6.0f;
lyricSize = 0.0f; lyricSize = 0.0f;
@ -142,7 +142,7 @@
- (void) dealloc - (void) dealloc
{ {
VLSoundOut::Instance()->Stop(); VLSoundOut::Instance()->Stop(false);
delete song; delete song;
@ -237,13 +237,12 @@
[self didChangeSong]; [self didChangeSong];
} }
- (void) setSongTempo:(int)tempo - (void) setSongTempo:(float)tempo
{ {
if (tempo == [songTempo intValue]) if (tempo == songTempo)
return; return;
[songTempo autorelease]; songTempo = tempo;
songTempo = [[NSNumber numberWithInt:tempo] retain];
if (VLSoundOut::Instance()->Playing()) if (VLSoundOut::Instance()->Playing())
VLSoundOut::Instance()->SetPlayRate(playRate*tempo/baseTempo); VLSoundOut::Instance()->SetPlayRate(playRate*tempo/baseTempo);
} }
@ -466,7 +465,7 @@
return task; return task;
} }
- (IBAction) play:(id)sender - (void) playSong
{ {
if (hasMusicSequence) { if (hasMusicSequence) {
VLSoundOut::Instance()->PlaySequence(NULL); VLSoundOut::Instance()->PlaySequence(NULL);
@ -492,7 +491,7 @@
hasMusicSequence = true; hasMusicSequence = true;
[sheetWin willPlaySequence:music]; [sheetWin willPlaySequence:music];
baseTempo = [songTempo floatValue]; baseTempo = songTempo;
VLSoundOut::Instance()->SetPlayRate(playRate); VLSoundOut::Instance()->SetPlayRate(playRate);
VLSoundOut::Instance()->PlaySequence(music); VLSoundOut::Instance()->PlaySequence(music);
} }
@ -507,7 +506,7 @@
songGroove = groove; songGroove = groove;
previewRange = sections; previewRange = sections;
playElements |= kVLPlayGroovePreview; playElements |= kVLPlayGroovePreview;
[self play:groove]; [self playSong];
playElements &= ~kVLPlayGroovePreview; playElements &= ~kVLPlayGroovePreview;
songGroove = savedGroove; songGroove = savedGroove;
[validTmpFiles removeObjectForKey:@"mma"]; [validTmpFiles removeObjectForKey:@"mma"];
@ -515,49 +514,6 @@
hasMusicSequence = false; hasMusicSequence = false;
} }
- (IBAction) stop:(id)sender
{
VLSoundOut::Instance()->Stop();
}
- (IBAction) playStop:(id)sender
{
if (VLSoundOut::Instance()->Playing()) {
[self stop:sender];
if ([sender isKindOfClass:[NSMenuItem class]]) {
[sender setTitle:@"Play"];
} else {
[sender setLabel:@"Play"];
[sender setImage:[NSImage imageNamed:@"play.icns"]];
}
} else {
[self play:sender];
if ([sender isKindOfClass:[NSMenuItem class]]) {
[sender setTitle:@"Stop"];
} else {
[sender setLabel:@"Stop"];
[sender setImage:[NSImage imageNamed:@"stop.icns"]];
}
}
}
- (IBAction) playMusic:(id)sender
{
switch ([sender tag]) {
case 1: // Fwd
VLSoundOut::Instance()->Fwd();
break;
case -1: // Rew
VLSoundOut::Instance()->Bck();
break;
}
}
- (IBAction) adjustTempo:(id)sender
{
[self setSongTempo:[songTempo intValue]+[sender tag]];
}
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings - (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings
error:(NSError **)outError error:(NSError **)outError
{ {

View File

@ -11,7 +11,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@class VLSheetView; @class VLSheetView;
@class VLDocument; @class VLSheetWindow;
@interface VLGrooveController : NSWindowController { @interface VLGrooveController : NSWindowController {
NSDictionary * fGrooves; NSDictionary * fGrooves;
@ -24,7 +24,7 @@
NSArray * fSubStyleList; NSArray * fSubStyleList;
NSPredicate * fSubStyleFilter; NSPredicate * fSubStyleFilter;
VLSheetView * fView; VLSheetView * fView;
VLDocument * fDocument; VLSheetWindow * fSheetWin;
} }
- (id) initWithSheetView:(VLSheetView *)view; - (id) initWithSheetView:(VLSheetView *)view;

View File

@ -5,12 +5,12 @@
// //
// (MN) Matthias Neeracher // (MN) Matthias Neeracher
// //
// Copyright © 2007 Matthias Neeracher // Copyright © 2007-2011 Matthias Neeracher
// //
#import "VLGrooveController.h" #import "VLGrooveController.h"
#import "VLSheetView.h" #import "VLSheetView.h"
#import "VLDocument.h" #import "VLSheetWindow.h"
@implementation VLGrooveController @implementation VLGrooveController
@ -24,7 +24,7 @@
@"!(SELF matches[c] '.*(Intro|End)\\\\d*$')"] @"!(SELF matches[c] '.*(Intro|End)\\\\d*$')"]
retain]; retain];
fView = view; fView = view;
fDocument = [view document]; fSheetWin = (VLSheetWindow *)[view window];
[NSApp beginSheet: [self window] [NSApp beginSheet: [self window]
modalForWindow: [view window] modalForWindow: [view window]
@ -46,7 +46,7 @@
if ([sender state]) if ([sender state])
[fView playWithGroove:[[fBrowser selectedCellInColumn:1] stringValue]]; [fView playWithGroove:[[fBrowser selectedCellInColumn:1] stringValue]];
else else
[fDocument stop:sender]; [fSheetWin stop:self];
} }
- (IBAction)endSheet:(id)sender - (IBAction)endSheet:(id)sender
@ -56,7 +56,7 @@
- (void)didEndSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo - (void)didEndSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{ {
[fDocument stop:self]; [fSheetWin stop:self];
if (returnCode == NSAlertFirstButtonReturn) if (returnCode == NSAlertFirstButtonReturn)
[fView setGroove:[[fBrowser selectedCellInColumn:1] stringValue]]; [fView setGroove:[[fBrowser selectedCellInColumn:1] stringValue]];
@ -119,7 +119,7 @@
[fSubStyles objectForKey:@".DESC"], [fSubStyles objectForKey:@".DESC"],
[fSubStyles objectForKey: [fSubStyles objectForKey:
[[fBrowser selectedCellInColumn:1] stringValue]]]]; [[fBrowser selectedCellInColumn:1] stringValue]]]];
[fDocument stop:self]; [fSheetWin stop:self];
[self togglePlay:fPlayButton]; [self togglePlay:fPlayButton];
} else } else
[fDescription setStringValue:[fSubStyles objectForKey:@".DESC"]]; [fDescription setStringValue:[fSubStyles objectForKey:@".DESC"]];

View File

@ -26,7 +26,7 @@
+ (const char *)[[bndl objectForInfoDictionaryKey:@"CFBundleVersion"] + (const char *)[[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]
UTF8String] UTF8String]
+ "\n\n"; + "\n\n";
sprintf(buf, "Tempo %d\n", [songTempo intValue]); sprintf(buf, "Tempo %ld\n", lround(songTempo));
mmaFile += buf; mmaFile += buf;
if (playElements & kVLPlayGroovePreview) { if (playElements & kVLPlayGroovePreview) {
// //

View File

@ -243,7 +243,7 @@ void VLPlistVisitor::VisitChord(VLChord & c)
{ {
NSMutableDictionary * plist = NSMutableDictionary * plist =
[NSMutableDictionary dictionaryWithObjectsAndKeys: [NSMutableDictionary dictionaryWithObjectsAndKeys:
songTitle, @"title", songTempo, @"tempo", songTitle, @"title", [NSNumber numberWithLong:lround(songTempo)], @"tempo",
[NSString stringWithUTF8String:song->PrimaryGroove().c_str()], @"groove", [NSString stringWithUTF8String:song->PrimaryGroove().c_str()], @"groove",
songComposer, @"composer", songLyricist, @"lyricist", songComposer, @"composer", songLyricist, @"lyricist",
[NSDate date], @"saved", [NSDate date], @"saved",

View File

@ -120,6 +120,7 @@ enum VLCursorVisual {
- (IBAction) editDisplayOptions:(id)sender; - (IBAction) editDisplayOptions:(id)sender;
- (IBAction) transposeOctave:(id)sender; - (IBAction) transposeOctave:(id)sender;
- (VLDocument *) document; - (VLDocument *) document;
- (VLSong *) song; - (VLSong *) song;
- (NSImage *) musicElement:(VLMusicElement)elt; - (NSImage *) musicElement:(VLMusicElement)elt;

View File

@ -33,6 +33,8 @@
IBOutlet NSProgressIndicator * progressIndicator; IBOutlet NSProgressIndicator * progressIndicator;
IBOutlet VLLogWindow * logWin; IBOutlet VLLogWindow * logWin;
IBOutlet VLPDFWindow * pdfWin; IBOutlet VLPDFWindow * pdfWin;
id soundStartObserver;
id soundStopObserver;
VLEditable * editTarget; VLEditable * editTarget;
@ -46,6 +48,10 @@
- (IBAction) togglePlayElements:(id)sender; - (IBAction) togglePlayElements:(id)sender;
- (IBAction) showOutput:(id)sender; - (IBAction) showOutput:(id)sender;
- (IBAction) stop:(id)sender;
- (IBAction) playStop:(id)sender;
- (IBAction) playMusic:(id)sender;
- (IBAction) adjustTempo:(id)sender;
- (VLEditable *) editTarget; - (VLEditable *) editTarget;
- (void) setEditTarget:(VLEditable *)editable; - (void) setEditTarget:(VLEditable *)editable;

View File

@ -12,6 +12,7 @@
#import "VLDocument.h" #import "VLDocument.h"
#import "VLPDFWindow.h" #import "VLPDFWindow.h"
#import "VLLogWindow.h" #import "VLLogWindow.h"
#import "VLSoundOut.h"
@implementation VLEditable @implementation VLEditable
@ -51,10 +52,27 @@
{ {
if (self = [super initWithWindow:window]) { if (self = [super initWithWindow:window]) {
editTarget = nil; editTarget = nil;
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
NSOperationQueue * oq = [NSOperationQueue mainQueue];
soundStartObserver = [nc addObserverForName:(NSString*)kVLSoundStartedNotification
object:nil queue:oq usingBlock:^(NSNotification *note) {
[[[self window] contentView] setNeedsDisplay:YES];
}];
soundStopObserver = [nc addObserverForName:(NSString*)kVLSoundStoppedNotification
object:nil queue:oq usingBlock:^(NSNotification *note) {
[[[self window] contentView] setNeedsDisplay:YES];
}];
} }
return self; return self;
} }
- (void) dealloc
{
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:soundStartObserver];
[nc removeObserver:soundStopObserver];
}
- (VLEditable *)editTarget - (VLEditable *)editTarget
{ {
return editTarget; return editTarget;
@ -100,11 +118,25 @@
[[self document] setPlayElements:[[self document] playElements] ^ [sender tag]]; [[self document] setPlayElements:[[self document] playElements] ^ [sender tag]];
} }
- (BOOL) validateMenuItem:(NSMenuItem *)menuItem - (BOOL) validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
{ {
if ([menuItem action] == @selector(togglePlayElements:)) if ([item action] == @selector(togglePlayElements:)) {
if (int tag = [menuItem tag]) NSMenuItem * menuItem = (NSMenuItem *)item;
if (int tag = [item tag])
[menuItem setState:([[self document] playElements] & tag) != 0]; [menuItem setState:([[self document] playElements] & tag) != 0];
} else if ([item action] == @selector(playStop:)) {
NSMenuItem * menuItem = [(NSObject *)item isKindOfClass:[NSMenuItem class]] ? (NSMenuItem *)item : nil;
NSToolbarItem * toolItem = menuItem ? nil : (NSToolbarItem *)item;
if (VLSoundOut::Instance()->Playing()) {
[menuItem setTitle:@"Stop"];
[toolItem setLabel:@"Stop"];
[toolItem setImage:[NSImage imageNamed:@"stop.icns"]];
} else {
[menuItem setTitle:@"Play"];
[toolItem setLabel:@"Play"];
[toolItem setImage:[NSImage imageNamed:@"play.icns"]];
}
}
return YES; return YES;
} }
@ -127,4 +159,35 @@
[pdfWin reloadPDF]; [pdfWin reloadPDF];
} }
- (IBAction) stop:(id)sender
{
VLSoundOut::Instance()->Stop();
}
- (IBAction) playStop:(id)sender
{
if (VLSoundOut::Instance()->Playing())
[self stop:sender];
else
[[self document] playSong];
}
- (IBAction) playMusic:(id)sender
{
switch ([sender tag]) {
case 1: // Fwd
VLSoundOut::Instance()->Fwd();
break;
case -1: // Rew
VLSoundOut::Instance()->Bck();
break;
}
}
- (IBAction) adjustTempo:(id)sender
{
[[self document] setSongTempo:[[self document] songTempo]+[sender tag]];
}
@end @end

View File

@ -5,7 +5,7 @@
// //
// (MN) Matthias Neeracher // (MN) Matthias Neeracher
// //
// Copyright © 2005-2007 Matthias Neeracher // Copyright © 2005-2011 Matthias Neeracher
// //
#include "VLSoundOut.h" #include "VLSoundOut.h"
@ -21,6 +21,9 @@
#define R(x) if (OSStatus r = (x)) fprintf(stderr, "%s -> %d\n", #x, r); #define R(x) if (OSStatus r = (x)) fprintf(stderr, "%s -> %d\n", #x, r);
CFStringRef kVLSoundStartedNotification = CFSTR("VLSoundStarted");
CFStringRef kVLSoundStoppedNotification = CFSTR("VLSoundStopped");
class VLAUSoundOut : public VLSoundOut { class VLAUSoundOut : public VLSoundOut {
public: public:
VLAUSoundOut(); VLAUSoundOut();
@ -36,23 +39,25 @@ public:
virtual void Bck(); virtual void Bck();
virtual ~VLAUSoundOut(); virtual ~VLAUSoundOut();
void PollMusic();
protected: protected:
VLAUSoundOut(bool fileOutput); VLAUSoundOut(bool fileOutput);
void InitSoundOutput(bool fileOutput); void InitSoundOutput(bool fileOutput);
virtual void SetupOutput(AUNode outputNode); virtual void SetupOutput(AUNode outputNode);
MusicTimeStamp SequenceLength(MusicSequence music); MusicTimeStamp SequenceLength(MusicSequence music);
void SkipTimeInterval(); void SkipTimeInterval();
AUGraph fGraph; AUGraph fGraph;
MusicPlayer fPlayer; MusicPlayer fPlayer;
private: private:
MusicSequence fMusic; MusicSequence fMusic;
MusicTimeStamp fMusicLength; MusicTimeStamp fMusicLength;
bool fRunning; bool fRunning;
bool fForward; bool fForward;
dispatch_source_t fMusicPoll;
void Play(const int8_t * note, size_t numNotes = 1); void Play(const int8_t * note, size_t numNotes = 1);
}; };
class VLAUFileSoundOut : public VLAUSoundOut { class VLAUFileSoundOut : public VLAUSoundOut {
@ -119,6 +124,12 @@ VLAUSoundOut::VLAUSoundOut()
: fMusic(0), fRunning(false), fForward(true) : fMusic(0), fRunning(false), fForward(true)
{ {
InitSoundOutput(false); InitSoundOutput(false);
fMusicPoll = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(fMusicPoll, ^{
this->PollMusic();
});
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_FOREVER, INT64_MAX, 1000*NSEC_PER_USEC);
dispatch_resume(fMusicPoll);
} }
VLAUSoundOut::VLAUSoundOut(bool) VLAUSoundOut::VLAUSoundOut(bool)
@ -128,8 +139,9 @@ VLAUSoundOut::VLAUSoundOut(bool)
VLAUSoundOut::~VLAUSoundOut() VLAUSoundOut::~VLAUSoundOut()
{ {
DisposeMusicPlayer(fPlayer);
Stop(false); Stop(false);
dispatch_release(fMusicPoll);
DisposeMusicPlayer(fPlayer);
DisposeAUGraph(fGraph); DisposeAUGraph(fGraph);
} }
@ -205,6 +217,9 @@ void VLAUSoundOut::PlaySequence(MusicSequence music)
R(MusicPlayerStart(fPlayer)); R(MusicPlayerStart(fPlayer));
fRunning = true; fRunning = true;
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), kVLSoundStartedNotification,
NULL, NULL, false);
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_NOW, 500*NSEC_PER_MSEC, 200*NSEC_PER_MSEC);
} }
void VLAUSoundOut::SetPlayRate(float rate) void VLAUSoundOut::SetPlayRate(float rate)
@ -266,6 +281,9 @@ void VLAUSoundOut::Stop(bool pause)
DisposeMusicSequence(fMusic); DisposeMusicSequence(fMusic);
fMusic = 0; fMusic = 0;
} }
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), kVLSoundStoppedNotification,
NULL, NULL, false);
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_FOREVER, INT64_MAX, 200*NSEC_PER_MSEC);
} }
bool VLAUSoundOut::Playing() bool VLAUSoundOut::Playing()
@ -280,6 +298,14 @@ bool VLAUSoundOut::AtEnd()
return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicLength; return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicLength;
} }
void VLAUSoundOut::PollMusic()
{
if (fRunning && AtEnd()) {
MusicPlayerSetTime(fPlayer, 0);
Stop(true);
}
}
void VLAUSoundOut::PlayNote(const VLNote & note) void VLAUSoundOut::PlayNote(const VLNote & note)
{ {
Play(&note.fPitch); Play(&note.fPitch);

View File

@ -5,13 +5,16 @@
// //
// (MN) Matthias Neeracher // (MN) Matthias Neeracher
// //
// Copyright © 2005-2008 Matthias Neeracher // Copyright © 2005-2011 Matthias Neeracher
// //
#include "VLModel.h" #include "VLModel.h"
#import <CoreFoundation/CoreFoundation.h> #import <CoreFoundation/CoreFoundation.h>
#include <AudioToolbox/AudioToolbox.h> #include <AudioToolbox/AudioToolbox.h>
extern CFStringRef kVLSoundStartedNotification;
extern CFStringRef kVLSoundStoppedNotification;
class VLSoundEvent { class VLSoundEvent {
protected: protected:
VLSoundEvent() {} VLSoundEvent() {}