1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-20 06:10:06 +02:00
audacity/src/Mix.cpp
richardash1981 6128684055 If only mix and rendering one track, make the new track have the same name.
This saves a lot of typing if it is necessary to render a lot tracks in-place whilst preserving their names.
It is currently impossible to test that the two channels of stereo tracks get the correct name, as making stereo tracks always results in tracks with the same name for both channels ... This code would be much simpler if stereo tracks were objects.
This function (Mix and Render) could be made simpler if it used the TrackList functions like GetNumExportChannels() and made better use of conditional iterators rather than working it out for itself.
2011-08-28 19:30:17 +00:00

708 lines
18 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Mix.cpp
Dominic Mazzoni
Markus Meyer
Vaughan Johnson
*******************************************************************//**
\class Mixer
\brief Functions for doing the mixdown of the tracks.
*//****************************************************************//**
\class MixerSpec
\brief Class used with Mixer.
*//*******************************************************************/
#include "Audacity.h"
#include "Mix.h"
#include <math.h>
#include <wx/textctrl.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/timer.h>
#include <wx/intl.h>
#include "WaveTrack.h"
#include "DirManager.h"
#include "Envelope.h"
#include "Internat.h"
#include "Prefs.h"
#include "Project.h"
#include "Resample.h"
#include "float_cast.h"
bool MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
double rate, sampleFormat format,
double startTime, double endTime,
WaveTrack **newLeft, WaveTrack **newRight)
{
// This function was formerly known as "Quick Mix". It takes one or
// more tracks as input; of all tracks that are selected, it mixes
// them together, applying any envelopes, amplitude gain, panning,
// and real-time effects in the process. The resulting pair of
// tracks (stereo) are "rendered" and have no effects, gain, panning,
// or envelopes.
WaveTrack **waveArray;
Track *t;
int numWaves = 0; /* number of wave tracks in the selection */
int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
bool mono = false; /* flag if output can be mono without loosing anything*/
bool oneinput = false; /* flag set to true if there is only one input track
(mono or stereo) */
int w;
TrackListIterator iter(tracks);
t = iter.First();
while (t) {
if (t->GetSelected() && t->GetKind() == Track::Wave) {
numWaves++;
float pan = ((WaveTrack*)t)->GetPan();
if (t->GetChannel() == Track::MonoChannel && pan == 0)
numMono++;
}
t = iter.Next();
}
if (numMono == numWaves)
mono = true;
double totalTime = 0.0;
waveArray = new WaveTrack *[numWaves];
w = 0;
t = iter.First();
while (t) {
if (t->GetSelected() && t->GetKind() == Track::Wave) {
waveArray[w++] = (WaveTrack *) t;
if (t->GetEndTime() > totalTime)
totalTime = t->GetEndTime();
}
t = iter.Next();
}
/* create the destination track (new track) */
if ((numWaves == 1) || ((numWaves == 2) && (iter.First()->GetLink() != NULL)))
oneinput = true;
// only one input track (either 1 mono or one linked stereo pair)
WaveTrack *mixLeft = trackFactory->NewWaveTrack(format, rate);
if (oneinput)
mixLeft->SetName(iter.First()->GetName()); /* set name of output track to be the same as the sole input track */
else
mixLeft->SetName(_("Mix"));
WaveTrack *mixRight = 0;
if (mono) {
mixLeft->SetChannel(Track::MonoChannel);
}
else {
mixRight = trackFactory->NewWaveTrack(format, rate);
if (oneinput)
mixLeft->SetName(iter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/
else
mixRight->SetName(_("Mix"));
mixLeft->SetChannel(Track::LeftChannel);
mixRight->SetChannel(Track::RightChannel);
mixLeft->SetLinked(true);
}
int maxBlockLen = mixLeft->GetIdealBlockSize();
if (startTime == endTime) {
startTime = 0.0;
endTime = totalTime;
}
Mixer *mixer = new Mixer(numWaves, waveArray, tracks->GetTimeTrack(),
startTime, endTime, mono ? 1 : 2, maxBlockLen, false,
rate, format);
::wxSafeYield();
ProgressDialog *progress = new ProgressDialog(_("Mix and Render"),
_("Mixing and rendering tracks"));
int updateResult = eProgressSuccess;
while(updateResult == eProgressSuccess) {
sampleCount blockLen = mixer->Process(maxBlockLen);
if (blockLen == 0)
break;
if (mono) {
samplePtr buffer = mixer->GetBuffer();
mixLeft->Append(buffer, format, blockLen);
}
else {
samplePtr buffer;
buffer = mixer->GetBuffer(0);
mixLeft->Append(buffer, format, blockLen);
buffer = mixer->GetBuffer(1);
mixRight->Append(buffer, format, blockLen);
}
updateResult = progress->Update(mixer->MixGetCurrentTime(), totalTime);
}
delete progress;
mixLeft->Flush();
if (!mono)
mixRight->Flush();
if (updateResult == eProgressCancelled || updateResult == eProgressFailed)
{
delete mixLeft;
if (!mono)
delete mixRight;
} else {
*newLeft = mixLeft;
if (!mono)
*newRight = mixRight;
#if 0
int elapsedMS = wxGetElapsedTime();
double elapsedTime = elapsedMS * 0.001;
double maxTracks = totalTime / (elapsedTime / numWaves);
// Note: these shouldn't be translated - they're for debugging
// and profiling only.
printf(" Tracks: %d\n", numWaves);
printf(" Mix length: %f sec\n", totalTime);
printf("Elapsed time: %f sec\n", elapsedTime);
printf("Max number of tracks to mix in real time: %f\n", maxTracks);
#endif
}
delete[] waveArray;
delete mixer;
return (updateResult == eProgressSuccess || updateResult == eProgressStopped);
}
Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks,
TimeTrack *timeTrack,
double startTime, double stopTime,
int numOutChannels, int outBufferSize, bool outInterleaved,
double outRate, sampleFormat outFormat,
bool highQuality, MixerSpec *mixerSpec)
{
int i;
mNumInputTracks = numInputTracks;
mInputTrack = new WaveTrack*[mNumInputTracks];
mSamplePos = new sampleCount[mNumInputTracks];
for(i=0; i<mNumInputTracks; i++) {
mInputTrack[i] = inputTracks[i];
mSamplePos[i] = inputTracks[i]->TimeToLongSamples(startTime);
}
mTimeTrack = timeTrack;
mT0 = startTime;
mT1 = stopTime;
mT = startTime;
mNumChannels = numOutChannels;
mBufferSize = outBufferSize;
mInterleaved = outInterleaved;
mRate = outRate;
mFormat = outFormat;
mApplyTrackGains = true;
mGains = new float[mNumChannels];
if( mixerSpec && mixerSpec->GetNumChannels() == mNumChannels &&
mixerSpec->GetNumTracks() == mNumInputTracks )
mMixerSpec = mixerSpec;
else
mMixerSpec = NULL;
if (mInterleaved) {
mNumBuffers = 1;
mInterleavedBufferSize = mBufferSize * mNumChannels;
}
else {
mNumBuffers = mNumChannels;
mInterleavedBufferSize = mBufferSize;
}
mBuffer = new samplePtr[mNumBuffers];
mTemp = new samplePtr[mNumBuffers];
for (int c = 0; c < mNumBuffers; c++) {
mBuffer[c] = NewSamples(mInterleavedBufferSize, mFormat);
mTemp[c] = NewSamples(mInterleavedBufferSize, floatSample);
}
mFloatBuffer = new float[mInterleavedBufferSize];
mQueueMaxLen = 65536;
mProcessLen = 1024;
mQueueStart = new int[mNumInputTracks];
mQueueLen = new int[mNumInputTracks];
mSampleQueue = new float *[mNumInputTracks];
mSRC = new Resample*[mNumInputTracks];
for(i=0; i<mNumInputTracks; i++) {
double factor = (mRate / mInputTrack[i]->GetRate());
double lowFactor = factor, highFactor = factor;
if (timeTrack) {
highFactor /= timeTrack->GetRangeLower() / 100.0;
lowFactor /= timeTrack->GetRangeUpper() / 100.0;
}
mSRC[i] = new Resample(highQuality, lowFactor, highFactor);
mSampleQueue[i] = new float[mQueueMaxLen];
mQueueStart[i] = 0;
mQueueLen[i] = 0;
}
int envLen = mInterleavedBufferSize;
if (mQueueMaxLen > envLen)
envLen = mQueueMaxLen;
mEnvValues = new double[envLen];
}
Mixer::~Mixer()
{
int i;
for (i = 0; i < mNumBuffers; i++) {
DeleteSamples(mBuffer[i]);
DeleteSamples(mTemp[i]);
}
delete[] mBuffer;
delete[] mTemp;
delete[] mInputTrack;
delete[] mEnvValues;
delete[] mFloatBuffer;
delete[] mGains;
delete[] mSamplePos;
for(i=0; i<mNumInputTracks; i++) {
delete mSRC[i];
delete[] mSampleQueue[i];
}
delete[] mSRC;
delete[] mSampleQueue;
delete[] mQueueStart;
delete[] mQueueLen;
}
void Mixer::ApplyTrackGains(bool apply)
{
mApplyTrackGains = apply;
}
void Mixer::Clear()
{
for (int c = 0; c < mNumBuffers; c++) {
memset(mTemp[c], 0, mInterleavedBufferSize * SAMPLE_SIZE(floatSample));
}
}
void MixBuffers(int numChannels, int *channelFlags, float *gains,
samplePtr src, samplePtr *dests,
int len, bool interleaved)
{
for (int c = 0; c < numChannels; c++) {
if (!channelFlags[c])
continue;
samplePtr destPtr;
int skip;
if (interleaved) {
destPtr = dests[0] + c*SAMPLE_SIZE(floatSample);
skip = numChannels;
} else {
destPtr = dests[c];
skip = 1;
}
float gain = gains[c];
float *dest = (float *)destPtr;
float *temp = (float *)src;
for (int j = 0; j < len; j++) {
*dest += temp[j] * gain; // the actual mixing process
dest += skip;
}
}
}
sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
sampleCount *pos, float *queue,
int *queueStart, int *queueLen,
Resample *SRC)
{
double trackRate = track->GetRate();
double initialWarp = mRate / trackRate;
double tstep = 1.0 / trackRate;
double t = *pos / trackRate;
int sampleSize = SAMPLE_SIZE(floatSample);
sampleCount out = 0;
/* time is floating point. Sample rate is integer. The number of samples
* has to be integer, but the multiplication gives a float result, which we
* round to get an integer result. TODO: is this always right or can it be
* off by one sometimes? Can we not get this information directly from the
* clip (which must know) rather than convert the time?
*
* LLL: Not at this time. While WaveClips provide methods to retrieve the
* start and end sample, they do the same float->sampleCount conversion
* to calculate the position.
*/
// Find the last sample
sampleCount endPos;
double endTime = track->GetEndTime();
if (endTime > mT1) {
endPos = track->TimeToLongSamples(mT1);
}
else {
endPos = track->TimeToLongSamples(endTime);
}
while (out < mMaxOut) {
if (*queueLen < mProcessLen) {
memmove(queue, &queue[*queueStart], (*queueLen) * sampleSize);
*queueStart = 0;
int getLen = mQueueMaxLen - *queueLen;
// Constrain
if (*pos + getLen > endPos) {
getLen = endPos - *pos;
}
// Nothing to do if past end of track
if (getLen > 0) {
track->Get((samplePtr)&queue[*queueLen],
floatSample,
*pos,
getLen);
track->GetEnvelopeValues(mEnvValues,
getLen,
(*pos) / trackRate,
tstep);
for (int i = 0; i < getLen; i++) {
queue[(*queueLen) + i] *= mEnvValues[i];
}
*queueLen += getLen;
*pos += getLen;
}
}
double factor = initialWarp;
if (mTimeTrack) {
double warpFactor = mTimeTrack->GetEnvelope()->GetValue(t);
warpFactor = (mTimeTrack->GetRangeLower() * (1.0 - warpFactor) +
warpFactor * mTimeTrack->GetRangeUpper()) / 100.0;
factor /= warpFactor;
}
sampleCount thisProcessLen = mProcessLen;
bool last = (*queueLen < mProcessLen);
if (last) {
thisProcessLen = *queueLen;
}
int input_used;
int outgen = SRC->Process(factor,
&queue[*queueStart],
thisProcessLen,
last,
&input_used,
&mFloatBuffer[out],
mMaxOut - out);
if (outgen < 0) {
return 0;
}
*queueStart += input_used;
*queueLen -= input_used;
out += outgen;
t += (input_used / trackRate);
if (last) {
break;
}
}
for (int c = 0; c < mNumChannels; c++) {
if (mApplyTrackGains) {
mGains[c] = track->GetChannelGain(c);
}
else {
mGains[c] = 1.0;
}
}
MixBuffers(mNumChannels,
channelFlags,
mGains,
(samplePtr)mFloatBuffer,
mTemp,
out,
mInterleaved);
return out;
}
sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track,
sampleCount *pos)
{
int slen = mMaxOut;
int c;
double t = *pos / track->GetRate();
double trackEndTime = track->GetEndTime();
double tEnd = trackEndTime > mT1 ? mT1 : trackEndTime;
//don't process if we're at the end of the selection or track.
if (t>=tEnd)
return 0;
//if we're about to approach the end of the track or selection, figure out how much we need to grab
if (t + slen/track->GetRate() > tEnd)
slen = (int)((tEnd - t) * track->GetRate() + 0.5);
if (slen > mMaxOut)
slen = mMaxOut;
track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
for(int i=0; i<slen; i++)
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
for(c=0; c<mNumChannels; c++)
if (mApplyTrackGains)
mGains[c] = track->GetChannelGain(c);
else
mGains[c] = 1.0;
MixBuffers(mNumChannels, channelFlags, mGains,
(samplePtr)mFloatBuffer, mTemp, slen, mInterleaved);
*pos += slen;
return slen;
}
sampleCount Mixer::Process(sampleCount maxToProcess)
{
if (mT >= mT1)
return 0;
int i, j;
sampleCount out;
sampleCount maxOut = 0;
int *channelFlags = new int[mNumChannels];
mMaxOut = maxToProcess;
Clear();
for(i=0; i<mNumInputTracks; i++) {
WaveTrack *track = mInputTrack[i];
for(j=0; j<mNumChannels; j++)
channelFlags[j] = 0;
if( mMixerSpec ) {
//ignore left and right when downmixing is not required
for( j = 0; j < mNumChannels; j++ )
channelFlags[ j ] = mMixerSpec->mMap[ i ][ j ] ? 1 : 0;
}
else {
switch(track->GetChannel()) {
case Track::MonoChannel:
default:
for(j=0; j<mNumChannels; j++)
channelFlags[j] = 1;
break;
case Track::LeftChannel:
channelFlags[0] = 1;
break;
case Track::RightChannel:
if (mNumChannels >= 2)
channelFlags[1] = 1;
else
channelFlags[0] = 1;
break;
}
}
if (mTimeTrack ||
track->GetRate() != mRate)
out = MixVariableRates(channelFlags, track,
&mSamplePos[i], mSampleQueue[i],
&mQueueStart[i], &mQueueLen[i], mSRC[i]);
else
out = MixSameRate(channelFlags, track, &mSamplePos[i]);
if (out > maxOut)
maxOut = out;
}
out = mInterleaved ? maxOut * mNumChannels : maxOut;
for(int c=0; c<mNumBuffers; c++)
CopySamples(mTemp[c], floatSample, mBuffer[c], mFormat, out);
mT += (maxOut / mRate);
delete [] channelFlags;
return maxOut;
}
samplePtr Mixer::GetBuffer()
{
return mBuffer[0];
}
samplePtr Mixer::GetBuffer(int channel)
{
return mBuffer[channel];
}
double Mixer::MixGetCurrentTime()
{
return mT;
}
void Mixer::Restart()
{
int i;
mT = mT0;
for(i=0; i<mNumInputTracks; i++)
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mT0);
for(i=0; i<mNumInputTracks; i++) {
mQueueStart[i] = 0;
mQueueLen[i] = 0;
}
}
void Mixer::Reposition(double t)
{
int i;
mT = t;
if( mT < mT0 )
mT = mT0;
if( mT > mT1 )
mT = mT1;
for(i=0; i<mNumInputTracks; i++) {
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mT);
mQueueStart[i] = 0;
mQueueLen[i] = 0;
}
}
MixerSpec::MixerSpec( int numTracks, int maxNumChannels )
{
mNumTracks = mNumChannels = numTracks;
mMaxNumChannels = maxNumChannels;
if( mNumChannels > mMaxNumChannels )
mNumChannels = mMaxNumChannels;
Alloc();
for( int i = 0; i < mNumTracks; i++ )
for( int j = 0; j < mNumChannels; j++ )
mMap[ i ][ j ] = ( i == j );
}
MixerSpec::MixerSpec( const MixerSpec &mixerSpec )
{
mNumTracks = mixerSpec.mNumTracks;
mMaxNumChannels = mixerSpec.mMaxNumChannels;
mNumChannels = mixerSpec.mNumChannels;
Alloc();
for( int i = 0; i < mNumTracks; i++ )
for( int j = 0; j < mNumChannels; j++ )
mMap[ i ][ j ] = mixerSpec.mMap[ i ][ j ];
}
void MixerSpec::Alloc()
{
mMap = new bool*[ mNumTracks ];
for( int i = 0; i < mNumTracks; i++ )
mMap[ i ] = new bool[ mMaxNumChannels ];
}
MixerSpec::~MixerSpec()
{
Free();
}
void MixerSpec::Free()
{
for( int i = 0; i < mNumTracks; i++ )
delete[] mMap[ i ];
delete[] mMap;
}
bool MixerSpec::SetNumChannels( int newNumChannels )
{
if( mNumChannels == newNumChannels )
return true;
if( newNumChannels > mMaxNumChannels )
return false;
for( int i = 0; i < mNumTracks; i++ )
{
for( int j = newNumChannels; j < mNumChannels; j++ )
mMap[ i ][ j ] = false;
for( int j = mNumChannels; j < newNumChannels; j++ )
mMap[ i ][ j ] = false;
}
mNumChannels = newNumChannels;
return true;
}
MixerSpec& MixerSpec::operator=( const MixerSpec &mixerSpec )
{
Free();
mNumTracks = mixerSpec.mNumTracks;
mNumChannels = mixerSpec.mNumChannels;
mMaxNumChannels = mixerSpec.mMaxNumChannels;
Alloc();
for( int i = 0; i < mNumTracks; i++ )
for( int j = 0; j < mNumChannels; j++ )
mMap[ i ][ j ] = mixerSpec.mMap[ i ][ j ];
return *this;
}
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
// version control system. Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3
// arch-tag: d4e10e74-cdf9-46ac-b309-91b115d2a78f