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

View File

@ -68,7 +68,7 @@
@implementation VLDocument
@synthesize playElements;
@synthesize songTempo, playElements;
+ (BOOL)autosavesInPlace
{
@ -87,7 +87,7 @@
songArranger = @"";
songGroove = @"Swing";
playElements = kVLPlayAccompaniment|kVLPlayMelody|kVLPlayCountIn;
songTempo = [[NSNumber alloc] initWithInt:120];
songTempo = 120.0f;
baseTempo = 120.0f;
chordSize = 6.0f;
lyricSize = 0.0f;
@ -142,7 +142,7 @@
- (void) dealloc
{
VLSoundOut::Instance()->Stop();
VLSoundOut::Instance()->Stop(false);
delete song;
@ -237,13 +237,12 @@
[self didChangeSong];
}
- (void) setSongTempo:(int)tempo
- (void) setSongTempo:(float)tempo
{
if (tempo == [songTempo intValue])
if (tempo == songTempo)
return;
[songTempo autorelease];
songTempo = [[NSNumber numberWithInt:tempo] retain];
songTempo = tempo;
if (VLSoundOut::Instance()->Playing())
VLSoundOut::Instance()->SetPlayRate(playRate*tempo/baseTempo);
}
@ -466,7 +465,7 @@
return task;
}
- (IBAction) play:(id)sender
- (void) playSong
{
if (hasMusicSequence) {
VLSoundOut::Instance()->PlaySequence(NULL);
@ -492,7 +491,7 @@
hasMusicSequence = true;
[sheetWin willPlaySequence:music];
baseTempo = [songTempo floatValue];
baseTempo = songTempo;
VLSoundOut::Instance()->SetPlayRate(playRate);
VLSoundOut::Instance()->PlaySequence(music);
}
@ -507,7 +506,7 @@
songGroove = groove;
previewRange = sections;
playElements |= kVLPlayGroovePreview;
[self play:groove];
[self playSong];
playElements &= ~kVLPlayGroovePreview;
songGroove = savedGroove;
[validTmpFiles removeObjectForKey:@"mma"];
@ -515,49 +514,6 @@
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
error:(NSError **)outError
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,6 +12,7 @@
#import "VLDocument.h"
#import "VLPDFWindow.h"
#import "VLLogWindow.h"
#import "VLSoundOut.h"
@implementation VLEditable
@ -51,10 +52,27 @@
{
if (self = [super initWithWindow:window]) {
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;
}
- (void) dealloc
{
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:soundStartObserver];
[nc removeObserver:soundStopObserver];
}
- (VLEditable *)editTarget
{
return editTarget;
@ -100,11 +118,25 @@
[[self document] setPlayElements:[[self document] playElements] ^ [sender tag]];
}
- (BOOL) validateMenuItem:(NSMenuItem *)menuItem
- (BOOL) validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
{
if ([menuItem action] == @selector(togglePlayElements:))
if (int tag = [menuItem tag])
if ([item action] == @selector(togglePlayElements:)) {
NSMenuItem * menuItem = (NSMenuItem *)item;
if (int tag = [item tag])
[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;
}
@ -127,4 +159,35 @@
[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

View File

@ -5,7 +5,7 @@
//
// (MN) Matthias Neeracher
//
// Copyright © 2005-2007 Matthias Neeracher
// Copyright © 2005-2011 Matthias Neeracher
//
#include "VLSoundOut.h"
@ -21,6 +21,9 @@
#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 {
public:
VLAUSoundOut();
@ -36,23 +39,25 @@ public:
virtual void Bck();
virtual ~VLAUSoundOut();
void PollMusic();
protected:
VLAUSoundOut(bool fileOutput);
void InitSoundOutput(bool fileOutput);
virtual void SetupOutput(AUNode outputNode);
MusicTimeStamp SequenceLength(MusicSequence music);
MusicTimeStamp SequenceLength(MusicSequence music);
void SkipTimeInterval();
AUGraph fGraph;
MusicPlayer fPlayer;
AUGraph fGraph;
MusicPlayer fPlayer;
private:
MusicSequence fMusic;
MusicTimeStamp fMusicLength;
bool fRunning;
bool fForward;
MusicSequence fMusic;
MusicTimeStamp fMusicLength;
bool fRunning;
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 {
@ -119,6 +124,12 @@ VLAUSoundOut::VLAUSoundOut()
: fMusic(0), fRunning(false), fForward(true)
{
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)
@ -128,8 +139,9 @@ VLAUSoundOut::VLAUSoundOut(bool)
VLAUSoundOut::~VLAUSoundOut()
{
DisposeMusicPlayer(fPlayer);
Stop(false);
dispatch_release(fMusicPoll);
DisposeMusicPlayer(fPlayer);
DisposeAUGraph(fGraph);
}
@ -205,6 +217,9 @@ void VLAUSoundOut::PlaySequence(MusicSequence music)
R(MusicPlayerStart(fPlayer));
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)
@ -266,6 +281,9 @@ void VLAUSoundOut::Stop(bool pause)
DisposeMusicSequence(fMusic);
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()
@ -280,6 +298,14 @@ bool VLAUSoundOut::AtEnd()
return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicLength;
}
void VLAUSoundOut::PollMusic()
{
if (fRunning && AtEnd()) {
MusicPlayerSetTime(fPlayer, 0);
Stop(true);
}
}
void VLAUSoundOut::PlayNote(const VLNote & note)
{
Play(&note.fPitch);

View File

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