AutoPocket: New Beat Detective clone for Reaper (demo video)

Colonel_claypoo, there shouldn't be phase issues as all tracks are cut at the same places and stretched by the same amount.
 
thanks a lot adam for what you've done for us.

in your video, which is very helpful by the way, when you play back the quantized song i think i can hear a glitch (13.46min). i hope it's not my hardware that produces the pop:zombie:

however, when i tried your method i sometimes got those pops too and i was wondering what could be done about them. i mean, we already have crossfades on the splits so what else can we do?

thanks

edit:

uh and more thing.
i don't know much about quantizing and beatdetective and slip editing and that stuff just yet.
nonetheless, aren't we facing phase problems here? if we cut transients of a single hit that is present in more than one mics because of bleed in different places differently on each microphone and quantizing it, shouldn't there be phase problems?

i hope you know what i'm saying.


Here's the resulting mp3:

http://dl.dropbox.com/u/3616293/drumeditresult.mp3

I heard the glitch in the video too, think it was just a playback error or something, the mp3 sounds pretty good, nothing I really notice that easily!

You shouldn't have any phase issues, that's the whole point of grouping everything. Exact same way it works in Beat Detective.

As far as avoiding any glitches, experiment with the trigger pad and crossfade length values, and if that doesn't help, just find the parts where you hear a glitch and tweak it manually until it sounds ok. No automatic process is ever going to be 100% perfect, but it does do 95% of the grunt work for you at least!
 
I'm kinda curious what the C++ code for something like this looks like....

;)

Code:
void AWFillGapsAdv(COMMAND_T* t)
{
    // Set up dialog info
    const char names[] = "Trigger Pad (ms),Crossfade Length (ms),Maximum Gap (ms),Maximum Stretch (0.5 is double),Preserve Transient (ms),Transient Crossfade Length (ms),Fade Shape (0 = linear),Mark possible artifacts? (0/1)";
    static char defaultValues[100] = "5,5,15,0.5,35,5,0,1";
    char retVals[100];
    char defValsBackup[100];
    int maxReturnLen = 100;
    int nitems = 8;
    bool runScript = 0;
    
    // Copy defaultValues into returnedValues (retains values in case user hits cancel)
    strcpy(retVals, defaultValues);
    strcpy(defValsBackup, defaultValues);
    
    // If 0 is passed, call dialog and get values, if 1 is passed, just use last settings
    if (t->user == 0)
    {
        // Call dialog, use retVals so defaultValues doesn't get miffed up
        runScript = GetUserInputs("Advanced Item Smoothing",nitems,names,retVals,maxReturnLen);
    }
    else if (t->user == 1)
    {
        runScript = 1;
    }
    
    // If user hit okay, get the parameters they entered
    if (runScript)    
    {
        // If user didn't hit cancel, copy the returned values back into the default values before ruining retVals with evil strtok
        strcpy(defaultValues, retVals);
        
        // Divided by 1000 to convert to milliseconds except maxStretch
        double triggerPad = (atof(strtok(retVals, ",")))/1000;
        double fadeLength = (atof(strtok(NULL, ",")))/1000;
        double maxGap = (atof(strtok(NULL, ",")))/1000;
        double maxStretch = atof(strtok(NULL, ","));
        double presTrans = (atof(strtok(NULL, ",")))/1000;
        double transFade = (atof(strtok(NULL, ",")))/1000;
        int fadeShape = atoi(strtok(NULL, ","));
        int markErrors = atoi(strtok(NULL, ","));
    
        if ((triggerPad < 0) || (fadeLength < 0) || (maxGap < 0) || (maxStretch < 0) || (maxStretch > 1) || (presTrans < 0) || (transFade < 0) || (fadeShape < 0) || (fadeShape > 5))
        {
            strcpy(defaultValues, defValsBackup);
            
            //ShowMessageBox("Don't use such stupid values, try again.","Invalid Input",0);
            MessageBox(g_hwndParent, "All values must be non-negative, Maximum Stretch must be a value from 0 to 1 and Fade Shape must be a value from 0 to 5.", "Item Smoothing Input Error", MB_OK);
            return;
        }
        
        
        
        // Run loop for every track in project
        for (int trackIndex = 0; trackIndex < GetNumTracks(); trackIndex++)
        {
            
            // Gets current track in loop
            MediaTrack* track = GetTrack(0, trackIndex);
            
            // Run loop for every item on track
            
            int itemCount = GetTrackNumMediaItems(track);
            
            for (int itemIndex = 0; itemIndex < (itemCount - 1); itemIndex++)
            {
                
                MediaItem* item1 = GetTrackMediaItem(track, itemIndex);
                MediaItem* item2 = GetTrackMediaItem(track, itemIndex + 1);
                // errorFlag = 0;
                
                // BEGIN TIMESTRETCH CODE---------------------------------------------------------------
                
                // If stretching is enabled
                if (maxStretch < 1)
                {
                
                    // Only stretch if both items are selected, avoids stretching unselected items or the last selected item
                    if (GetMediaItemInfo_Value(item1, "B_UISEL") && GetMediaItemInfo_Value(item2, "B_UISEL"))
                    {
                        
                        // Get various pieces of info for the 2 items
                        double item1Start = GetMediaItemInfo_Value(item1, "D_POSITION");
                        double item1Length = GetMediaItemInfo_Value(item1, "D_LENGTH");
                        double item1End = item1Start + item1Length;
                
                        double item2Start = GetMediaItemInfo_Value(item2, "D_POSITION");
                
                        // Calculate gap between items
                        double gap = item2Start - item1End;
                        
                        // If gap is bigger than the max allowable gap
                        if (gap > maxGap)
                        {
                            
                            // If preserve transient is enabled, split item at preserve point
                            if (presTrans > 0)
                            {
                            
                                // Get snap offset for item1
                                double item1SnapOffset = GetMediaItemInfo_Value(item1, "D_SNAPOFFSET");
                                
                                // Calculate position of item1 transient
                                double item1TransPos = item1Start + item1SnapOffset;
                                
                                // Split point is (presTrans) after the transient point
                                // Subtract fade length because easier to change item end then item start
                                // (don't need take loop to adjust all start offsets)
                                double splitPoint = item1TransPos + presTrans - transFade;
                                
                                // Check for default item fades
                                double defItemFades = *(double*)(GetConfigVar("deffadelen"));
                                bool fadeFlag = 0;
                                
                                if (defItemFades > 0)
                                {
                                    fadeFlag = 1;
                                    Main_OnCommand(41194,0);
                                }
                                
                                // Split item1 at the split point
                                MediaItem* item1B = SplitMediaItem(item1, splitPoint);
                                
                                // Revert item fades
                                if (fadeFlag)
                                {
                                    Main_OnCommand(41194,0);
                                }
                                
                                // Get new item1 length after split
                                item1Length = GetMediaItemInfo_Value(item1, "D_LENGTH") + transFade;
                                
                                // Create overlap of 'transFade'
                                SetMediaItemInfo_Value(item1, "D_LENGTH", item1Length);
                                
                                // Crossfade the overlap
                                SetMediaItemInfo_Value(item1, "D_FADEOUTLEN_AUTO", transFade);
                                SetMediaItemInfo_Value(item1B, "D_FADEINLEN_AUTO", transFade);
                                
                                // Set Fade Shapes
                                SetMediaItemInfo_Value(item1, "C_FADEOUTSHAPE", fadeShape);
                                SetMediaItemInfo_Value(item1B, "C_FADEINSHAPE", fadeShape);
                                
                                // Set the stretched half to be item 1 so loop continues properly
                                item1 = item1B;
                                
                                // Get item1 info since item1 is now item1B
                                item1Start = GetMediaItemInfo_Value(item1, "D_POSITION");
                                item1Length = GetMediaItemInfo_Value(item1, "D_LENGTH");
                                item1End = item1Start + item1Length;
                                
                                // Increase itemIndex and itemCount since split added item
                                itemIndex += 1;
                                itemCount += 1;
                            }
                            
                            // Calculate distance between item1Start and the beginning of the maximum allowable gap
                            double item1StartToGap = (item2Start - maxGap) - item1Start;
                            
                            // Calculate amount to stretch
                            double stretchPercent = (item1Length/(item1StartToGap));
                            
                            if (stretchPercent < maxStretch)
                            {
                                stretchPercent = maxStretch;
                                
                            }
                            
                            item1Length *= (1/stretchPercent);
                            
                            SetMediaItemInfo_Value(item1, "D_LENGTH", item1Length);    
                            
                            for (int takeIndex = 0; takeIndex < GetMediaItemNumTakes(item1); takeIndex++)
                            {
                                MediaItem_Take* currentTake = GetMediaItemTake(item1, takeIndex);
                                SetMediaItemTakeInfo_Value(currentTake, "D_PLAYRATE", stretchPercent);
                            }
                        }    
                    }    
                }
                
                // END TIME STRETCH CODE ---------------------------------------------------------------
            
                // BEGIN FIX OVERLAP CODE --------------------------------------------------------------
                
                // If first item is selected selected, run this loop
                // if (GetMediaItemInfo_Value(item1, "B_UISEL"))
                if (GetMediaItemInfo_Value(item1, "B_UISEL"))
                {
                    // Get various pieces of info for the 2 items
                    double item1Start = GetMediaItemInfo_Value(item1, "D_POSITION");
                    double item1Length = GetMediaItemInfo_Value(item1, "D_LENGTH");
                    double item1End = item1Start + item1Length;
                
                    double item2Start = GetMediaItemInfo_Value(item2, "D_POSITION");
                
                    // If the first item overlaps the second item, trim the first item
                    if (item1End > (item2Start - triggerPad))
                    {
                        
                        // If both items selected, account for trigger pad, if not, just trim to item 2 start
                        if (GetMediaItemInfo_Value(item1, "B_UISEL") && GetMediaItemInfo_Value(item2, "B_UISEL"))
                        {
                            item1Length = item2Start - item1Start - triggerPad;
                        }
                        else
                        {
                            item1Length = item2Start - item1Start;
                        }
                        
                        SetMediaItemInfo_Value(item1, "D_LENGTH", item1Length);
                    }
                    
                    if (item1End <= (item2Start - triggerPad))
                    {
                        // If both items selected, account for trigger pad, if not, do nothing
                        if (GetMediaItemInfo_Value(item1, "B_UISEL") && GetMediaItemInfo_Value(item2, "B_UISEL"))
                        {
                            item1Length -= triggerPad;
                            SetMediaItemInfo_Value(item1, "D_LENGTH", item1Length);
                        }
                    }
                }
                
                // END FIX OVERLAP CODE ----------------------------------------------------------------
                
                // BEGIN FILL GAPS CODE ----------------------------------------------------------------
                
                // If both items selected, run this loop
                if (GetMediaItemInfo_Value(item1, "B_UISEL") && GetMediaItemInfo_Value(item2, "B_UISEL"))
                { 
                
                    double item1Start = GetMediaItemInfo_Value(item1, "D_POSITION");
                    double item1Length = GetMediaItemInfo_Value(item1, "D_LENGTH");
                    double item1End = item1Start + item1Length;
                
                    double item2Start = GetMediaItemInfo_Value(item2, "D_POSITION");
                    double item2Length = GetMediaItemInfo_Value(item2, "D_LENGTH");
                    double item2SnapOffset = GetMediaItemInfo_Value(item2, "D_SNAPOFFSET");
                    
                    
            
                    // If there is a gap, fill it and also add an overlap to the left that is "fadeLength" long
                    if (item2Start >= item1End)
                    {
                        double item2StartDiff = item2Start - item1End + fadeLength;
                        item2Length += item2StartDiff;
                        
                        // Calculate gap between items
                        double gap = item2Start - item1End;
                        
                        // Check to see if gap is bigger than maxGap, even after time stretching
                        // Make sure to account for triggerPad since it is subtracted before this point and shouldn't count towards the gap
                        if (gap > (maxGap + triggerPad))
                        {
                            // If gap is big and mark errors is enabled, add a marker
                            if (markErrors == 1)
                            {
                            AddProjectMarker(NULL, false, item1End, NULL, "Possible Artifact", NULL);
                            }
                            
                        }
                            
                        
                        // Adjust start offset for all takes in item2 to account for fill
                        for (int takeIndex = 0; takeIndex < GetMediaItemNumTakes(item2); takeIndex++)
                            {
                                MediaItem_Take* currentTake = GetMediaItemTake(item2, takeIndex);
                                double startOffset = GetMediaItemTakeInfo_Value(currentTake, "D_STARTOFFS");
                                startOffset -= item2StartDiff;
                                SetMediaItemTakeInfo_Value(currentTake, "D_STARTOFFS", startOffset);
                            }
                            
                        // Finally trim the item to fill the gap and adjust the snap offset
                        SetMediaItemInfo_Value(item2, "D_POSITION", (item1End - fadeLength));
                        SetMediaItemInfo_Value(item2, "D_LENGTH", item2Length);
                        SetMediaItemInfo_Value(item2, "D_SNAPOFFSET", (item2SnapOffset + item2StartDiff));
                        
                        // Crossfade the overlap between the two items
                        SetMediaItemInfo_Value(item1, "D_FADEOUTLEN_AUTO", fadeLength);
                        SetMediaItemInfo_Value(item2, "D_FADEINLEN_AUTO", fadeLength);
                        
                        // Set Fade Shapes
                        SetMediaItemInfo_Value(item1, "C_FADEOUTSHAPE", fadeShape);
                        SetMediaItemInfo_Value(item2, "C_FADEINSHAPE", fadeShape);
                    }
                }
                
                // END FILL GAPS CODE ------------------------------------------------------------------
                
            }
            
        }
    }
    
    
    UpdateTimeline();
    Undo_OnStateChangeEx(SWSAW_CMD_SHORTNAME(t), UNDO_STATE_ITEMS | UNDO_STATE_MISCCFG, -1);
}
 
Late to the party, but very awesome stuff indeed. Out of curiosity Adam, are there specific applications where you prefer this method to slip editing? So far I have found that slip editing rocks with drums, bass, even guitars sometimes. I used to have such a hard time with beat detective that I just can't see a reason to use this, no offense :devil:
 
Late to the party, but very awesome stuff indeed. Out of curiosity Adam, are there specific applications where you prefer this method to slip editing? So far I have found that slip editing rocks with drums, bass, even guitars sometimes. I used to have such a hard time with beat detective that I just can't see a reason to use this, no offense :devil:

I use both! I use this anytime there is a decent sized section with no kick+snare at the same time, where the drummer was reasonably on with the click and where the part isn't tech'd out or too fast. It is WAY faster than slip editing in those cases. But for tricky or fast stuff, or stuff where there's a lot of kick + some other drum hitting at the same time, slip editing is less prone to error since automatic processes have a harder time dealing with those scenarios.
 
I use both! I use this anytime there is a decent sized section with no kick+snare at the same time, where the drummer was reasonably on with the click and where the part isn't tech'd out or too fast. It is WAY faster than slip editing in those cases. But for tricky or fast stuff, or stuff where there's a lot of kick + some other drum hitting at the same time, slip editing is less prone to error since automatic processes have a harder time dealing with those scenarios.

That's generally what I was thinking but I rarely come across what's in the bold :bah:. I recently recorded a band playing together all at once no click and your slip editing video literally saved my project. You da man! :Spin:
 
I use both! I use this anytime there is a decent sized section with no kick+snare at the same time, where the drummer was reasonably on with the click and where the part isn't tech'd out or too fast. It is WAY faster than slip editing in those cases. But for tricky or fast stuff, or stuff where there's a lot of kick + some other drum hitting at the same time, slip editing is less prone to error since automatic processes have a harder time dealing with those scenarios.

interesting. could you tell us why slip editing is preferred to autopocket when two hits hit at the same time? i mean in almost all genres the hihat and the kick are on the same hit so what is different with kick and snare?

one more thing i'd like to know is which drum tracks are going to be included in the stereo stem?
only the close mics?
i think in your video you didn't trigger the blip sound on overheads or room mics if you have had any which would be a little difficult to do.

thanks
 
interesting. could you tell us why slip editing is preferred to autopocket when two hits hit at the same time? i mean in almost all genres the hihat and the kick are on the same hit so what is different with kick and snare?

one more thing i'd like to know is which drum tracks are going to be included in the stereo stem?
only the close mics?
i think in your video you didn't trigger the blip sound on overheads or room mics if you have had any which would be a little difficult to do.

thanks

When most people hit the kick and snare at the same time, one is usually earlier than the other, so if the kick is early, and you set the "Minimum slice length" long enough to not cut two slices and keep it as one, the kick will get quantized but the snare will be out. In those guys you still have to go back in and fix those parts manually, so usually I just slip edit them manually in the first place.

It's not as big of a deal with kick/snare + high hat because an actual drum transient is a lot more significant than the high hat transient. It's usually only the ride that you have to almost treat as a drum because of how sharp the transient is. Aside from that, I don't worry about cymbal transients unless the cymbal isn't hit at the same time as a drum, then you can easily slide it into place.

I only use kick/snare/toms for the stereo stem, even then just kick/snare a lot of the time. Room mics you definitely don't want to use because it is just going to contain the same transients as the close mics, just blurred and shifted later in time which screws things up a bit.
 
You shouldn't have any phase issues, that's the whole point of grouping everything. Exact same way it works in Beat Detective.

If the stretch algorithm considers each track individually + is transient aware why wouldn't there be a possibility of it giving different results based on what it interprets as (primary) transient? Wouldn't hihat/snare combination in the hihat track vs the OH track be recognized differently and then calculated differently?
I'm not familiar with reaper so maybe someone can clarify.
Cheers!
-b
 
If the stretch algorithm considers each track individually + is transient aware why wouldn't there be a possibility of it giving different results based on what it interprets as (primary) transient? Wouldn't hihat/snare combination in the hihat track vs the OH track be recognized differently and then calculated differently?
I'm not familiar with reaper so maybe someone can clarify.
Cheers!
-b

There's definitely a possibility of some slight adjustments in phase after stretching! The post you quoted though was meant to clear up the idea of grouping the drum tracks, the poster who was asking about phase problems thought we were editing each track individually! ;)

Anyways, here's an example of stretching in Reaper, the top track is a snare track and the bottom two tracks are the overhead tracks, slipped a bit early to put the transients in phase to make it easier to compare the result:

prestretch.png


After stretching all the items down to 75% and rendering them:

poststretch.png


As you can see the items do stay in phase for the most part, certainly enough to not be a problem for the handful of hits that get stretched. With Elastic Audio this is an issue because every single hit is being compressed or stretched, so it starts to sound really obvious. With my method, most of the time nothing is being stretched, only once in a while is one hit in a batch of hits going to have to be time stretched.
 
Downloaded Reaper, will give it a try on the next song I'm editing. I remember reading somewhere a long time ago of stemming the overheads (or anything that is phase susceptible) into a surround 5.1 or other non stereo format and editing things this way, I tried it a long time ago and it worked but since 99% of the time there is no need to stretch things so it wasn't worth the effort. On the other hand your method saves DAYS (!!!) of work but stretches more.
It would be an interesting test to take the same segment of 5-6 tracks, group them, stretch them vs surround-sound stem them, stretch them and then compare by ear and phase reverse...
I'll do it once I have Reaper set up and running but if anyone is faster or has the time it would be even better since moving to a new DAW is like re-inventing the wheel for a while...
 
Downloaded Reaper, will give it a try on the next song I'm editing. I remember reading somewhere a long time ago of stemming the overheads (or anything that is phase susceptible) into a surround 5.1 or other non stereo format and editing things this way, I tried it a long time ago and it worked but since 99% of the time there is no need to stretch things so it wasn't worth the effort. On the other hand your method saves DAYS (!!!) of work but stretches more.
It would be an interesting test to take the same segment of 5-6 tracks, group them, stretch them vs surround-sound stem them, stretch them and then compare by ear and phase reverse...
I'll do it once I have Reaper set up and running but if anyone is faster or has the time it would be even better since moving to a new DAW is like re-inventing the wheel for a while...


You don't have to stretch any more using this method than using multichannel... In fact using my scripts you can disable stretching completely, it gives you all the flexibility you need! So if you only want to stretch rarely, set the "Maximum Gap" to be fairly high and it will only stretch when there are really large gaps. If you don't want to stretch at all, set the "Maximum Stretch" value to 1 and it will never time stretch anything! ;)

Using multichannel stems the editing is exactly the same, you just don't have to group the items :)

I believe that stretching multichannel media gives the exact same result as stretching separate tracks, because the stretching algorithm is applied independently to each audio stream and each channel in a file is it's own stream, so stemming them to surround and then stretching shouldn't sound any better or more phase coherent.
 
trying to download the sws extensions both 32 and 64 bit and all i get is 0 byte files... only links working are for sws for reaper 2 which probably won't have AutoPocket ... maybe something is being updated... I'll try tomorrow.
 
dear adam, please execute the following steps:

1. compile all of your scripts, tools, customizations, etc. into one simple installer (not cause i'm an idiot, but for the marketability.)
2. sell it on here for a reasonable price
3. accept my money and the money of ...probably almost every other reaper user on here
4. ???
5. profit

why hasn't cockos hired you yet???

edit:

ok now, thinking back, your methodology and logic here is more or less 100% filling the gap in Reapers workflow hole that's been holding it back as a DAW and creating complaints from power users that want to be able to migrate over from other DAW's in a professional environment but can't. You are of immeasurable worth to Cocko's, if they don't work directly with you and provide even some sort of compensation then they are really dumb, they are REALLY DUMB, FOR REAL.

youtube-star-antoine-dodson-260594.jpg
 
dear adam, please execute the following steps:

1. compile all of your scripts, tools, customizations, etc. into one simple installer (not cause i'm an idiot, but for the marketability.)
2. sell it on here for a reasonable price
3. accept my money and the money of ...probably almost every other reaper user on here
4. ???
5. profit

why hasn't cockos hired you yet???

edit:

ok now, thinking back, your methodology and logic here is more or less 100% filling the gap in Reapers workflow hole that's been holding it back as a DAW and creating complaints from power users that want to be able to migrate over from other DAW's in a professional environment but can't. You are of immeasurable worth to Cocko's, if they don't work directly with you and provide even some sort of compensation then they are really dumb, they are REALLY DUMB, FOR REAL.

youtube-star-antoine-dodson-260594.jpg

I second that!