Exemple #1
0
bool EffectNoise::Process()
{
   if (noiseDuration <= 0.0)
      noiseDuration = sDefaultGenerateLen;

   //Iterate over each track
   TrackListIterator iter(mWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate());
      numSamples = (longSampleCount)(noiseDuration * track->GetRate() + 0.5);
      longSampleCount i = 0;
      float *data = new float[tmp->GetMaxBlockSize()];
      sampleCount block;

      while(i < numSamples) {
         block = tmp->GetBestBlockSize(i);
         if (block > (numSamples - i))
             block = numSamples - i;

         MakeNoise(data, block, track->GetRate(), noiseAmplitude);

         tmp->Append((samplePtr)data, floatSample, block);
         i += block;
      }
      delete[] data;

      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;

      //Iterate to the next track
      track = (WaveTrack *)iter.Next();
   }

   /*
      save last used values
      save duration unless value was got from selection, so we save only
      when user explicitely setup a value
   */
   if (mT1 == mT0)
      gPrefs->Write(wxT("/CsPresets/NoiseGen_Duration"), noiseDuration);

   gPrefs->Write(wxT("/CsPresets/NoiseGen_Type"), noiseType);
   gPrefs->Write(wxT("/CsPresets/NoiseGen_Amp"), noiseAmplitude);

   mT1 = mT0 + noiseDuration; // Update selection.

   return true;
}
Exemple #2
0
bool EffectToneGen::Process()
{
   if (length <= 0.0)
      length = sDefaultGenerateLen;

   //Iterate over each track
   TrackListIterator iter(mWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      mSample = 0;
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat());
      mCurRate = track->GetRate();
      tmp->SetRate(mCurRate);
      longSampleCount numSamples =
         (longSampleCount)(length * mCurRate + 0.5);
      longSampleCount i = 0;
      float *data = new float[tmp->GetMaxBlockSize()];
      sampleCount block;

      while(i < numSamples) {
         block = tmp->GetBestBlockSize(i);
         if (block > (numSamples - i))
             block = numSamples - i;
         MakeTone(data, block);
         tmp->Append((samplePtr)data, floatSample, block);
         i += block;
      }
      delete[] data;

      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;
      
      //Iterate to the next track
      track = (WaveTrack *)iter.Next();
   }

	mT1 = mT0 + length; // Update selection.

   return true;
}
Exemple #3
0
bool EffectSilence::Process()
{
   if (length <= 0.0)
      length = sDefaultGenerateLen;

   TrackListIterator iter(mWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate());
      tmp->InsertSilence(0.0, length);
      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;
      
      //Iterate to the next track
      track = (WaveTrack *)iter.Next();
   }

	mT1 = mT0 + length; // Update selection.
   return true;
}
// ProcessOne() takes a track, transforms it to bunch of buffer-blocks,
// and calls libsamplerate code on these blocks.
bool EffectChangeSpeed::ProcessOne(WaveTrack * track,
                           sampleCount start, sampleCount end)
{
   if (track == NULL)
      return false;

   // initialization, per examples of Mixer::Mixer and
   // EffectSoundTouch::ProcessOne

   WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),
                                                    track->GetRate());

   //Get the length of the selection (as double). len is
   //used simple to calculate a progress meter, so it is easier
   //to make it a double now than it is to do it later 
   double len = (double)(end - start);

   // Initiate processing buffers, most likely shorter than 
   // the length of the selection being processed.
   sampleCount inBufferSize = track->GetMaxBlockSize();

   float * inBuffer = new float[inBufferSize];

   sampleCount outBufferSize = 
      (sampleCount)((mFactor * inBufferSize) + 10);
   float * outBuffer = new float[outBufferSize]; 

   // Set up the resampling stuff for this track.
   Resample resample(true, mFactor, mFactor);

   //Go through the track one buffer at a time. samplePos counts which
   //sample the current buffer starts at.
   bool bResult = true;
   sampleCount blockSize;
   sampleCount samplePos = start;
   while (samplePos < end) {
      //Get a blockSize of samples (smaller than the size of the buffer)
      blockSize = track->GetBestBlockSize(samplePos);

      //Adjust the block size if it is the final block in the track
      if (samplePos + blockSize > end)
         blockSize = end - samplePos;

      //Get the samples from the track and put them in the buffer
      track->Get((samplePtr) inBuffer, floatSample, samplePos, blockSize);

      int inUsed;
      int outgen = resample.Process(mFactor,
                                    inBuffer,
                                    blockSize,
                                    ((samplePos + blockSize) >= end),
                                    &inUsed,
                                    outBuffer,
                                    outBufferSize);
      if (outgen < 0) {
         bResult = false;
         break;
      }

      if (outgen > 0)
         outputTrack->Append((samplePtr)outBuffer, floatSample, 
                             outgen);

      // Increment samplePos
      samplePos += inUsed;

      // Update the Progress meter
      if (TrackProgress(mCurTrackNum, (samplePos - start) / len)) {
         bResult = false;
         break;
      }
   }

   // Flush the output WaveTrack (since it's buffered, too)
   outputTrack->Flush();

   // Clean up the buffers
   delete [] inBuffer;
   delete [] outBuffer;

   // Take the output track and insert it in place of the original
   // sample data
   double newLength = outputTrack->GetEndTime(); 
   if (bResult) 
   {
      SetTimeWarper(new LinearTimeWarper(mCurT0, mCurT0, mCurT1, mCurT0 + newLength));
      bResult = track->ClearAndPaste(mCurT0, mCurT1, outputTrack, true, false, GetTimeWarper());
   }

   if (newLength > mMaxNewLength) 
      mMaxNewLength = newLength; 

   // Delete the outputTrack now that its data is inserted in place
   delete outputTrack;

   return bResult;
}
//------------------------- Processing methods -------------------------
bool EffectSineSweepGenerator::Process()
{
   // taken `as is` from Audacity`s Generator.cpp to resolve
   // a vc++ linking problem...
   
   if (mDuration < 0.0)
      return false;

   BeforeGenerate();

   // Set up mOutputTracks. This effect needs Track::All for grouping
   this->CopyInputTracks(Track::All);

   // Iterate over the tracks
   bool bGoodResult = true;
   int ntrack = 0;
   TrackListIterator iter(mOutputTracks);
   Track* t = iter.First();

   while (t != NULL)
   {
      if (t->GetKind() == Track::Wave && t->GetSelected()) 
      {
         WaveTrack* track = (WaveTrack*)t;
         
         bool editClipCanMove = true;
         //gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove, true);

         //if we can't move clips, and we're generating into an empty space,
         //make sure there's room.
         if (!editClipCanMove &&
             track->IsEmpty(mT0, mT1+1.0/track->GetRate()) &&
             !track->IsEmpty(mT0, mT0+mDuration-(mT1-mT0)-1.0/track->GetRate()))
         {
             wxMessageBox(_("There is not enough room available to generate the audio"),
                          _("Error"), wxICON_STOP);   
            Failure();
            return false;
         }

         if (mDuration > 0.0)
         {
            // Create a temporary track
            WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(),
                                                    track->GetRate());
            //BeforeTrack(*track);

            // Fill it with data
            if (!GenerateTrack(tmp, *track, ntrack))
               bGoodResult = false;
            else 
            {
               // Transfer the data from the temporary track to the actual one
               tmp->Flush();
               SetTimeWarper(new AFStepTimeWarper(mT0+mDuration, mDuration-(mT1-mT0)));
               bGoodResult = track->ClearAndPaste(mT0, mT1, tmp, true,
                     false, GetTimeWarper());
               delete tmp;
            }

            if (!bGoodResult) 
            {
               Failure();
               return false;
            }
         }
         else
         {
            // If the duration is zero, there's no need to actually
            // generate anything
            track->Clear(mT0, mT1);
         }

         ntrack++;
      }
      else if (t->IsSyncLockSelected()) 
      {
         t->SyncLockAdjust(mT1, mT0 + mDuration);
      }
      // Move on to the next track
      t = iter.Next();
   }

   Success();

   this->ReplaceProcessedTracks(bGoodResult);

   mT1 = mT0 + mDuration; // Update selection.

   return true;
}
Exemple #6
0
bool EffectNoise::Process()
{
   if (noiseDuration <= 0.0)
      noiseDuration = sDefaultGenerateLen;

   //Iterate over each track
   int ntrack = 0;
   this->CopyInputWaveTracks(); // Set up mOutputWaveTracks.
   bool bGoodResult = true;

   TrackListIterator iter(mOutputWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate());
      numSamples = (longSampleCount)(noiseDuration * track->GetRate() + 0.5);
      longSampleCount i = 0;
      float *data = new float[tmp->GetMaxBlockSize()];
      sampleCount block;

      while ((i < numSamples) && bGoodResult) {
         block = tmp->GetBestBlockSize(i);
         if (block > (numSamples - i))
             block = numSamples - i;

         MakeNoise(data, block, track->GetRate(), noiseAmplitude);

         tmp->Append((samplePtr)data, floatSample, block);
         i += block;

         //Update the Progress meter
         if (TrackProgress(ntrack, (double)i / numSamples))
            bGoodResult = false;
      }
      delete[] data;

      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;

      if (!bGoodResult)
         break;

      //Iterate to the next track
      ntrack++;
      track = (WaveTrack *)iter.Next();
   }

   if (bGoodResult)
   {
      /*
         save last used values
         save duration unless value was got from selection, so we save only
         when user explicitely setup a value
      */
      if (mT1 == mT0)
         gPrefs->Write(wxT("/CsPresets/NoiseGen_Duration"), noiseDuration);

      gPrefs->Write(wxT("/CsPresets/NoiseGen_Type"), noiseType);
      gPrefs->Write(wxT("/CsPresets/NoiseGen_Amp"), noiseAmplitude);

      mT1 = mT0 + noiseDuration; // Update selection.
   }

   this->ReplaceProcessedWaveTracks(bGoodResult); 
   return bGoodResult;
}
Exemple #7
0
void ControlToolBar::OnRecord(wxCommandEvent &evt)
{
   if (gAudioIO->IsBusy()) {
      mRecord->PopUp();
      return;
   }
   AudacityProject *p = GetActiveProject();

   if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored.
      mRecord->SetShift(true);
   if( evt.GetInt() == 2 )
      mRecord->SetShift(false);

   SetRecord(true, mRecord->WasShiftDown());

   if (p) {
      TrackList *t = p->GetTracks();
      TrackListIterator it(t);
      if(it.First() == NULL)
         mRecord->SetShift(false);
      double t0 = p->GetSel0();
      double t1 = p->GetSel1();
      if (t1 == t0)
         t1 = 1000000000.0;     // record for a long, long time (tens of years)

      /* TODO: set up stereo tracks if that is how the user has set up
       * their preferences, and choose sample format based on prefs */
      WaveTrackArray newRecordingTracks, playbackTracks;
#ifdef EXPERIMENTAL_MIDI_OUT
      NoteTrackArray midiTracks;
#endif
      bool duplex;
      gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);

      if(duplex){
         playbackTracks = t->GetWaveTrackArray(false);
#ifdef EXPERIMENTAL_MIDI_OUT
         midiTracks = t->GetNoteTrackArray(false);
#endif
     }
      else {
         playbackTracks = WaveTrackArray();
#ifdef EXPERIMENTAL_MIDI_OUT
         midiTracks = NoteTrackArray();
#endif
     }

      // If SHIFT key was down, the user wants append to tracks
      int recordingChannels = 0;
      bool shifted = mRecord->WasShiftDown();
      if (shifted) {
         bool sel = false;
         double allt0 = t0;

         // Find the maximum end time of selected and all wave tracks
         // Find whether any tracks were selected.  (If any are selected,
         // record only into them; else if tracks exist, record into all.)
         for (Track *tt = it.First(); tt; tt = it.Next()) {
            if (tt->GetKind() == Track::Wave) {
               WaveTrack *wt = static_cast<WaveTrack *>(tt);
               if (wt->GetEndTime() > allt0) {
                  allt0 = wt->GetEndTime();
               }

               if (tt->GetSelected()) {
                  sel = true;
                  if (wt->GetEndTime() > t0) {
                     t0 = wt->GetEndTime();
                  }
               }
            }
         }

         // Use end time of all wave tracks if none selected
         if (!sel) {
            t0 = allt0;
         }

         // Pad selected/all wave tracks to make them all the same length
         // Remove recording tracks from the list of tracks for duplex ("overdub")
         // playback.
         for (Track *tt = it.First(); tt; tt = it.Next()) {
            if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) {
               WaveTrack *wt = static_cast<WaveTrack *>(tt);
               if (duplex)
                  playbackTracks.Remove(wt);
               t1 = wt->GetEndTime();
               if (t1 < t0) {
                  WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack();
                  newTrack->InsertSilence(0.0, t0 - t1);
                  newTrack->Flush();
                  wt->Clear(t1, t0);
                  bool bResult = wt->Paste(t1, newTrack);
                  wxASSERT(bResult); // TO DO: Actually handle this.
                  delete newTrack;
               }
               newRecordingTracks.Add(wt);
            }
         }

         t1 = 1000000000.0;     // record for a long, long time (tens of years)
      }
      else {
         recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2);
         for (int c = 0; c < recordingChannels; c++) {
            WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack();

            newTrack->SetOffset(t0);

            if (recordingChannels > 2)
              newTrack->SetMinimized(true);

            if (recordingChannels == 2) {
               if (c == 0) {
                  newTrack->SetChannel(Track::LeftChannel);
                  newTrack->SetLinked(true);
               }
               else {
                  newTrack->SetChannel(Track::RightChannel);
               }
            }
            else {
               newTrack->SetChannel( Track::MonoChannel );
            }

            newRecordingTracks.Add(newTrack);
         }

         // msmeyer: StartStream calls a callback which triggers auto-save, so
         // we add the tracks where recording is done into now. We remove them
         // later if starting the stream fails
         for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++)
            t->Add(newRecordingTracks[i]);
      }

      //Automated Input Level Adjustment Initialization
      #ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT
         gAudioIO->AILAInitialize();
      #endif

      int token = gAudioIO->StartStream(playbackTracks,
                                        newRecordingTracks,
#ifdef EXPERIMENTAL_MIDI_OUT
                                        midiTracks,
#endif
                                        t->GetTimeTrack(),
                                        p->GetRate(), t0, t1, p);

      bool success = (token != 0);

      if (success) {
         p->SetAudioIOToken(token);
         mBusyProject = p;
      }
      else {
         // msmeyer: Delete recently added tracks if opening stream fails
         if (!shifted) {
            for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) {
               t->Remove(newRecordingTracks[i]);
               delete newRecordingTracks[i];
            }
         }

         // msmeyer: Show error message if stream could not be opened
         wxMessageBox(_("Error while opening sound device. Please check the recording device settings and the project sample rate."),
                      _("Error"), wxOK | wxICON_EXCLAMATION, this);

         SetPlay(false);
         SetStop(false);
         SetRecord(false);
      }
   }
}
Exemple #8
0
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;
   int numMono = 0;
   bool mono = false;
   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();
   }

   WaveTrack *mixLeft = trackFactory->NewWaveTrack(format, rate);
   mixLeft->SetName(_("Mix"));
   WaveTrack *mixRight = 0;
   if (mono) {
      mixLeft->SetChannel(Track::MonoChannel);
   }
   else {
      mixRight = trackFactory->NewWaveTrack(format, rate);
      mixRight->SetName(_("Mix"));
      mixLeft->SetChannel(Track::LeftChannel);
      mixRight->SetChannel(Track::RightChannel);
      mixLeft->SetLinked(true);
      mixRight->SetTeamed(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);

   wxYield();
   GetActiveProject()->ProgressShow(_NoAcc("&Mix and Render"),
                                    _("Mixing and rendering tracks"));
   wxBusyCursor busy;
   
   bool cancelling = false;
   while(!cancelling) {
      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);
      }

      int progressvalue = int (1000 * (mixer->MixGetCurrentTime() / totalTime));
      cancelling = !GetActiveProject()->ProgressUpdate(progressvalue);
   }

   GetActiveProject()->ProgressHide();

   mixLeft->Flush();
   *newLeft = mixLeft;
   if (!mono) {
      mixRight->Flush();
      *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 true;
}
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks,
//and executes ProcessSoundTouch on these blocks
bool EffectSoundTouch::ProcessOne(WaveTrack *track,
                                  longSampleCount start, longSampleCount end)
{
   WaveTrack *outputTrack;
   longSampleCount s;

   mSoundTouch->setSampleRate((unsigned int)(track->GetRate()+0.5));
   
   outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat());

   //Get the length of the buffer (as double). len is
   //used simple to calculate a progress meter, so it is easier
   //to make it a double now than it is to do it later 
   double len = (double)(end - start);

   //Initiate a processing buffer.  This buffer will (most likely)
   //be shorter than the length of the track being processed.
   float *buffer = new float[track->GetMaxBlockSize()];

   //Go through the track one buffer at a time. s counts which
   //sample the current buffer starts at.
   s = start;
   while (s < end) {
      //Get a block of samples (smaller than the size of the buffer)
      sampleCount block = track->GetBestBlockSize(s);

      //Adjust the block size if it is the final block in the track
      if (s + block > end)
         block = end - s;

      //Get the samples from the track and put them in the buffer
      track->Get((samplePtr) buffer, floatSample, s, block);

      //Add samples to SoundTouch
      mSoundTouch->putSamples(buffer, block);

      //Get back samples from SoundTouch
      unsigned int outputCount = mSoundTouch->numSamples();
      if (outputCount > 0) {
         float *buffer2 = new float[outputCount];
         mSoundTouch->receiveSamples(buffer2, outputCount);
         outputTrack->Append((samplePtr)buffer2, floatSample, outputCount);
         delete[] buffer2;
      }

      //Increment s one blockfull of samples
      s += block;

      //Update the Progress meter
      if (TrackProgress(mCurTrackNum, (s - start) / len))
         return false;
   }

   // Tell SoundTouch to finish processing any remaining samples
   mSoundTouch->flush();

   unsigned int outputCount = mSoundTouch->numSamples();
   if (outputCount > 0) {
      float *buffer2 = new float[outputCount];
      mSoundTouch->receiveSamples(buffer2, outputCount);
      outputTrack->Append((samplePtr)buffer2, floatSample, outputCount);
      delete[] buffer2;
   }

   // Flush the output WaveTrack (since it's buffered, too)
   outputTrack->Flush();

   // Clean up the buffer
   delete[]buffer;

   // Take the output track and insert it in place of the original
   // sample data

   track->Clear(mT0, mT1);
   track->Paste(mT0, outputTrack);

	double newLength = outputTrack->GetEndTime(); 
	if (newLength > m_maxNewLength) 
		m_maxNewLength = newLength; 

   // Delete the outputTrack now that its data is inserted in place
   delete outputTrack;

   //Return true because the effect processing succeeded.
   return true;
}
bool EffectSoundTouch::ProcessStereo(WaveTrack* leftTrack, WaveTrack* rightTrack, 
                                       longSampleCount start, longSampleCount end)
{
   mSoundTouch->setSampleRate((unsigned int)(leftTrack->GetRate()+0.5));
   
   WaveTrack* outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat());
   WaveTrack* outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat());

   //Get the length of the buffer (as double). len is
   //used simple to calculate a progress meter, so it is easier
   //to make it a double now than it is to do it later 
   double len = (double)(end - start);

   //Initiate a processing buffer.  This buffer will (most likely)
   //be shorter than the length of the track being processed.
   // Make soundTouchBuffer twice as big as MaxBlockSize for each channel, 
   // because Soundtouch wants them interleaved, i.e., each 
   // Soundtouch sample is left-right pair. 
   sampleCount maxBlockSize = leftTrack->GetMaxBlockSize();
   float* leftBuffer = new float[maxBlockSize];
   float* rightBuffer = new float[maxBlockSize];
   float* soundTouchBuffer = new float[maxBlockSize * 2];

   // Go through the track one stereo buffer at a time. 
   // sourceSampleCount counts the sample at which the current buffer starts, 
   // per channel.
   longSampleCount sourceSampleCount = start;
   while (sourceSampleCount < end) {
      //Get a block of samples (smaller than the size of the buffer)
      sampleCount blockSize = leftTrack->GetBestBlockSize(sourceSampleCount);

      //Adjust the block size if it is the final block in the track
      if (sourceSampleCount + blockSize > end)
         blockSize = end - sourceSampleCount;

      // Get the samples from the tracks and put them in the buffers.
      leftTrack->Get((samplePtr)(leftBuffer), floatSample, sourceSampleCount, blockSize);
      rightTrack->Get((samplePtr)(rightBuffer), floatSample, sourceSampleCount, blockSize);

      // Interleave into soundTouchBuffer.
      for (int index = 0; index < blockSize; index++) {
         soundTouchBuffer[index*2]       = leftBuffer[index];
         soundTouchBuffer[(index*2)+1]   = rightBuffer[index];
      }

      //Add samples to SoundTouch
      mSoundTouch->putSamples(soundTouchBuffer, blockSize);

      //Get back samples from SoundTouch
      unsigned int outputCount = mSoundTouch->numSamples();
      if (outputCount > 0) 
         this->ProcessStereoResults(outputCount, outputLeftTrack, outputRightTrack);

      //Increment sourceSampleCount one blockfull of samples
      sourceSampleCount += blockSize;

      //Update the Progress meter
      if (TrackProgress(mCurTrackNum, (sourceSampleCount - start) / len))
         return false;
   }

   // Tell SoundTouch to finish processing any remaining samples
   mSoundTouch->flush();

   unsigned int outputCount = mSoundTouch->numSamples();
   if (outputCount > 0) 
      this->ProcessStereoResults(outputCount, outputLeftTrack, outputRightTrack);

   // Flush the output WaveTracks (since they're buffered, too)
   outputLeftTrack->Flush();
   outputRightTrack->Flush();

   // Clean up the buffers.
   delete [] leftBuffer;
   delete [] rightBuffer;
   delete [] soundTouchBuffer;

   // Take the output tracks and insert in place of the original
   // sample data.
   leftTrack->Clear(mT0, mT1);
   leftTrack->Paste(mT0, outputLeftTrack);
   rightTrack->Clear(mT0, mT1);
   rightTrack->Paste(mT0, outputRightTrack);

	double newLength = outputLeftTrack->GetEndTime(); 
	if (newLength > m_maxNewLength) 
		m_maxNewLength = newLength; 

   // Delete the outputTracks now that their data are inserted in place.
   delete outputLeftTrack;
   delete outputRightTrack;

   //Return true because the effect processing succeeded.
   return true;
}
Exemple #11
0
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count)
{
   int stretch_buf_size = GetBufferSize(track->GetRate());
   double amount = this->mAmount;

   sampleCount start = track->TimeToLongSamples(t0);
   sampleCount end = track->TimeToLongSamples(t1);
   sampleCount len = (sampleCount)(end - start);

   int minDuration = stretch_buf_size * 2 + 1;
   if (len < minDuration){   //error because the selection is too short

      float maxTimeRes = log(len) / log(2.0);
      maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5);
      maxTimeRes = maxTimeRes / track->GetRate();

      if (this->IsPreviewing()) {
         double defaultPreviewLen;
         gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0);

         /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
         if ((minDuration / mProjectRate) < defaultPreviewLen) {
            ::wxMessageBox (wxString::Format(_("Audio selection too short to preview.\n\n"
                                               "Try increasing the audio selection to at least %.1f seconds,\n"
                                               "or reducing the 'Time Resolution' to less than %.1f seconds."),
                                             (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
                                             floor(maxTimeRes * 10.0) / 10.0),
                            GetName(), wxOK | wxICON_EXCLAMATION);
         }
         else {
            /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
            ::wxMessageBox (wxString::Format(_("Unable to Preview.\n\n"
                                               "For the current audio selection, the maximum\n"
                                               "'Time Resolution' is %.1f seconds."),
                                             floor(maxTimeRes * 10.0) / 10.0),
                            GetName(), wxOK | wxICON_EXCLAMATION);
         }
      }
      else {
         /* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
         ::wxMessageBox (wxString::Format(_("The 'Time Resolution' is too long for the selection.\n\n"
                                            "Try increasing the audio selection to at least %.1f seconds,\n"
                                            "or reducing the 'Time Resolution' to less than %.1f seconds."),
                                          (minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
                                          floor(maxTimeRes * 10.0) / 10.0),
                         GetName(), wxOK | wxICON_EXCLAMATION);
      }

      return false;
   }


   double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0));
   amount=1.0+(amount-1.0)*adjust_amount;

   WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate());

   PaulStretch *stretch=new PaulStretch(amount,stretch_buf_size,track->GetRate());

   sampleCount nget=stretch->get_nsamples_for_fill();

   int bufsize=stretch->poolsize;
   float *buffer0=new float[bufsize];
   float *bufferptr0=buffer0;
   sampleCount outs=0;
   bool first_time=true;

   int fade_len=100;
   if (fade_len>(bufsize/2-1)) fade_len=bufsize/2-1;
   float *fade_track_smps=new float[fade_len];
   sampleCount s=0;
   bool cancelled=false;

   while (s<len){
      track->Get((samplePtr)bufferptr0,floatSample,start+s,nget);
      stretch->process(buffer0,nget);

      if (first_time) {
         stretch->process(buffer0,0);
      };

      outs+=stretch->out_bufsize;
      s+=nget;

      if (first_time){//blend the the start of the selection
         track->Get((samplePtr)fade_track_smps,floatSample,start,fade_len);
         first_time=false;
         for (int i=0;i<fade_len;i++){
            float fi=(float)i/(float)fade_len;
            stretch->out_buf[i]=stretch->out_buf[i]*fi+(1.0-fi)*fade_track_smps[i];
         };
      };
      if (s>=len){//blend the end of the selection
         track->Get((samplePtr)fade_track_smps,floatSample,end-fade_len,fade_len);
         for (int i=0;i<fade_len;i++){
            float fi=(float)i/(float)fade_len;
            int i2=bufsize/2-1-i;
            stretch->out_buf[i2]=stretch->out_buf[i2]*fi+(1.0-fi)*fade_track_smps[fade_len-1-i];
         };
      };

      outputTrack->Append((samplePtr)stretch->out_buf,floatSample,stretch->out_bufsize);

      nget=stretch->get_nsamples();
      if (TrackProgress(count, (s / (double) len))) {
         cancelled=true;
         break;
      };
   };

   delete [] fade_track_smps;
   outputTrack->Flush();

   track->Clear(t0,t1);
   bool success = track->Paste(t0,outputTrack);
   if (!cancelled && success){
      m_t1 = mT0 + outputTrack->GetEndTime();
   }

   delete stretch;
   delete []buffer0;

   delete outputTrack;
   return !cancelled;
};
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count){

   int stretch_buf_size;//must be power of 2 (because Audacity's fft requires it)
   if (time_resolution<0.001) time_resolution=0.001f;
   {
      float tmp=track->GetRate()*time_resolution*0.5;
      tmp=log(tmp)/log(2.0);
      tmp=pow(2.0,floor(tmp+0.5));
      stretch_buf_size=(int)tmp;
   };
   if (stretch_buf_size<128) stretch_buf_size=128;
   double amount=this->amount;
   if (amount<1.0) amount=1.0;

   sampleCount start = track->TimeToLongSamples(t0);
   sampleCount end = track->TimeToLongSamples(t1);
   sampleCount len = (sampleCount)(end - start);

   m_t1=mT1;

   if (len<=(stretch_buf_size*2+1)){//error because the selection is too short
      /* i18n-hint: This is an effect error message, for the effect named Paulstretch.
       * Time Resolution is a parameter of the effect, the translation should match
       */
      ::wxMessageBox(_("Error in Paulstretch:\nThe selection is too short.\n It must be much longer than the Time Resolution."));
      return false;
   };


   double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0));
   amount=1.0+(amount-1.0)*adjust_amount;

   WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate());

   PaulStretch *stretch=new PaulStretch(amount,stretch_buf_size,track->GetRate());

   sampleCount nget=stretch->get_nsamples_for_fill();

   int bufsize=stretch->poolsize;
   float *buffer0=new float[bufsize];
   float *bufferptr0=buffer0;
   sampleCount outs=0;
   bool first_time=true;

   int fade_len=100;
   if (fade_len>(bufsize/2-1)) fade_len=bufsize/2-1;
   float *fade_track_smps=new float[fade_len];
   sampleCount s=0;
   bool cancelled=false;

   while (s<len){
      track->Get((samplePtr)bufferptr0,floatSample,start+s,nget);
      stretch->process(buffer0,nget);

      if (first_time) {
         stretch->process(buffer0,0);
      };

      outs+=stretch->out_bufsize;
      s+=nget;

      if (first_time){//blend the the start of the selection
         track->Get((samplePtr)fade_track_smps,floatSample,start,fade_len);
         first_time=false;
         for (int i=0;i<fade_len;i++){
            float fi=(float)i/(float)fade_len;
            stretch->out_buf[i]=stretch->out_buf[i]*fi+(1.0-fi)*fade_track_smps[i];
         };
      };
      if (s>=len){//blend the end of the selection
         track->Get((samplePtr)fade_track_smps,floatSample,end-fade_len,fade_len);
         for (int i=0;i<fade_len;i++){
            float fi=(float)i/(float)fade_len;
            int i2=bufsize/2-1-i;
            stretch->out_buf[i2]=stretch->out_buf[i2]*fi+(1.0-fi)*fade_track_smps[fade_len-1-i];
         };
      };

      outputTrack->Append((samplePtr)stretch->out_buf,floatSample,stretch->out_bufsize);

      nget=stretch->get_nsamples();
      if (TrackProgress(count, (s / (double) len))) {
         cancelled=true;
         break;
      };
   };

   delete [] fade_track_smps; 
   outputTrack->Flush();



   track->Clear(t0,t1);
   track->Paste(t0,outputTrack);
   if (!cancelled){
      double flen=t1-t0;
      if (s>0) m_t1=t0+flen*(double)outs/(double)(s);
   };


   delete stretch;
   delete []buffer0;

   delete outputTrack;
   return !cancelled;
};
Exemple #13
0
//TODO-MB: wouldn't it make more sense to delete the time track after 'mix and render'?
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".
   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);
   SelectedTrackListOfKindIterator usefulIter(Track::Wave, tracks);
   // this only iterates tracks which are relevant to this function, i.e.
   // selected WaveTracks. The tracklist is (confusingly) the list of all
   // tracks in the project

   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;

   /* the next loop will do two things at once:
    * 1. build an array of all the wave tracks were are trying to process
    * 2. determine when the set of WaveTracks starts and ends, in case we
    *    need to work out for ourselves when to start and stop rendering.
    */

   double mixStartTime = 0.0;    /* start time of first track to start */
   bool gotstart = false;  // flag indicates we have found a start time
   double mixEndTime = 0.0;   /* end time of last track to end */
   double tstart, tend;    // start and end times for one track.

   waveArray = new WaveTrack *[numWaves];
   w = 0;
   t = iter.First();

   while (t) {
      if (t->GetSelected() && t->GetKind() == Track::Wave) {
         waveArray[w++] = (WaveTrack *) t;
         tstart = t->GetStartTime();
         tend = t->GetEndTime();
         if (tend > mixEndTime)
            mixEndTime = tend;
         // try and get the start time. If the track is empty we will get 0,
         // which is ambiguous because it could just mean the track starts at
         // the beginning of the project, as well as empty track. The give-away
         // is that an empty track also ends at zero.

         if (tstart != tend) {
            // we don't get empty tracks here
            if (!gotstart) {
               // no previous start, use this one unconditionally
               mixStartTime = tstart;
               gotstart = true;
            } else if (tstart < mixStartTime)
               mixStartTime = tstart;  // have a start, only make it smaller
         }  // end if start and end are different
      }  // end if track is a selected WaveTrack.
      /** @TODO: could we not use a SelectedTrackListOfKindIterator here? */
      t = iter.Next();
   }

   /* create the destination track (new track) */
   if ((numWaves == 1) || ((numWaves == 2) && (usefulIter.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(usefulIter.First()->GetName()); /* set name of output track to be the same as the sole input track */
   else
      mixLeft->SetName(_("Mix"));
   mixLeft->SetOffset(mixStartTime);
   WaveTrack *mixRight = 0;
   if (mono) {
      mixLeft->SetChannel(Track::MonoChannel);
   }
   else {
      mixRight = trackFactory->NewWaveTrack(format, rate);
      if (oneinput) {
         if (usefulIter.First()->GetLink() != NULL)   // we have linked track
            mixLeft->SetName(usefulIter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/
         else
            mixLeft->SetName(usefulIter.First()->GetName());   /* set name to that of sole input channel */
      }
      else
         mixRight->SetName(_("Mix"));
      mixLeft->SetChannel(Track::LeftChannel);
      mixRight->SetChannel(Track::RightChannel);
      mixRight->SetOffset(mixStartTime);
      mixLeft->SetLinked(true);
   }



   int maxBlockLen = mixLeft->GetIdealBlockSize();

   // If the caller didn't specify a time range, use the whole range in which
   // any input track had clips in it.
   if (startTime == endTime) {
      startTime = mixStartTime;
      endTime = mixEndTime;
   }

   Mixer *mixer = new Mixer(numWaves, waveArray,
                            Mixer::WarpOptions(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() - startTime, endTime - startTime);
   }

   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);
}
Exemple #14
0
bool EffectDtmf::Process()
{
   if (dtmfDuration <= 0.0)
      return false;

   //Iterate over each track
   TrackListIterator iter(mWaveTracks);
   WaveTrack *track = (WaveTrack *)iter.First();
   while (track) {
      // new tmp track, to fill with dtmf sequence
      // we will build the track by adding a tone, then a silence, next tone, and so on...
      WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate());

      // all dtmf sequence durations in samples from seconds
      numSamplesSequence = (longSampleCount)(dtmfDuration * track->GetRate() + 0.5);
      numSamplesTone = (longSampleCount)(dtmfTone * track->GetRate() + 0.5);
      numSamplesSilence = (longSampleCount)(dtmfSilence * track->GetRate() + 0.5);

      // recalculate the sum, and spread the difference - due to approximations.
      // Since diff should be in the order of "some" samples, a division (resulting in zero)
      // is not sufficient, so we add the additional remaining samples in each tone/silence block,
      // at least until available.
      int diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence;
      if (diff>dtmfNTones) {
         // in this case, both these values would change, so it makes sense to recalculate diff
         // otherwise just keep the value we already have

         // should always be the case that dtmfNTones>1, as if 0, we don't even start processing,
         // and with 1 there is no difference to spread (no silence slot)...
         wxASSERT(dtmfNTones > 1);
         numSamplesTone += (diff/(dtmfNTones));
         numSamplesSilence += (diff/(dtmfNTones-1));
         diff = numSamplesSequence - (dtmfNTones*numSamplesTone) - (dtmfNTones-1)*numSamplesSilence;
      }
      // this var will be used as extra samples distributor
      int extra=0;

      longSampleCount i = 0;
      longSampleCount j = 0;
      int n=0; // pointer to string in dtmfString
      sampleCount block;
      bool isTone = true;
      float *data = new float[tmp->GetMaxBlockSize()];

      // for the whole dtmf sequence, we will be generating either tone or silence
      // according to a bool value, and this might be done in small chunks of size
      // 'block', as a single tone might sometimes be larger than the block
      // tone and silence generally have different duration, thus two generation blocks
      //
      // Note: to overcome a 'clicking' noise introduced by the abrupt transition from/to
      // silence, I added a fade in/out of 1/250th of a second (4ms). This can still be
      // tweaked but gives excellent results at 44.1kHz: I haven't tried other freqs.
      // A problem might be if the tone duration is very short (<10ms)... (?)
      //
      // One more problem is to deal with the approximations done when calculating the duration 
      // of both tone and silence: in some cases the final sum might not be same as the initial
      // duration. So, to overcome this, we had a redistribution block up, and now we will spread
      // the remaining samples in every bin in order to achieve the full duration: test case was
      // to generate an 11 tone DTMF sequence, in 4 seconds, and with DutyCycle=75%: after generation
      // you ended up with 3.999s or in other units: 3 seconds and 44097 samples.
      //
      while(i < numSamplesSequence) {
         if (isTone)
         // generate tone
         {
            // the statement takes care of extracting one sample from the diff bin and
            // adding it into the tone block until depletion
            extra=(diff-- > 0?1:0);
            for(j=0; j < numSamplesTone+extra; j+=block) {
               block = tmp->GetBestBlockSize(j);
               if (block > (numSamplesTone+extra - j))
                   block = numSamplesTone+extra - j;

               // generate the tone and append
               MakeDtmfTone(data, block, track->GetRate(), dtmfString[n], j, numSamplesTone);
               tmp->Append((samplePtr)data, floatSample, block);
            }
            i += numSamplesTone;
            n++;
            if(n>=dtmfNTones)break;
         }
         else
         // generate silence
         {
            // the statement takes care of extracting one sample from the diff bin and
            // adding it into the silence block until depletion
            extra=(diff-- > 0?1:0);
            for(j=0; j < numSamplesSilence+extra; j+=block) {
               block = tmp->GetBestBlockSize(j);
               if (block > (numSamplesSilence+extra - j))
                   block = numSamplesSilence+extra - j;

               // generate silence and append
               memset(data, 0, sizeof(float)*block);
               tmp->Append((samplePtr)data, floatSample, block);
            }
            i += numSamplesSilence;
         }
         // flip flag
         isTone=!isTone;
      } // finished the whole dtmf sequence

      delete[] data;

      tmp->Flush();
      track->Clear(mT0, mT1);
      track->Paste(mT0, tmp);
      delete tmp;

      //Iterate to the next track
      track = (WaveTrack *)iter.Next();
   }

   /*
      save last used values
      save duration unless value was got from selection, so we save only
      when user explicitely setup a value
   */
   if (mT1 == mT0)
      gPrefs->Write(wxT("/CsPresets/DtmfGen_SequenceDuration"), dtmfDuration);

   gPrefs->Write(wxT("/CsPresets/DtmfGen_String"), dtmfString);
   gPrefs->Write(wxT("/CsPresets/DtmfGen_DutyCycle"), dtmfDutyCycle);

   // Update selection: this is not accurate if my calculations are wrong.
   // To validate, once the effect is done, unselect, and select all, then
   // see what the selection length is being reported (in sec,ms,samples)
   mT1 = mT0 + dtmfDuration;

   return true;
}
void ControlToolBar::OnRecord(wxCommandEvent &evt)
{
   if (gAudioIO->IsBusy()) {
      mRecord->PopUp();
      return;
   }
   AudacityProject *p = GetActiveProject();
   if (p && p->GetCleanSpeechMode()) {
      size_t numProjects = gAudacityProjects.Count();
      bool tracks = (p && !p->GetTracks()->IsEmpty());
      if (tracks || (numProjects > 1)) {
         wxMessageBox(_("Recording in CleanSpeech mode is not possible when a track, or more than one project, is already open."),
            _("Recording not permitted"),
            wxOK | wxICON_INFORMATION,
            this);
         mRecord->PopUp();
         mRecord->Disable();
         return;
      }
   }

   if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored.
      mRecord->SetShift(true);
   if( evt.GetInt() == 2 )
      mRecord->SetShift(false);

   SetRecord(true);

   if (p) {
      TrackList *t = p->GetTracks();
      TrackListIterator it(t);
      if(it.First() == NULL)
         mRecord->SetShift(false);
      double t0 = p->GetSel0();
      double t1 = p->GetSel1();
      if (t1 == t0)
         t1 = 1000000000.0;     // record for a long, long time (tens of years)

      /* TODO: set up stereo tracks if that is how the user has set up
       * their preferences, and choose sample format based on prefs */
      WaveTrackArray newRecordingTracks, playbackTracks;
/* REQUIRES PORTMIDI */
//      NoteTrackArray midiTracks;

      bool duplex;
      gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);
            
      if(duplex){
         playbackTracks = t->GetWaveTrackArray(false);
/* REQUIRES PORTMIDI */
//		 midiTracks = t->GetNoteTrackArray(false);
     }
      else {
         playbackTracks = WaveTrackArray();
/* REQUIRES PORTMIDI */
//		 midiTracks = NoteTrackArray();
     }
      
      // If SHIFT key was down, the user wants append to tracks
      int recordingChannels = 0;
      bool shifted = mRecord->WasShiftDown();
      if (shifted) {
         TrackListIterator it(t);
         WaveTrack *wt;
         bool sel = false;
         double allt0 = t0;

         // Find the maximum end time of selected and all wave tracks
         for (Track *tt = it.First(); tt; tt = it.Next()) {
            if (tt->GetKind() == Track::Wave) {
               wt = (WaveTrack *)tt;
               if (wt->GetEndTime() > allt0) {
                  allt0 = wt->GetEndTime();
               }
            
               if (tt->GetSelected()) {
                  sel = true;
                  if (duplex)
                     playbackTracks.Remove(wt);
                  if (wt->GetEndTime() > t0) {
                     t0 = wt->GetEndTime();
                  }
               }
            }
         }

         // Use end time of all wave tracks if none selected
         if (!sel) {
            t0 = allt0;
         }

         // Pad selected/all wave tracks to make them all the same length
         for (Track *tt = it.First(); tt; tt = it.Next()) {
            if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) {
               wt = (WaveTrack *)tt;
               t1 = wt->GetEndTime();
               if (t1 < t0) {
                  WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack();
                  newTrack->InsertSilence(0.0, t0 - t1);
                  newTrack->Flush();
                  wt->Clear(t1, t0);
                  wt->Paste(t1, newTrack);
                  delete newTrack;
               }
               newRecordingTracks.Add(wt);
            }
         }

         t1 = 1000000000.0;     // record for a long, long time (tens of years)
      }
      else {
         recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2);
         for (int c = 0; c < recordingChannels; c++) {
            WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack();

            int initialheight = newTrack->GetHeight();

            newTrack->SetOffset(t0);

            if (recordingChannels <= 2) {
               newTrack->SetHeight(initialheight/recordingChannels);
            }
            else {
               newTrack->SetMinimized(true);
            }

            if (recordingChannels == 2) {
               if (c == 0) {
                  newTrack->SetChannel(Track::LeftChannel);
                  newTrack->SetLinked(true);
               }
               else {
                  newTrack->SetChannel(Track::RightChannel);
                  newTrack->SetTeamed(true);
               }
            }
            else {
               newTrack->SetChannel( Track::MonoChannel );
            }

            newRecordingTracks.Add(newTrack);
         }
         
         // msmeyer: StartStream calls a callback which triggers auto-save, so
         // we add the tracks where recording is done into now. We remove them
         // later if starting the stream fails
         for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++)
            t->Add(newRecordingTracks[i]);
      }

      int token = gAudioIO->StartStream(playbackTracks,
                                        newRecordingTracks,
/* REQUIRES PORTMIDI */
//                                        midiTracks,
                                        t->GetTimeTrack(),
                                        p->GetRate(), t0, t1, p);

      bool success = (token != 0);
      
      if (success) {
         p->SetAudioIOToken(token);
         mBusyProject = p;
         SetVUMeters(p);
      }
      else {
         // msmeyer: Delete recently added tracks if opening stream fails
         if (!shifted) {
            for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) {
               t->Remove(newRecordingTracks[i]);
               delete newRecordingTracks[i];
            }
         }

         // msmeyer: Show error message if stream could not be opened
         wxMessageBox(_("Error while opening sound device. "
            wxT("Please check the input device settings and the project sample rate.")),
                      _("Error"), wxOK | wxICON_EXCLAMATION, this);

         SetPlay(false);
         SetStop(false);
         SetRecord(false);
      }
   }
}