Re: Sequencer project
Re: Sequencer project
- Subject: Re: Sequencer project
- From: Gregory Wieber <email@hidden>
- Date: Mon, 16 Aug 2010 09:22:06 -0700
I've been using time stamps in an audio unit render callback to count samples. Problem with that is that the callback processes 512 frames of audio at once; so it's not really accurate either.
The idea of a metronome has been discussed here at length, but I'd be curious as well what the correct solution is. I was considering adapting the same apple code project (metronome), but I wanted to get a better sense of it before tearing apart my timing code...
Regarding your problem, you'll have to switch to audio queues or audio unit graphs if you want accuracy -- av audio play won't get you there.
Cheers
Greg
Sent from my iPad
On Aug 15, 2010, at 9:18 AM, Patrick Muringer <email@hidden> wrote:
> 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
_______________________________________________
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