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.
I'm kinda curious what the C++ code for something like this looks like....
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
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.
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
Nevermind...I tried that too...I just created my own one in the end. Super sweet.
BTW NSGUITAR, you're wanted in the "studio pics" thread!
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
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...
So maybe some name that alludes to the filling of the gaps?
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.