Sequencer project
Sequencer project
- Subject: Sequencer project
- From: Patrick Muringer <email@hidden>
- Date: Sun, 15 Aug 2010 18:18:35 +0200
- Thread-topic: Sequencer project
Hi,
I'm at the early stage of my project which aim is to develop a very simple
audio step sequencer.
I searched for a very long time on the Internet to get a rough idea of the
algorithm of such an app but, I was not able to find nothing.
I red the CoreAudio documentation but honestly it didn¹t help me so far.
As a first start, I used the metronome code sample, to start having a
"timed machine"
I tried to run my first attempt on the simulator (iphone) which gives
great results (even if, starting two AVAudioPlay one after the other,
hoping to have simultaneous play, I can ear some delay between them (in my
case, a bass drum and a clap).
When I launched the app on the iphone (3GS, 3.1.3) I noticed that the bpm
was not respected as the app running on the device was much slower than
the one running on the simulator. I wonder therefore if my starting point
is the correct one as timing accuracy is a must for a sequencer.
I wonder if you, masters of audio, could tell me if I'm going on the right
direction. If not, what would you suggest?
As stated at the beginning, this is my fisrt attempt, so please consider
me as a real newbee for that :)
Thanks if anyone wish to advise/help.
Patrick
------------------------------------------------
Bellow is the modified metronomeview class.
#import "MetronomeView.h"
#import "MetronomeAppDelegate.h"
#define kDisplacementAngle 20
#define kMaxBPM 510
#define kMinBPM 1
#define kDefaultBPM 180
#define kGridQuantize 4
// Setter is private, so redeclare property here in a class extension.
@interface MetronomeView ()
@property CGFloat duration;
@end
// Private interface, methods used only internally.
@interface MetronomeView (PrivateMethods)
@property CGFloat duration;
- (void)stopArm:(id)sender;
- (void)stopSoundAndArm;
- (void)stopDriverThread;
- (void)startSoundAndAnimateArmToRight:(BOOL)startToRight;
- (void)updateWeightPosition;
- (void)dragWeightByYDisplacement:(CGFloat)yDisplacement;
- (void)updateBPM;
- (void)rotateArmToDegree:(CGFloat)positionInDegrees;
@end
@implementation MetronomeView
@synthesize beatNumber, duration, metronomeViewController, tickPlayer,
tockPlayer, BDPlayer, SNPlayer, OHPlayer, CHPlayer, soundPlayerThread;
#pragma mark -
#pragma mark === Initialization and dealloc ===
#pragma mark -
// Initialize the view and sounds.
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Set up default state
armIsAnimating = NO;
tempoChangeInProgress = NO;
self.bpm = kDefaultBPM;
self.beatNumber = 0.0;
/*
Set up sounds and views.
*/
// Create and prepare audio players for tick and tock sounds
NSBundle *mainBundle = [NSBundle mainBundle];
NSError *error;
NSURL *tickURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"tick" ofType:@"caf"]];
tickPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:tickURL
error:&error];
if (!tickPlayer) {
NSLog(@"no tickPlayer: %@", [error localizedDescription]);
}
[tickPlayer prepareToPlay];
NSURL *tockURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"tock" ofType:@"caf"]];
tockPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:tockURL
error:&error];
if (!tockPlayer) {
NSLog(@"no tockPlayer: %@", [error localizedDescription]);
}
[tockPlayer prepareToPlay];
NSURL *BDURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"Kick" ofType:@"aif"]];
BDPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:BDURL
error:&error];
if (!BDPlayer) {
NSLog(@"no tockPlayer: %@", [error localizedDescription]);
}
[BDPlayer prepareToPlay];
NSURL *SNURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"Snare" ofType:@"aif"]];
SNPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:SNURL
error:&error];
if (!SNPlayer) {
NSLog(@"no tockPlayer: %@", [error localizedDescription]);
}
[SNPlayer prepareToPlay];
NSURL *OHURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"OpenHat" ofType:@"aif"]];
OHPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:OHURL
error:&error];
if (!OHPlayer) {
NSLog(@"no tockPlayer: %@", [error localizedDescription]);
}
[OHPlayer prepareToPlay];
NSURL *CHURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:@"ClosedHat" ofType:@"aif"]];
CHPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:CHURL
error:&error];
if (!CHPlayer) {
NSLog(@"no tockPlayer: %@", [error localizedDescription]);
}
[CHPlayer prepareToPlay];
tempoDisplay = [[UILabel alloc] initWithFrame:CGRectMake(135, 10, 50,
18)];
tempoDisplay.font = [UIFont boldSystemFontOfSize:24.0];
tempoDisplay.backgroundColor = [UIColor clearColor];
tempoDisplay.textColor = [UIColor whiteColor];
tempoDisplay.textAlignment = UITextAlignmentCenter;
tempoDisplay.text = [NSString stringWithFormat:@"%d", self.bpm];
beatNumberDisplay = [[UILabel alloc] initWithFrame:CGRectMake(110, 30,
100, 18)];
beatNumberDisplay.font = [UIFont boldSystemFontOfSize:24.0];
beatNumberDisplay.backgroundColor = [UIColor clearColor];
beatNumberDisplay.textColor = [UIColor whiteColor];
beatNumberDisplay.textAlignment = UITextAlignmentCenter;
beatNumberDisplay.text = [NSString stringWithFormat:@"%.2f",
self.beatNumber];
// Add 'i' button
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
infoButton.frame = CGRectMake(256, 432, 44, 44);
[infoButton addTarget:self action:@selector(showInfo)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:tempoDisplay];
[self addSubview:beatNumberDisplay];
[self addSubview:infoButton];
[self startDriverThread];
}
return self;
}
- (void)dealloc {
[tempoDisplay release];
[tickPlayer release];
[tockPlayer release];
[BDPlayer release];
[super dealloc];
}
- (void)updateBPMInfo {
tempoDisplay.text = [NSString stringWithFormat:@"%d", self.bpm];
NSLog(@"bpm = %d", self.bpm);
}
- (void)updatebeatNumberInfo {
if (self.beatNumber < 4) beatNumberDisplay.text = [NSString
stringWithFormat:@"%.2f", self.beatNumber];
NSLog(@"beatNumber = ?", self.beatNumber);
}
#pragma mark -
#pragma mark === Actions ===
#pragma mark -
- (void)playSound {
int intPart;
float decPart;
intPart = (int)self.beatNumber;
decPart = self.beatNumber - intPart;
MetronomeAppDelegate *appDelegate = (MetronomeAppDelegate
*)[[UIApplication sharedApplication] delegate];
if (((intPart == 1) || (intPart == 3)) && (!decPart)) {
//[SNPlayer play];
//[CHPlayer play];
}
if (!decPart) {
[BDPlayer play];
//[CHPlayer play];
}
if (decPart == 0.50) {
//[OHPlayer play];
}
if (self.beatNumber == [appDelegate timeSignature]) {
self.beatNumber = 0;
}
self.beatNumber += (float)1/kGridQuantize;
[self performSelectorOnMainThread:@selector(updateBPMInfo) withObject:nil
waitUntilDone:NO];
[self performSelectorOnMainThread:@selector(updatebeatNumberInfo)
withObject:nil waitUntilDone:NO];
}
// This method is invoked from the driver thread
- (void)startDriverTimer:(id)info {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Give the sound thread high priority to keep the timing steady.
[NSThread setThreadPriority:1.0];
BOOL continuePlaying = YES;
while (continuePlaying) { // Loop until cancelled.
// An autorelease pool to prevent the build-up of temporary
objects.
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
[self playSound];
//[self
performSelectorOnMainThread:@selector(animateArmToOppositeExtreme)
withObject:nil waitUntilDone:NO];
NSDate *curtainTime = [[NSDate alloc]
initWithTimeIntervalSinceNow:self.duration];
NSDate *currentTime = [[NSDate alloc] init];
// Wake up periodically to see if we've been cancelled.
while (continuePlaying && ([currentTime compare:curtainTime] !=
NSOrderedDescending)) {
if ([soundPlayerThread isCancelled] == YES) {
continuePlaying = NO;
}
[NSThread sleepForTimeInterval:0.01];
[currentTime release];
currentTime = [[NSDate alloc] init];
}
[curtainTime release];
[currentTime release];
[loopPool drain];
}
[pool drain];
}
- (void)waitForSoundDriverThreadToFinish {
while (soundPlayerThread && ![soundPlayerThread isFinished]) { // Wait
for the thread to finish.
[NSThread sleepForTimeInterval:0.01];
}
}
- (void)startDriverThread {
if (soundPlayerThread != nil) {
[soundPlayerThread cancel];
[self waitForSoundDriverThreadToFinish];
}
NSThread *driverThread = [[NSThread alloc] initWithTarget:self
selector:@selector(startDriverTimer:) object:nil];
self.soundPlayerThread = driverThread;
[driverThread release];
[self.soundPlayerThread start];
}
- (void)stopDriverThread {
[self.soundPlayerThread cancel];
[self waitForSoundDriverThreadToFinish];
self.soundPlayerThread = nil;
}
- (void)showInfo {
[metronomeViewController showInfo];
}
#pragma mark -
#pragma mark === bpm ===
#pragma mark -
- (NSUInteger)bpm {
return lrint(ceil(60.0 / (kGridQuantize * self.duration)));
}
- (void)setBpm:(NSUInteger)bpm {
if (bpm >= kMaxBPM) {
bpm = kMaxBPM;
} else if (bpm <= kMinBPM) {
bpm = kMinBPM;
}
self.duration = (60.0 / bpm / kGridQuantize);
}
@end
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden