TAAE2 - Stop at time/Loop-duration

Hello! Just started playing around with the TAAE2 and really liking it so far! I have an idea that I want to try out but can't figure out what the best way to do it is. What I want to do is basically to queue loops and let them play together synchronized with other loops. The thing is that I want to be able to set the regionDuration of an AEAudioFilePlayerModule to less than the entire loop but I want it to loop at it's original length, so instead of the AEAudioFilePlayerModule restarting at the regionDuration which for example could be 2 I would like to use a property that is called "loopDuration" or something that would determine how long the loop should be.

Another solution that I thought about would be to implement some sort of "stopAtTimeAndRestart:" and then in that function stop the module and reschedule it with the "playAtTime".

Does any of these approaches seem reasonable?

Best,
J

Comments

  • Hey @triplej,

    Great!

    Hmm... I think the most frictionless way to solve this is to use AEAudioFilePlayerModule's completionBlock. For any players with a regionDuration less than the target loop length, set loop to NO, then set completionBlock to a block that will reschedule the player at the next interval.

  • edited May 2016

    Thanks!

    I tried this out but I couldn't get the file player to call the block a second time. Probably doing something wrong.. Right now it loops but only the first time.

    Edit: Got it working now! It does however come with some pops and clicks every time it loops.

    /J

  • Pops and clicks? That doesn't sound right...can you reproduce that within the TAAE sample app? Are you using the latest version from Github?

  • edited May 2016

    Wasn't using the latest version but updated now, still the same thing. Seems like it still is clicks at (almost) every restart/end of the loop. Depending on what loop it is. I thought it might have hade something to do with the microfadeFrames and increased them a lot without noticing any difference. Here's what I'm doing, based on the code from the sample app.

    - (void)playPiano {
    
        NSURL * url = [[NSBundle mainBundle] URLForResource:@"piano" withExtension:@"m4a"];
        if ( [[NSFileManager defaultManager] fileExistsAtPath:url.path] ) {
    
            // Start player
            AEAudioFilePlayerModule * player =
            [[AEAudioFilePlayerModule alloc] initWithRenderer:self.theSubrenderer URL:url error:NULL];
            player.regionDuration = player.duration/8;
            player.regionStartTime = player.duration/2;
            player.microfadeFrames = 32;
            player.loop = NO;
            if ( !player ) return;
    
            // Make player available to audio renderer
            self.playerValue.objectValue = player;
            __weak AEAudioController * weakSelf = self;
            player.completionBlock = ^{
    
                //Restart when playback ends
                [weakSelf playPiano];
    
            };
    
            // Go
            [player playAtTime:[self nextSyncTimeForPlayer:player]+AEHostTicksFromSeconds(2)];
        }
    }
    
  • Ah! Right you are, I found a bug. Try the latest version, now.

  • Genious! Thanks a lot!

    I realized that a lot of the clicks were also due to this: Warning: render took 0.011809s, 406.8541% of buffer duration.

    Do you have any idea on what I might be doing wrong to cause this? This is the render-function:

    AEVarispeedModule * varispeed = [[AEVarispeedModule alloc] initWithRenderer:renderer subrenderer:subrenderer];
        subrenderer.block = ^(const AERenderContext * _Nonnull context) {
            // Run all the players, though the aggregator
            AEModuleProcess(aggregator, context);
    
            // See if we have an active player
            __unsafe_unretained AEAudioFilePlayerModule * player
            = (__bridge AEAudioFilePlayerModule *)AEManagedValueGetValue(playerValue);
    
            // Play recorded file, if playing
            if ( player ) {
                // Play
                AEModuleProcess(player, context);
    
                // Put on output
                //AERenderContextOutput(context, 1);
            }
    
            AERenderContextOutput(context, 0);
        };
    
        self.varispeed = varispeed;
        // Setup top-level renderer. This is all performed on the audio thread, so the usual
        // rules apply: No holding locks, no memory allocation, no Objective-C/Swift code.
        renderer.block = ^(const AERenderContext * _Nonnull context) {
    
            // Run varispeed unit, which will run its own render loop, above
            AEModuleProcess(varispeed, context);
    
            // Put the resulting buffer on the output
            AERenderContextOutput(context, 1);
        };
    
  • edited May 2016

    Are you still seeing that behaviour? It was occurring because of the same bug, but that should be fixed now.

    Edit: Actually, now I remember, it wasn't that bug at all, but a problem with your code; this:

        [player playAtTime:[self nextSyncTimeForPlayer:player]+AEHostTicksFromSeconds(2)];
    

    The sample app's nextSyncTimeForPlayer function can return zero, so you're specifying an invalid host time by then adding 2 seconds to that (absolute) timestamp. You should see warnings in the console about that.

  • edited May 2016

    @Michael thanks for your help!

    I've been trying to figure out where the problem lies. I no longer get the warning (removed the seconds) and the clipping has become a lot less apparent with the microfades. But it's still there quite often and when there should be no clipping (like at the start of a non clipping audio file).

    I could go with really long fades but that doesn't feel like the main problem. Do you have any idea where I should start troubleshooting?

    Now I call the player with: [player playAtTime:[self timeForSyncTime:self.firstSyncTime]

    and then that function looks like this:

    - (AEHostTicks)timeForSyncTime:(AESeconds)syncTimeInEigths {
        AEHostTicks now = AECurrentTimeInHostTicks();
    
        //Identify time-keeper
        AEAudioFilePlayerModule * timekeeper = self.timekeeperPlayer;
    
        if ( timekeeper ) {
            // Determine sync interval
            AESeconds oneBarInterval = (self.timekeeperPlayer.duration/4)*_numberOfBars;
    
            // Work out how far into this interal the timekeeper is
            AESeconds timeIntoBarInterval = fmod(AEAudioFilePlayerModuleGetPlayhead(timekeeper, now), oneBarInterval);
    
            AESeconds nextSyncTime = (((self.timekeeperPlayer.duration/4)/8)*syncTimeInEigths) - timeIntoBarInterval;
    
            if (nextSyncTime<0) {
                nextSyncTime+=oneBarInterval;
            }
    
            // Calculate time to next interval
            AEHostTicks nextIntervalTime
            = now + AEHostTicksFromSeconds(nextSyncTime) / self.varispeed.playbackRate;
    
            return nextIntervalTime;
        }
    
        return 0;
    }
    
  • Hm. I was mistaken, the warnings are still there but only appear when the app is run on a device.

    "Warning: render took 0.003463s, 119.3284% of buffer duration."

    I'm 100% sure that I'm doing something wrong here cause the players that just loop using the "player.loop" works as a charm.

  • Hmmm.. I think I might need to see a sample app to debug further. Would you perhaps bundle your changes that're causing problems into the TAAE sample app and send me a copy? This is still my problem, as even if you're doing something wrong, TAAE should be seeing that and warning you =)

Sign In or Register to comment.