コード例 #1
0
ファイル: NoiseRemoval.cpp プロジェクト: andreipaga/audacity
void EffectNoiseRemoval::CleanSpeechMayWriteNoiseGate()
{
	AudacityProject * project = GetActiveProject();
	if( !project || !project->GetCleanSpeechMode() )
      return;
   wxFFile noiseGateFile(wxT("noisegate.nrp"), wxT("wb"));
   bool flag = noiseGateFile.IsOpened();
   if (flag == true) {
      int expectedCount = (windowSize / 2) * sizeof(float);
      // FIX-ME: Should we check return value on Write?
      noiseGateFile.Write(mNoiseGate, expectedCount);
      noiseGateFile.Close();
   }
}
コード例 #2
0
// Gets all commands that are valid for this mode.
wxArrayString BatchCommands::GetAllCommands()
{
   wxArrayString commands;
   wxString command;
   commands.Clear();

   AudacityProject *project = GetActiveProject();
   if (!project)
      return commands;

   EffectArray * effects;
   unsigned int i;

   // CLEANSPEECH remnant
   for(i=0;i<sizeof(SpecialCommands)/sizeof(SpecialCommands[0]);i++)
   {
      commands.Add( SpecialCommands[i] );
   }
   // end CLEANSPEECH remnant
   
   int additionalEffects=ADVANCED_EFFECT;
#ifdef CLEANSPEECH
   if( project->GetCleanSpeechMode() )
       additionalEffects = 0;
#endif   // CLEANSPEECH

   effects = EffectManager::Get().GetEffects(PROCESS_EFFECT | BUILTIN_EFFECT | PLUGIN_EFFECT | additionalEffects);
   for(i=0; i<effects->GetCount(); i++) {
      if ((*effects)[i]->SupportsChains()) {
         command=(*effects)[i]->GetEffectIdentifier();
         if (!command.IsEmpty()) {
            commands.Add( command);
         }
      }
   }
   delete effects;

   /* This is for later in development: include the menu commands.
         CommandManager * mManager = project->GetCommandManager();
         wxArrayString mNames;
         mNames.Clear();
         mManager->GetAllCommandNames(mNames, false);
         for(i=0; i<mNames.GetCount(); i++) {
            commands.Add( mNames[i] );
         }
   */
   return commands;
}
コード例 #3
0
void EffectNoiseRemoval::CleanSpeechMayWriteNoiseGate()
{
   AudacityProject * project = GetActiveProject();
   if( !project || !project->GetCleanSpeechMode() )
      return;

   // code borrowed from ThemeBase::SaveComponents() - MJS
   // IF directory doesn't exist THEN create it
   if( !wxDirExists( FileNames::NRPDir() ))
   {
      /// \bug 1 in wxWidgets documentation; wxMkDir returns false if 
      /// directory didn't exist, even if it successfully creates it.
      /// so we create and then test if it exists instead.
      /// \bug 2 in wxWidgets documentation; wxMkDir has only one argument
      /// under MSW
#ifdef __WXMSW__
      wxMkDir( FileNames::NRPDir().fn_str() );
#else
      wxMkDir( FileNames::NRPDir().fn_str(), 0700 );
#endif
      if( !wxDirExists( FileNames::NRPDir() ))
      {
         wxMessageBox(
            wxString::Format( 
            _("Could not create directory:\n  %s"),
               FileNames::NRPDir().c_str() ));
         return;
      }
   }

   wxString fileName = FileNames::NRPFile();
   fileName = PlatformCompatibility::GetLongFileName(fileName);
   wxFFile noiseGateFile(fileName, wxT("wb"));
   bool flag = noiseGateFile.IsOpened();
   if (flag == true) {
      int expectedCount = (mWindowSize / 2) * sizeof(float);
      // FIX-ME: Should we check return value on Write?
      noiseGateFile.Write(mNoiseThreshold, expectedCount);
      noiseGateFile.Close();
   }
   else {
      wxMessageBox(
         wxString::Format( 
         _("Could not open file:\n  %s"), fileName.c_str() ));
      return;
   }
}
コード例 #4
0
BatchProcessDialog::BatchProcessDialog(wxWindow * parent):
    wxDialog(parent, wxID_ANY, _("Apply Chain"),
             wxDefaultPosition, wxDefaultSize,
             wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
    AudacityProject * p = GetActiveProject();
    if (p->GetCleanSpeechMode())
    {
        SetTitle(_("CleanSpeech Batch Processing"));
    }

    SetLabel(_("Apply Chain"));         // Provide visual label
    SetName(_("Apply Chain"));          // Provide audible label
    Populate();

    mAbort = false;
}
コード例 #5
0
bool EffectNoiseRemoval::PromptUser()
{
   NoiseRemovalDialog dlog(this, mParent);
   dlog.mGain = -mNoiseGain;
   dlog.mFreq = mFreqSmoothingHz;
   dlog.mTime = mAttackDecayTime;

   if( !mHasProfile )
   {
      AudacityProject * p = GetActiveProject();
      if (p->GetCleanSpeechMode())
         CleanSpeechMayReadNoisegate();
   }

   // We may want to twiddle the levels if we are setting
   // from an automation dialog, the only case in which we can
   // get here without any wavetracks.
   bool bAllowTwiddleSettings = (mWaveTracks==NULL); 

   if (mHasProfile || bAllowTwiddleSettings ) {
      dlog.m_pButton_Preview->Enable(mWaveTracks != NULL);
      dlog.m_pButton_RemoveNoise->SetDefault();
   } else {
      dlog.m_pButton_Preview->Enable(false);
      dlog.m_pButton_RemoveNoise->Enable(false);
   }

   dlog.TransferDataToWindow();
   dlog.CentreOnParent();
   dlog.ShowModal();
   
   if (dlog.GetReturnCode() == 0) {
      return false;
   }

   mNoiseGain = -dlog.mGain;
   mFreqSmoothingHz = dlog.mFreq;
   mAttackDecayTime = dlog.mTime;
   gPrefs->Write(wxT("/CsPresets/NoiseGain"), mNoiseGain);
   gPrefs->Write(wxT("/CsPresets/NoiseFreqSmoothing"), mFreqSmoothingHz);
   gPrefs->Write(wxT("/CsPresets/NoiseAttackDecayTime"), mAttackDecayTime);

   mDoProfile = (dlog.GetReturnCode() == 1);
   return true;
}
コード例 #6
0
ファイル: BatchCommands.cpp プロジェクト: andreipaga/audacity
// Gets all commands that are valid for this mode.
wxArrayString BatchCommands::GetAllCommands()
{
   wxArrayString commands;
   wxString command;
   commands.Clear();

   AudacityProject *project = GetActiveProject();
   if (!project)
      return commands;

   EffectArray * effects;
   unsigned int i;

   for(i=0;i<sizeof(SpecialCommands)/sizeof(SpecialCommands[0]);i++)
   {
      commands.Add( SpecialCommands[i] );
   }
   
   int additionalEffects=ADVANCED_EFFECT;
   if( project->GetCleanSpeechMode() )
       additionalEffects = 0;
   effects = Effect::GetEffects(PROCESS_EFFECT | BUILTIN_EFFECT | additionalEffects);
   for(i=0; i<effects->GetCount(); i++) {
      command=(*effects)[i]->GetEffectName();
      command.Replace( wxT("..."), wxT(""));
      commands.Add( command);
   }
   delete effects;

/* This is for later in development: include the menu commands.
   CommandManager * mManager = project->GetCommandManager();
   wxArrayString mNames;
   mNames.Clear();
   mManager->GetAllCommandNames(mNames, false);
   for(i=0; i<mNames.GetCount(); i++) {
      commands.Add( mNames[i] );
   }
*/
   return commands;
}
コード例 #7
0
ファイル: NoiseRemoval.cpp プロジェクト: andreipaga/audacity
wxSizer *NoiseRemovalDialog::MakeNoiseRemovalDialog(bool call_fit /* = true */, 
																	 bool set_sizer /* = true */)
{
   wxBoxSizer *mainSizer = new wxBoxSizer( wxVERTICAL );
   wxStaticBoxSizer *group;
   wxControl *item;
   
   item = new wxStaticText(this, -1,
                   _("Noise Removal by Dominic Mazzoni"), wxDefaultPosition,
                   wxDefaultSize, wxALIGN_CENTRE );
   mainSizer->Add(item, 0, wxALIGN_CENTRE|wxALL, 5);
	AudacityProject * p;
	p=GetActiveProject();
	bool bCleanSpeechMode = p && p->GetCleanSpeechMode();
	if( bCleanSpeechMode )
	{
      item = new wxStaticText(this, -1,
         _("Entire recording should have been Normalized prior to this step.\n\nFor noisy speech, find the best tradeoff for quieting the gaps between phrases and\nnot causing the voice to sound distorted. For good audio with low noise, a setting\nmore to the left should work well. Leveling and TruncateSilence work better with\nmore of the noise removed, even if the voice ends up sounding somewhat distorted.\nYour objective is that the softly spoken words can be heard more clearly."),
           wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
      	  mainSizer->Add(item, 0, wxALIGN_LEFT|wxALL, 15);
	}
   // Step 1
   group = new wxStaticBoxSizer(new wxStaticBox(this, -1,
	bCleanSpeechMode ? 
		_("Preparation Steps")
	: 	_("Step 1")
		), wxVERTICAL);

   item = new wxStaticText(this, -1,
                           bCleanSpeechMode ?
                              _("Listen carefully to section with some speech and some silence to check before/after.\nSelect a few seconds of just noise ('thinner' part of wave pattern usually between\nspoken phrases or during pauses) so Audacity knows what to filter out, then click")
                              :  _("Select a few seconds of just noise\nso Audacity knows what to filter out, then\nclick Get Noise Profile:"),
                           wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );

   group->Add(item, 0, wxALIGN_CENTRE|wxALL, 5 );

   m_pButton_GetProfile = new wxButton(this, ID_BUTTON_GETPROFILE, _("Get Noise Profile"), wxDefaultPosition, wxDefaultSize, 0 );
   group->Add(m_pButton_GetProfile, 0, wxALIGN_CENTRE|wxALL, 5 );

   mainSizer->Add( group, 1, wxALIGN_CENTRE|wxALL|wxGROW, 5 );
   
   // Step 2
   
   group = new wxStaticBoxSizer(new wxStaticBox(this, -1,
			bCleanSpeechMode ? 
         	_("Actually Remove Noise")
			:  _("Step 2")), wxVERTICAL);

   item = new wxStaticText(this, -1,
	                        bCleanSpeechMode ?
                                 _("Select what part of the audio you want filtered (Ctrl-A = All), chose how much noise\nyou want filtered out with Slider below, and then click 'OK' to remove noise.\nFind best setting with Ctrl-Z to Undo, Select All, and change Slider position.")
                              :	_("Select all of the audio you want filtered,\nchoose how much noise you want filtered out,\nand then click 'OK' to remove noise.\n"),
                           wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
   group->Add(item, 0, wxALIGN_CENTRE|wxALL, 5 );

   m_pSlider = new wxSlider(this, -1, mLevel, 0, MAX_NOISE_LEVEL,    //lda-131a mLevel=0 indicates skip
										wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
   group->Add(m_pSlider, 1, wxEXPAND|wxALIGN_CENTRE|wxLEFT | wxRIGHT | wxTOP, 5 );

   wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL);
   item = new wxStaticText(this, -1, _("None"));
   hSizer->Add(item, 0, wxALIGN_CENTRE|wxLEFT | wxRIGHT | wxBOTTOM, 5 );   
   hSizer->Add(10, 10, 1, wxALIGN_CENTRE | wxLEFT | wxRIGHT | wxBOTTOM, 5);
	if( bCleanSpeechMode )
	{
      item = new wxStaticText(this, -1, _("Medium"));
      hSizer->Add(item, 0, wxALIGN_CENTRE|wxLEFT | wxRIGHT | wxBOTTOM, 5 );   
      hSizer->Add(10, 10, 1, wxALIGN_CENTRE | wxLEFT | wxRIGHT | wxBOTTOM, 5);
	}
   item = new wxStaticText(this, -1, 
	bCleanSpeechMode ?
		_("Extreme (May Distort)")
	:	_("More"));
   hSizer->Add(item, 0, wxALIGN_CENTRE|wxLEFT | wxRIGHT | wxBOTTOM, 5 );
   group->Add(hSizer, 1, wxEXPAND|wxALIGN_CENTRE|wxALL, 5 );
   
   hSizer = new wxBoxSizer(wxHORIZONTAL);

   m_pButton_Preview = new wxButton(this, ID_BUTTON_PREVIEW, m_pEffect->GetPreviewName());
   hSizer->Add(m_pButton_Preview, 0, wxALIGN_LEFT | wxALL, 5);
   
   hSizer->Add(25, 5); // horizontal spacer

   item = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
   hSizer->Add(item, 0, wxALIGN_CENTER | wxALL, 5 );

   hSizer->Add(25, 5); // horizontal spacer
   
	m_pButton_RemoveNoise = new wxButton(this, wxID_OK, _("&OK"), wxDefaultPosition, wxDefaultSize, 0 );
   hSizer->Add(m_pButton_RemoveNoise, 0, wxALIGN_RIGHT | wxALL, 5 );

	group->Add(hSizer, 0, wxALIGN_CENTER | wxALL, 5 );
   mainSizer->Add( group, 0, wxALIGN_CENTRE|wxALL|wxGROW, 5 );

   if (set_sizer) {
      this->SetAutoLayout( TRUE );
      this->SetSizer( mainSizer );
      if (call_fit) {
         mainSizer->Fit( this );
         mainSizer->SetSizeHints( this );
      }
   }
    
   return mainSizer;
}
コード例 #8
0
void BatchProcessDialog::OnApplyToFiles(wxCommandEvent &event)
{
    long item = mChains->GetNextItem(-1,
                                     wxLIST_NEXT_ALL,
                                     wxLIST_STATE_SELECTED);
    if (item == -1) {
        wxMessageBox(_("No chain selected"));
        return;
    }

    wxString name = mChains->GetItemText(item);
    gPrefs->Write(wxT("/Batch/ActiveChain"), name);

    AudacityProject *project = GetActiveProject();
    if (!project->GetIsEmpty()) {
        wxMessageBox(_("Please save and close the current project first."));
        return;
    }

    wxString path = gPrefs->Read(wxT("/DefaultOpenPath"), ::wxGetCwd());
    wxString prompt =  project->GetCleanSpeechMode() ?
                       _("Select vocal file(s) for batch CleanSpeech Chain...") :
                       _("Select file(s) for batch processing...");
    wxString fileSelector = project->GetCleanSpeechMode() ?
                            _("Vocal files (*.wav;*.mp3)|*.wav;*.mp3|WAV files (*.wav)|*.wav|MP3 files (*.mp3)|*.mp3") :
                            _("All files (*.*)|*.*|WAV files (*.wav)|*.wav|AIFF files (*.aif)|*.aif|AU files (*.au)|*.au|MP3 files (*.mp3)|*.mp3|Ogg Vorbis files (*.ogg)|*.ogg|FLAC files (*.flac)|*.flac"
                             );

    FileDialog dlog(this, prompt,
                    path, wxT(""), fileSelector,
                    wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);

    if (dlog.ShowModal() != wxID_OK) {
        return;
    }

    wxArrayString files;
    dlog.GetPaths(files);

    files.Sort();

    wxDialog d(this, wxID_ANY, GetTitle());
    ShuttleGui S(&d, eIsCreating);

    S.StartVerticalLay(false);
    {
        S.StartStatic(_("Applying..."), 1);
        {
            wxImageList *imageList = new wxImageList(9, 16);
            imageList->Add(wxIcon(empty_9x16_xpm));
            imageList->Add(wxIcon(arrow_xpm));

            S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
                       wxLC_SINGLE_SEL);
            mList = S.AddListControlReportMode();
            mList->AssignImageList(imageList, wxIMAGE_LIST_SMALL);
            mList->InsertColumn(0, _("File"), wxLIST_FORMAT_LEFT);
        }
        S.EndStatic();

        S.StartHorizontalLay(wxCENTER, false);
        {
            S.Id(wxID_CANCEL).AddButton(_("&Cancel"));
        }
        S.EndHorizontalLay();
    }
    S.EndVerticalLay();

    int i;
    for (i = 0; i < (int)files.GetCount(); i++ ) {
        mList->InsertItem(i, files[i], i == 0);
    }

    // Set the column size for the files list.
    mList->SetColumnWidth(0, wxLIST_AUTOSIZE);

    int width = mList->GetColumnWidth(0);
    wxSize sz = mList->GetClientSize();
    if (width > sz.GetWidth() && width < 500) {
        sz.SetWidth(width);
        mList->SetInitialSize(sz);
    }

    d.Layout();
    d.Fit();
    d.SetSizeHints(d.GetSize());
    d.CenterOnScreen();
    d.Move(-1, 0);
    d.Show();
    Hide();

    mBatchCommands.ReadChain(name);
    for (i = 0; i < (int)files.GetCount(); i++) {
        wxWindowDisabler wd(&d);
        if (i > 0) {
            //Clear the arrow in previous item.
            mList->SetItemImage(i - 1, 0, 0);
        }
        mList->SetItemImage(i, 1, 1);
        mList->EnsureVisible(i);

        project->OnRemoveTracks();
        project->Import(files[i]);
        project->OnSelectAll();
        if (!mBatchCommands.ApplyChain()) {
            break;
        }

        if (!d.IsShown() || mAbort) {
            break;
        }
        UndoManager *um = project->GetUndoManager();
        um->ClearStates();
    }
    project->OnRemoveTracks();
}
コード例 #9
0
void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S)
{
   wxString step1Label;
   wxString step1Prompt;
   wxString step2Label;
   wxString step2Prompt;

   bool bCleanSpeechMode = false;

   AudacityProject * project = GetActiveProject();
   if( project && project->GetCleanSpeechMode() ) {
      bCleanSpeechMode = true;
   }

   if (bCleanSpeechMode) {
      // We're not marking these as translatable because most people
      // don't use CleanSpeech so it'd be a waste of time for most
      // translators
      step1Label = wxT("Preparation Step");
      step1Prompt = wxT("Listen carefully to section with some speech "
                        wxT("and some silence to check before/after.\n")
                        wxT("Select a few seconds of just noise ('thinner' ")
                        wxT("part of wave pattern usually between\nspoken ")
                        wxT("phrases or during pauses) so Audacity knows ")
                        wxT("what to filter out, then click"));
      step2Label = wxT("Actually Remove Noise");
      step2Prompt = wxT("Select what part of the audio you want filtered "
                        wxT("(Ctrl-A = All), chose how much noise\nyou want ")
                        wxT("filtered out with sliders below, and then click ")
                        wxT("'OK' to remove noise.\nFind best setting with ")
                        wxT("Ctrl-Z to Undo, Select All, and change ")
                        wxT("the slider positions."));
   }
   else {
      step1Label = _("Step 1");
      step1Prompt = _("Select a few seconds of just noise so Audacity knows what to filter out,\nthen click Get Noise Profile:");
      step2Label = _("Step 2");
      step2Prompt = _("Select all of the audio you want filtered, choose how much noise you want\nfiltered out, and then click 'OK' to remove noise.\n");
   }

   S.StartHorizontalLay(wxCENTER, false);
   {
      S.AddTitle(_("Noise Removal by Dominic Mazzoni"));
   }
   S.EndHorizontalLay();
   
   S.StartStatic(step1Label);
   {
      S.AddVariableText(step1Prompt);
      m_pButton_GetProfile = S.Id(ID_BUTTON_GETPROFILE).
         AddButton(_("Get Noise Profile"));
   }
   S.EndStatic();

   S.StartStatic(step2Label);
   {
      S.AddVariableText(step2Prompt);

      S.StartMultiColumn(3, wxEXPAND);
      S.SetStretchyCol(2);
      {
         mGainT = S.Id(ID_GAIN_TEXT).AddTextBox(_("Noise reduction (dB):"),
                                                wxT(""),
                                                0);
         S.SetStyle(wxSL_HORIZONTAL);
         mGainS = S.Id(ID_GAIN_SLIDER).AddSlider(wxT(""), 0, GAIN_MAX);
         mGainS->SetName(_("Noise reduction"));
         mGainS->SetRange(GAIN_MIN, GAIN_MAX);
         mGainS->SetSizeHints(150, -1);

         mFreqT = S.Id(ID_FREQ_TEXT).AddTextBox(_("Frequency smoothing (Hz):"),
                                                wxT(""),
                                                0);
         S.SetStyle(wxSL_HORIZONTAL);
         mFreqS = S.Id(ID_FREQ_SLIDER).AddSlider(wxT(""), 0, FREQ_MAX);
         mFreqS->SetName(_("Frequency smoothing"));
         mFreqS->SetRange(FREQ_MIN, FREQ_MAX);
         mFreqS->SetSizeHints(150, -1);

         mTimeT = S.Id(ID_FREQ_TEXT).AddTextBox(_("Attack/decay time (secs):"),
                                                wxT(""),
                                                0);
         S.SetStyle(wxSL_HORIZONTAL);
         mTimeS = S.Id(ID_TIME_SLIDER).AddSlider(wxT(""), 0, TIME_MAX);
         mTimeS->SetName(_("Attach/decay time"));
         mTimeS->SetRange(TIME_MIN, TIME_MAX);
         mTimeS->SetSizeHints(150, -1);
      }
      S.EndMultiColumn();
   }
   S.EndStatic();
}
コード例 #10
0
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);
      }
   }
}