Exemple #1
0
int TrackList::GetNumExportChannels(bool selectionOnly)
{
   /* counters for tracks panned different places */
   int numLeft = 0;
   int numRight = 0;
   int numMono = 0;
   /* track iteration kit */
   Track *tr;
   TrackListIterator iter;

   for (tr = iter.First(this); tr != NULL; tr = iter.Next()) {

      // Want only unmuted wave tracks.
      if ((tr->GetKind() != Track::Wave) || tr->GetMute())
         continue;

      // do we only want selected ones?
      if (selectionOnly && !(tr->GetSelected())) {
         //want selected but this one is not
         continue;
      }

      // Found a left channel
      if (tr->GetChannel() == Track::LeftChannel) {
         numLeft++;
      }

      // Found a right channel
      else if (tr->GetChannel() == Track::RightChannel) {
         numRight++;
      }

      // Found a mono channel, but it may be panned
      else if (tr->GetChannel() == Track::MonoChannel) {
         float pan = ((WaveTrack*)tr)->GetPan();

         // Figure out what kind of channel it should be
         if (pan == -1.0) {   // panned hard left
            numLeft++;
         }
         else if (pan == 1.0) {  // panned hard right
            numRight++;
         }
         else if (pan == 0) { // panned dead center
            numMono++;
         }
         else {   // panned somewhere else
            numLeft++;
            numRight++;
         }
      }
   }

   // if there is stereo content, report 2, else report 1
   if (numRight > 0 || numLeft > 0) {
      return 2;
   }

   return 1;
}
Exemple #2
0
bool Track::LinkConsistencyCheck()
{
   // Sanity checks for linked tracks; unsetting the linked property
   // doesn't fix the problem, but it likely leaves us with orphaned
   // blockfiles instead of much worse problems.
   bool err = false;
   if (GetLinked())
   {
      Track *l = GetLink();
      if (l)
      {
         // A linked track's partner should never itself be linked
         if (l->GetLinked())
         {
            wxLogWarning(
               wxT("Left track %s had linked right track %s with extra right track link.\n   Removing extra link from right track."),
               GetName(), l->GetName());
            err = true;
            l->SetLinked(false);
         }

         // Channels should be left and right
         if ( !(  (GetChannel() == Track::LeftChannel &&
                     l->GetChannel() == Track::RightChannel) ||
                  (GetChannel() == Track::RightChannel &&
                     l->GetChannel() == Track::LeftChannel) ) )
         {
            wxLogWarning(
               wxT("Track %s and %s had left/right track links out of order. Setting tracks to not be linked."),
               GetName(), l->GetName());
            err = true;
            SetLinked(false);
         }
      }
      else
      {
         wxLogWarning(
            wxT("Track %s had link to NULL track. Setting it to not be linked."),
            GetName());
         err = true;
         SetLinked(false);
      }
   }

   return ! err;
}
Exemple #3
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;
}
Exemple #4
0
ExportMixerDialog::ExportMixerDialog( TrackList *tracks, bool selectionOnly,
                                      int maxNumChannels, wxWindow *parent, wxWindowID id, const wxString &title,
                                      const wxPoint &position, const wxSize& size, long style ) :
    wxDialog( parent, id, title, position, size, style | wxRESIZE_BORDER )
{
    int numTracks = 0;
    TrackListIterator iter( tracks );

    for( Track *t = iter.First(); t; t = iter.Next() )
        if( t->GetKind() == Track::Wave && ( t->GetSelected() || !selectionOnly ) )
        {
            numTracks++;
            if( t->GetChannel() == Track::LeftChannel )
            {
                mTrackNames.Add( t->GetName() + _( " - Left" ) );
                mTrackNames.Add( t->GetName() + _( " - Right" ) );
                t = iter.Next();
                numTracks++;
            }
            else
                mTrackNames.Add( t->GetName() );
        }

    mMixerSpec = new MixerSpec( numTracks, maxNumChannels );

    wxBoxSizer *vertSizer = new wxBoxSizer( wxVERTICAL );

    wxWindow *mixerPanel = new ExportMixerPanel( mMixerSpec, mTrackNames, this,
            ID_MIXERPANEL, wxDefaultPosition, wxSize( 400, -1 ) );
    vertSizer->Add( mixerPanel, 1, wxEXPAND | wxALIGN_CENTRE | wxALL, 5 );

    wxBoxSizer *horSizer = new wxBoxSizer( wxHORIZONTAL );

    mChannelsText = new wxStaticText( this, -1,
                                      wxString::Format( _( "Output Channels: %2d" ),
                                              mMixerSpec->GetNumChannels() ) );
    horSizer->Add( mChannelsText, 0, wxALIGN_LEFT | wxALL, 5 );

    wxSlider *channels = new wxSlider( this, ID_SLIDER_CHANNEL,
                                       mMixerSpec->GetNumChannels(), 1, mMixerSpec->GetMaxNumChannels(),
                                       wxDefaultPosition, wxSize( 300, -1 ) );
    horSizer->Add( channels, 0, wxEXPAND | wxALL, 5 );

    vertSizer->Add( horSizer, 0, wxALIGN_CENTRE | wxALL, 5 );

    horSizer = new wxBoxSizer( wxHORIZONTAL );

    wxButton *cancel = new wxButton( this, wxID_CANCEL, _( "&Cancel" ) );
    horSizer->Add( cancel, 0, wxALIGN_CENTRE | wxALL, 5 );

    wxButton *ok = new wxButton( this, wxID_OK, _( "&OK" ) );
    ok->SetDefault();
    horSizer->Add( ok, 0, wxALIGN_CENTRE | wxALL, 5 );

    vertSizer->Add( horSizer, 0, wxALIGN_CENTRE | wxALL, 5 );

    SetAutoLayout( true );
    SetSizer( vertSizer );
    vertSizer->Fit( this );
    vertSizer->SetSizeHints( this );

    SetSizeHints( 640, 480, 20000, 20000 );

    SetSize( 640, 480 );
}
Exemple #5
0
/*
 * This first function contains the code common to both
 * Export() and ExportLossy()
 *
 * For safety, if the file already exists it stores the filename
 * the user wants in actualName, and returns a temporary file name.
 * The calling function should rename the file when it's successfully
 * exported.
 */
wxString ExportCommon( AudacityProject *project, wxString format,
                       wxString defaultExtension, bool selectionOnly, double *t0, double *t1,
                       int *numChannels, wxString &actualName, int maxNumChannels,
                       MixerSpec **mixerSpec )
{
    TrackList *tracks = project->GetTracks();

    /* First analyze the selected audio, perform sanity checks, and provide
     * information as appropriate. */

    /* Tally how many are right, left, mono, and make sure at
       least one track is selected (if selectionOnly==true) */

    int numSelected = 0, numLeft = 0, numRight = 0, numMono = 0;
    float earliestBegin = *t1;
    float latestEnd = *t0;

    TrackListIterator iter1(tracks);
    Track *tr = iter1.First();

    while (tr) {
        if (tr->GetKind() == Track::Wave) {
            if (tr->GetSelected() || !selectionOnly) {

                numSelected++;

                if (tr->GetChannel() == Track::LeftChannel)
                    numLeft++;
                else if (tr->GetChannel() == Track::RightChannel)
                    numRight++;
                else if (tr->GetChannel() == Track::MonoChannel) {
                    // It's a mono channel, but it may be panned
                    float pan = ((WaveTrack*)tr)->GetPan();

                    if (pan == -1.0)
                        numLeft++;
                    else if (pan == 1.0)
                        numRight++;
                    else if (pan == 0)
                        numMono++;
                    else {
                        // Panned partially off-center. Mix as stereo.
                        numLeft++;
                        numRight++;
                    }
                }
                if(tr->GetOffset() < earliestBegin)
                    earliestBegin = tr->GetOffset();

                if(tr->GetEndTime() > latestEnd)
                    latestEnd = tr->GetEndTime();

            }
        }

        tr = iter1.Next();
    }

    if(*t0 < earliestBegin)
        *t0 = earliestBegin;

    if(*t1 > latestEnd)
        *t1 = latestEnd;

    if (numSelected == 0 && selectionOnly) {
        wxMessageBox(_("No tracks are selected! Use Ctrl-A (Select All)\nChoose Export... to export all tracks."),
                     _("Unable to export"),
                     wxOK | wxICON_INFORMATION);

        return wxT("");
    }

    /* Detemine if exported file will be stereo or mono or multichannel,
       and if mixing will occur */

    bool downMix = (gPrefs->Read( wxT("/FileFormats/ExportDownMix" ), true ) !=0) ? true:false ;

    int channels;
    if( downMix || !mixerSpec )
    {
        if (numRight > 0 || numLeft > 0)
            channels = 2;
        else
            channels = 1;

        numRight += numMono;
        numLeft += numMono;

        if (numLeft > 1 || numRight > 1)
            if (channels == 2) {
                ShowWarningDialog(project, wxT("MixStereo"),
                                  _("Your tracks will be mixed down to two stereo channels in the exported file."));
            }
            else {
                ShowWarningDialog(project, wxT("MixMono"),
                                  _("Your tracks will be mixed down to a single mono channel in the exported file."));
            }
    }
    else
    {
        ExportMixerDialog md( tracks, selectionOnly, maxNumChannels, NULL,
                              1, _( "Advanced Mixing Options" ) );

        if( md.ShowModal() != wxID_OK )
            return wxT( "" );

        *mixerSpec = new MixerSpec( *( md.GetMixerSpec() ) );
        channels = ( *mixerSpec )->GetNumChannels();
    }

    /* Prepare and display the filename selection dialog */

    wxString path = gPrefs->Read(wxT("/DefaultExportPath"),
                                 ::wxGetCwd());
    wxString nameOnly;
    wxString extension;
    wxString defaultName = project->GetName();
    wxString fName;
    wxString maskString;
    wxString endOfPathSep;

#if 0 // this code shouldn't be here --dmazzoni
    //MERGE exercise exception
    if (defaultName == wxT("ThrowExceptionOnExport")) {  //lda
        throw("Exercise exception");
    }
#endif

    if (defaultExtension.Left(1) == wxT("."))
        defaultExtension =
            defaultExtension.Right(defaultExtension.Length()-1);

    maskString.Printf(wxT("%s files (*.%s)|*.%s|All files (*.*)|*.*"), format.c_str(),
                      defaultExtension.c_str(), defaultExtension.c_str());

    bool fileOkay;

    do {
        fileOkay = true;

        fName = defaultName + wxT(".") + defaultExtension;
        fName = wxFileSelector(wxString::Format(_("Save %s File As:"),
                                                format.c_str()),
                               path,
                               fName,       // default file name
                               defaultExtension,
                               maskString,
                               wxSAVE | wxOVERWRITE_PROMPT);

        if (fName.Length() >= 256) {
            wxMessageBox
            (_("Sorry, pathnames longer than 256 characters not supported."));
            return wxT("");
        }

        if (fName == wxT(""))
            return wxT("");

        ::wxSplitPath(fName, &path, &nameOnly, &extension);

        //
        // Make sure the user doesn't accidentally save the file
        // as an extension with no name, like just plain ".wav".
        //

        if ((nameOnly.Left(1)==wxT(".") && extension==wxT("")) ||
                (nameOnly==wxT("") && extension!=wxT(""))) {
            wxString prompt =
                _("Are you sure you want to save the file as \"")+
                ::wxFileNameFromPath(fName)+wxT("\"?\n");

            int action = wxMessageBox(prompt,
                                      wxT("Warning"),
                                      wxYES_NO | wxICON_EXCLAMATION,
                                      project);

            fileOkay = (action == wxYES);
            continue;
        }

        //
        // Check the extension - add the default if it's not there,
        // and warn user if it's abnormal.
        //

        wxString defaultExtension3 = defaultExtension;
        if (defaultExtension.Length() > 3)
            defaultExtension = defaultExtension.Left(3);

        if (extension == wxT("")) {
#ifdef __WXMSW__
            // Windows prefers 3-char uppercase extensions
            extension = defaultExtension;
#else
            // Linux and Mac prefer lowercase extensions
            extension = defaultExtension.Lower();
#endif
        }
        else if (extension.Upper() != defaultExtension.Upper() &&
                 extension.Upper() != defaultExtension3.Upper()) {
#ifdef __WXMSW__
            // Windows prefers 3-char extensions
            defaultExtension3 = defaultExtension3;
#endif

            wxString prompt;
            prompt.Printf(_("You are about to save a %s file with the name %s.\nNormally these files end in %s, and some programs will not open files with nonstandard extensions.\nAre you sure you want to save the file under this name?"),
                          format.c_str(),
                          (wxT("\"")+nameOnly+wxT(".")+extension+wxT("\"")).c_str(),
                          (wxT("\".")+defaultExtension+wxT("\"")).c_str());

            int action = wxMessageBox(prompt,
                                      wxT("Warning"),
                                      wxYES_NO | wxICON_EXCLAMATION,
                                      project);

            if (action == wxYES)
                fileOkay = true;
            else {
                fileOkay = false;
                defaultName = nameOnly + wxT(".") + extension;
            }
        }

        if (path.Length() > 0 && path.Last() == wxFILE_SEP_PATH)
            endOfPathSep = wxT("");
        else
            endOfPathSep = wxFILE_SEP_PATH;

        fName = path + endOfPathSep +
                nameOnly + wxT(".") + extension;
    } while(!fileOkay);

    /*
     * Ensure that exporting a file by this name doesn't overwrite
     * one of the existing files in the project.  (If it would
     * overwrite an existing file, DirManager tries to rename the
     * existing file.)
     */

    if (!project->GetDirManager()->EnsureSafeFilename(wxFileName(fName)))
        return wxT("");

    gPrefs->Write(wxT("/DefaultExportPath"), path);

    *numChannels = channels;

    /*
     * To be even MORE safe, return a temporary file name based
     * on this one...
     */

    actualName = fName;

    int suffix = 0;
    while(::wxFileExists(fName)) {
        fName = path + endOfPathSep +
                nameOnly + wxString::Format(wxT("%d"), suffix) + wxT(".") + extension;
        suffix++;
    }

    return fName;
}
Exemple #6
0
bool ExportOGG(AudacityProject *project,
               bool stereo, wxString fName,
               bool selectionOnly, double t0, double t1)
{
   double    rate    = project->GetRate();
   wxWindow  *parent = project;
   TrackList *tracks = project->GetTracks();
   double    quality = (gPrefs->Read("/FileFormats/OggExportQuality", 50)/(float)100.0);

   wxLogNull logNo;            // temporarily disable wxWindows error messages 
   bool      cancelling = false;

   wxFFile outFile(fName, "wb");

   if(!outFile.IsOpened()) {
      wxMessageBox(_("Unable to open target file for writing"));
      return false;
   }

   // All the Ogg and Vorbis encoding data
   ogg_stream_state stream;
   ogg_page         page;
   ogg_packet       packet;

   vorbis_info      info;
   vorbis_comment   comment;
   vorbis_dsp_state dsp;
   vorbis_block     block;

   // Encoding setup
   vorbis_info_init(&info);
   vorbis_encode_init_vbr(&info, stereo ? 2 : 1, int(rate + 0.5), quality);

   vorbis_comment_init(&comment);
   // If we wanted to add comments, we would do it here

   // Set up analysis state and auxiliary encoding storage
   vorbis_analysis_init(&dsp, &info);
   vorbis_block_init(&dsp, &block);

   // Set up packet->stream encoder.  According to encoder example,
   // a random serial number makes it more likely that you can make
   // chained streams with concatenation.
   srand(time(NULL));
   ogg_stream_init(&stream, rand());

   // First we need to write the required headers:
   //    1. The Ogg bitstream header, which contains codec setup params
   //    2. The Vorbis comment header
   //    3. The bitstream codebook.
   //
   // After we create those our responsibility is complete, libvorbis will
   // take care of any other ogg bistream constraints (again, according
   // to the example encoder source)
   ogg_packet bitstream_header;
   ogg_packet comment_header;
   ogg_packet codebook_header;

   vorbis_analysis_headerout(&dsp, &comment, &bitstream_header, &comment_header,
         &codebook_header);

   // Place these headers into the stream
   ogg_stream_packetin(&stream, &bitstream_header);
   ogg_stream_packetin(&stream, &comment_header);
   ogg_stream_packetin(&stream, &codebook_header);

   // Flushing these headers now guarentees that audio data will
   // start on a new page, which apparently makes streaming easier
   ogg_stream_flush(&stream, &page);
   outFile.Write(page.header, page.header_len);
   outFile.Write(page.body, page.body_len);

   double t = t0;
   bool   done = false;
   wxProgressDialog *progress = NULL;

   wxYield();
   wxStartTimer();

   while(!done && !cancelling){
      float       deltat = (float)SAMPLES_PER_RUN / rate;
      sampleCount samplesThisRun = SAMPLES_PER_RUN;
      Mixer       *mixer = new Mixer(stereo ? 2 : 1, SAMPLES_PER_RUN, 
            /* interleaved = */ false, rate, floatSample);

      if(t + deltat > t1) {
         done = true;
         deltat = t1 - t;
         samplesThisRun = int(deltat * rate + 0.5);
      }
      
      mixer->Clear();

      TrackListIterator iter(tracks);
      Track *tr = iter.First();
      while (tr) {
         if (tr->GetKind() == Track::Wave) {
            if (tr->GetSelected() || !selectionOnly) {
               if (tr->GetChannel() == Track::MonoChannel)
                  mixer->MixMono((WaveTrack *) tr, t, t + deltat);
               else if (tr->GetChannel() == Track::LeftChannel)
                  mixer->MixLeft((WaveTrack *) tr, t, t + deltat);
               else if (tr->GetChannel() == Track::RightChannel)
                  mixer->MixRight((WaveTrack *) tr, t, t + deltat);
            }
         }
         tr = iter.Next();
      }
      
      float **vorbis_buffer = vorbis_analysis_buffer(&dsp, SAMPLES_PER_RUN);
      
      float *left = (float *)mixer->GetBuffer(0);
      memcpy(vorbis_buffer[0], left, sizeof(float)*SAMPLES_PER_RUN);

      if(stereo) {
         float *right = (float *)mixer->GetBuffer(1);
         memcpy(vorbis_buffer[1], right, sizeof(float)*SAMPLES_PER_RUN);
      }

      // tell the encoder how many samples we have
      vorbis_analysis_wrote(&dsp, samplesThisRun);

      // I don't understand what this call does, so here is the comment
      // from the example, verbatim:
      //
      //    vorbis does some data preanalysis, then divvies up blocks
      //    for more involved (potentially parallel) processing. Get
      //    a single block for encoding now
      while(vorbis_analysis_blockout(&dsp, &block) == 1) {

         // analysis, assume we want to use bitrate management
         vorbis_analysis(&block, NULL);
         vorbis_bitrate_addblock(&block);

         while(vorbis_bitrate_flushpacket(&dsp, &packet)) {

            // add the packet to the bitstream
            ogg_stream_packetin(&stream, &packet);
            int result = ogg_stream_pageout(&stream, &page);

            if(result != 0) {
               outFile.Write(page.header, page.header_len);
               outFile.Write(page.body, page.body_len);
            }
         }
      }

      if(progress)
         cancelling = !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5));
      else if(wxGetElapsedTime(false) > 500) {
            
         wxString message = selectionOnly ?
            _("Exporting the selected audio as Ogg Vorbis") :
            _("Exporting the entire project as Ogg Vorbis");

         progress = new wxProgressDialog(
               _("Export"),
               message,
               1000,
               parent,
               wxPD_CAN_ABORT | wxPD_REMAINING_TIME | wxPD_AUTO_HIDE);
      }

      delete mixer;
      t += deltat;
   }

   outFile.Close();

   if(progress)
      delete progress;

   return true;
}
Exemple #7
0
/*
 * This first function contains the code common to both
 * Export() and ExportLossy()
 *
 * For safety, if the file already exists it stores the filename
 * the user wants in actualName, and returns a temporary file name.
 * The calling function should rename the file when it's successfully
 * exported.
 */
wxString ExportCommon(AudacityProject *project,
                      wxString format, wxString defaultExtension,
                      bool selectionOnly, double *t0, double *t1,
                      bool *isStereo,
                      wxString &actualName)
{
   TrackList *tracks = project->GetTracks();

   /* First analyze the selected audio, perform sanity checks, and provide
    * information as appropriate. */

   /* Tally how many are right, left, mono, and make sure at
      least one track is selected (if selectionOnly==true) */

   int numSelected = 0, numLeft = 0, numRight = 0, numMono = 0;
   float earliestBegin = *t1;
   float latestEnd = *t0;

   TrackListIterator iter1(tracks);
   Track *tr = iter1.First();

   while (tr) {
      if (tr->GetKind() == Track::Wave) {
         if (tr->GetSelected() || !selectionOnly) {

            numSelected++;

            if (tr->GetChannel() == Track::LeftChannel)
               numLeft++;
            else if (tr->GetChannel() == Track::RightChannel)
               numRight++;
            else if (tr->GetChannel() == Track::MonoChannel)
            {
               // It's a mono channel, but it may be panned
               float pan = ((WaveTrack*)tr)->GetPan();
               
               if (pan == -1.0)
                  numLeft++;
               else if (pan == 1.0)
                  numRight++;
               else if (pan == 0)
                  numMono++;
               else {
                  numLeft++;
                  numRight++;
               }
            }
            
            if(tr->GetOffset() < earliestBegin)
               earliestBegin = tr->GetOffset();

            if(tr->GetEndTime() > latestEnd)
               latestEnd = tr->GetEndTime();

         }
      }

      tr = iter1.Next();
   }

   if(*t0 < earliestBegin)
      *t0 = earliestBegin;
   
   if(*t1 > latestEnd)
      *t1 = latestEnd;

   if (numSelected == 0 && selectionOnly) {
      wxMessageBox(_("No tracks are selected!\n"
                     "Choose Export... to export all tracks."));
      return "";
   }
   
   /* Detemine if exported file will be stereo or mono,
      and if mixing will occur */

   bool stereo = false;
   if (numRight > 0 || numLeft > 0)
      stereo = true;

   numRight += numMono;
   numLeft += numMono;
   
   if (numLeft > 1 || numRight > 1)
      if (stereo) {
         ShowWarningDialog(project, "MixStereo",
                           _("Your tracks will be mixed down to two "
                             "stereo channels in the exported file."));
      }
      else {
         ShowWarningDialog(project, "MixMono",
                           _("Your tracks will be mixed down to a "
                             "single mono channel in the exported file."));
      }

   /* Prepare and display the filename selection dialog */

   wxString path = gPrefs->Read("/DefaultExportPath",
                                FROMFILENAME(::wxGetCwd()));
   wxString nameOnly;
   wxString extension;
   wxString defaultName = project->GetName();
   wxString fName;
   wxString maskString;
   wxString endOfPathSep;

   if (defaultExtension.Left(1) == ".")
      defaultExtension =
         defaultExtension.Right(defaultExtension.Length()-1);

   maskString.Printf("%s files (*.%s)|*.%s|All files (*.*)|*.*", (const char *)format,
                     (const char *)defaultExtension, (const char *)defaultExtension);

   bool fileOkay;

   do {
      fileOkay = true;

      fName = defaultName + "." + defaultExtension;
      fName = wxFileSelector(wxString::Format(_("Save %s File As:"),
                                              (const char *) format),
                             path,
                             fName,       // default file name
                             defaultExtension,
                             maskString,
                             wxSAVE | wxOVERWRITE_PROMPT);
      
      if (fName.Length() >= 256) {
         wxMessageBox
            (_("Sorry, pathnames longer than 256 characters not supported."));
         return "";
      }
      
      if (fName == "")
         return "";

      ::wxSplitPath(fName, &path, &nameOnly, &extension);

      //
      // Make sure the user doesn't accidentally save the file
      // as an extension with no name, like just plain ".wav".
      //

      if ((nameOnly.Left(1)=="." && extension=="") ||
          (nameOnly=="" && extension!="")) {
         wxString prompt =
            _("Are you sure you want to save the file as \"")+
            ::wxFileNameFromPath(fName)+"\"?\n";
         
         int action = wxMessageBox(prompt,
                                   "Warning",
                                   wxYES_NO | wxICON_EXCLAMATION,
                                   project);
         
         fileOkay = (action == wxYES);
         continue;
      }

      //
      // Check the extension - add the default if it's not there,
      // and warn user if it's abnormal.
      //

      wxString defaultExtension3 = defaultExtension;
      if (defaultExtension.Length() > 3)
         defaultExtension = defaultExtension.Left(3);
      
      if (extension == "") {
         #ifdef __WXMSW__
         // Windows prefers 3-char uppercase extensions
         extension = defaultExtension;
         #else
         // Linux and Mac prefer lowercase extensions
         extension = defaultExtension.Lower();
         #endif
      }
      else if (extension.Upper() != defaultExtension.Upper() &&
               extension.Upper() != defaultExtension3.Upper()) {
         #ifdef __WXMSW__
         // Windows prefers 3-char extensions
         defaultExtension3 = defaultExtension3;
         #endif

         wxString prompt;
         prompt.Printf(_("You are about to save a %s file with the name %s.\n"
                       "Normally these files end in %s, and some programs "
                       "will not open files with nonstandard extensions.\n"
                       "Are you sure you want to save the file "
                       "under this name?"),
                       (const char *)format,
                       (const char *)("\""+nameOnly+"."+extension+"\""),
                       (const char *)("\"."+defaultExtension+"\""));

         int action = wxMessageBox(prompt,
                                   "Warning",
                                   wxYES_NO | wxICON_EXCLAMATION,
                                   project);

         if (action == wxYES)
            fileOkay = true;
         else {
            fileOkay = false;
            defaultName = nameOnly + "." + extension;
         }
      }

      if (path.Length() > 0 && path.Last() == wxFILE_SEP_PATH)
         endOfPathSep = "";
      else
         endOfPathSep = wxFILE_SEP_PATH;

      fName = path + endOfPathSep + 
         nameOnly + "." + extension;
   } while(!fileOkay);

   /*
    * Ensure that exporting a file by this name doesn't overwrite
    * one of the existing files in the project.  (If it would
    * overwrite an existing file, DirManager tries to rename the
    * existing file.)
    */

   if (!project->GetDirManager()->EnsureSafeFilename(wxFileName(fName)))
      return "";

   gPrefs->Write("/DefaultExportPath", path);

   *isStereo = stereo;

   /*
    * To be even MORE safe, return a temporary file name based
    * on this one...
    */

   actualName = fName;

   int suffix = 0;
   while(::wxFileExists(FILENAME(fName))) {
      fName = path + endOfPathSep + 
         nameOnly + wxString::Format("%d", suffix) + "." + extension;
      suffix++;
   }

   return fName;
}
Exemple #8
0
//TODO-MB: wouldn't it make more sense to DELETE the time track after 'mix and render'?
void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
                  double rate, sampleFormat format,
                  double startTime, double endTime,
                  WaveTrack::Holder &uLeft, WaveTrack::Holder &uRight)
{
   uLeft.reset(), uRight.reset();

   // This function was formerly known as "Quick Mix".
   Track *t;
   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) */

   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

   int numWaves = 0; /* number of wave tracks in the selection */
   int numMono = 0;  /* number of mono, centre-panned wave tracks in selection*/
   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.

   WaveTrackConstArray waveArray;
   t = iter.First();

   while (t) {
      if (t->GetSelected() && t->GetKind() == Track::Wave) {
         waveArray.push_back(static_cast<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)

   auto 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);
   decltype(mixLeft) mixRight{};
   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(waveArray,
      Mixer::WarpOptions(tracks->GetTimeTrack()),
      startTime, endTime, mono ? 1 : 2, maxBlockLen, false,
      rate, format);

   ::wxSafeYield();

   int updateResult = eProgressSuccess;
   {
      ProgressDialog progress(_("Mix and Render"),
         _("Mixing and rendering tracks"));

      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);
      }
   }

   mixLeft->Flush();
   if (!mono)
      mixRight->Flush();
   if (updateResult == eProgressCancelled || updateResult == eProgressFailed)
   {
      return;
   }
   else {
      uLeft = std::move(mixLeft),
         uRight = std::move(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
   }
}
bool ExportMultiple::ExportMultipleByLabel(bool byName, wxString prefix)
{
   Track *tr;
   int numFiles = mNumLabels;
   int l = 0;
   int numLeft = 0;
   int numRight = 0;
   int numMono = 0;

   // Account for exporting before first label
   if (mFirst->GetValue()) {
      l--;
      numFiles++;
   }

   // Figure out if we're exporting stereo or mono
   for (tr = mIterator.First(mTracks); tr != NULL; tr = mIterator.Next()) {

      // Only want wave tracks
      if (!tr->GetKind() == Track::Wave) {
         continue;
      }

      // Found a left channel
      if (tr->GetChannel() == Track::LeftChannel) {
         numLeft++;
      }

      // Found a right channel
      else if (tr->GetChannel() == Track::RightChannel) {
         numRight++;
      }

      // Found a mono channel, but it may be panned
      else if (tr->GetChannel() == Track::MonoChannel) {
         float pan = ((WaveTrack*)tr)->GetPan();

         // Figure out what kind of channel it should be
         if (pan == -1.0) {
            numLeft++;
         }
         else if (pan == 1.0) {
            numRight++;
         }
         else if (pan == 0) {
            numMono++;
         }
         else {
            numLeft++;
            numRight++;
         }
      }
   }

   // We we have a stereo or mono mix?
   bool stereo = false;
   if (numRight > 0 || numLeft > 0) {
      stereo = true;
   }

   wxArrayString otherNames;
   wxString name;
   double t0, t1;
   int count = 0;
   bool ok = true;

   // Examine all labels
   while (l < mNumLabels) {
      const LabelStruct *info = NULL;

      // Get file name and starting time
      if (l < 0) {
         name = mFirstFileName->GetValue();
         t0 = 0.0;
      }
      else {
         info = mLabels->GetLabel(l);
         name = info->title;
         t0 = info->t;
      }

      // Figure out the ending time
      if (info && info->t < info->t1) {
         t1 = info->t1;
      }
      else if (l < mNumLabels-1) {
         const LabelStruct *info1 = mLabels->GetLabel(l+1);
         t1 = info1->t;
      }
      else {
         t1 = mTracks->GetEndTime();
      }

      // Numbering files...
      if (!byName) {
         name.Printf(wxT("%s-%d"), prefix.c_str(), count+1);
      }

      // Make sure the file name is unique within the directory
      MakeNameUnique(otherNames, name);
      
      // Export it
      ok = DoExport(stereo, name, false, t0, t1, count+1);
      if (!ok) {
         break;
      }

      count++;
      l++;
   }
   
   // Give 'em the result
   ::wxMessageBox(wxString::Format(ok
                                   ? _("Successfully exported %d file(s).")
                                   : _("Something went wrong after exporting %d file(s)."),
                                   count),
                  _("Export Multiple"),
                  wxOK | wxCENTRE, this);

   return ok;
}
Exemple #10
0
bool ExportCL(AudacityProject *project, bool stereo, wxString fName,
              bool selectionOnly, double t0, double t1)
{
   int rate = int(project->GetRate() + 0.5);
   wxWindow *parent = project;
   TrackList *tracks = project->GetTracks();
   
   wxString command = gPrefs->Read("/FileFormats/ExternalProgramExportCommand", "lame - '%f'");
   command.Replace("%f", fName);

   /* establish parameters */
   int channels = stereo ? 2 : 1;
   unsigned long totalSamples = (unsigned long)((t1 - t0) * rate + 0.5);
   unsigned long sampleBytes = totalSamples * channels * SAMPLE_SIZE(int16Sample);
   double timeStep = 10.0;      // write in blocks of 10 secs

   /* fill up the wav header */
   wav_header header;
   header.riffID[0] = 'R';
   header.riffID[1] = 'I';
   header.riffID[2] = 'F';
   header.riffID[3] = 'F';
   header.riffType[0] = 'W';
   header.riffType[1] = 'A';
   header.riffType[2] = 'V';
   header.riffType[3] = 'E';
   header.lenAfterRiff = sampleBytes + 32;

   header.fmtID[0]  = 'f';
   header.fmtID[1]  = 'm';
   header.fmtID[2]  = 't';
   header.fmtID[3]  = ' ';
   header.formatChunkLen = 16;
   header.formatTag      = 1;
   header.channels       = channels;
   header.sampleRate     = rate;
   header.bitsPerSample  = SAMPLE_SIZE(int16Sample) * 8;
   header.blockAlign     = header.bitsPerSample * header.channels;
   header.avgBytesPerSec = header.sampleRate * header.blockAlign;

   header.dataID[0] = 'd';
   header.dataID[1] = 'a';
   header.dataID[2] = 't';
   header.dataID[3] = 'a';
   header.dataLen   = sampleBytes;

   FILE *pipe = popen(command.c_str(), "w");

   /* write the header */

   fwrite( &header, sizeof(wav_header), 1, pipe );

   //sampleCount maxSamples = int (timeStep * rate + 0.5);

   wxProgressDialog *progress = NULL;
   wxYield();
   wxStartTimer();
   wxBusyCursor busy;
   bool cancelling = false;

   double t = t0;

   while (t < t1 && !cancelling) {

      double deltat = timeStep;
      if (t + deltat > t1)
         deltat = t1 - t;

      sampleCount numSamples = int (deltat * rate + 0.5);

      Mixer *mixer = new Mixer(channels, numSamples, true, rate, int16Sample);
      wxASSERT(mixer);
      mixer->Clear();

      char *buffer = new char[numSamples * SAMPLE_SIZE(int16Sample) * channels];
      wxASSERT(buffer);

      TrackListIterator iter(tracks);
      Track *tr = iter.First();
      while (tr) {
         if (tr->GetKind() == Track::Wave) {
            if (tr->GetSelected() || !selectionOnly) {
               if (tr->GetChannel() == Track::MonoChannel)
                  mixer->MixMono((WaveTrack *) tr, t, t + deltat);
               if (tr->GetChannel() == Track::LeftChannel)
                  mixer->MixLeft((WaveTrack *) tr, t, t + deltat);
               if (tr->GetChannel() == Track::RightChannel)
                  mixer->MixRight((WaveTrack *) tr, t, t + deltat);
            }
         }
         tr = iter.Next();
      }

      samplePtr mixed = mixer->GetBuffer();

      // Byte-swapping is neccesary on big-endian machines, since
      // WAV files are little-endian
#if wxBYTE_ORDER == wxBIG_ENDIAN
      {
         short *buffer = (short*)mixed;
         for( int i = 0; i < numSamples; i++ )
            buffer[i] = wxINT16_SWAP_ON_BE(buffer[i]);
      }
#endif

      fwrite( mixed, numSamples * channels * SAMPLE_SIZE(int16Sample), 1, pipe );

      t += deltat;

      if (!progress && wxGetElapsedTime(false) > 500) {

         wxString message;

         if (selectionOnly)
            message = "Exporting the selected audio using command-line encoder";
         else
            message = "Exporting the entire project using command-line encoder";

         progress =
             new wxProgressDialog("Export",
                                  message,
                                  1000,
                                  parent,
                                  wxPD_CAN_ABORT |
                                  wxPD_REMAINING_TIME | wxPD_AUTO_HIDE);
      }
      if (progress) {
         cancelling =
             !progress->Update(int (((t - t0) * 1000) / (t1 - t0) + 0.5));
      }

      delete mixer;
      delete[]buffer;
   }

   pclose( pipe );

   if(progress)
      delete progress;
   return true;
}
Exemple #11
0
ExportMixerDialog::ExportMixerDialog( TrackList *tracks, bool selectedOnly,
      int maxNumChannels, wxWindow *parent, wxWindowID id, const wxString &title, 
      const wxPoint &position, const wxSize& size, long style ) :
   wxDialog( parent, id, title, position, size, style | wxRESIZE_BORDER )
{
   int numTracks = 0;
   TrackListIterator iter( tracks );
   
   for( Track *t = iter.First(); t; t = iter.Next() )
   {
      if( t->GetKind() == Track::Wave && ( t->GetSelected() || !selectedOnly ) )
      {
         numTracks++;
         if( t->GetChannel() == Track::LeftChannel )
         {
            mTrackNames.Add( t->GetName() + _( " - Left" ) );
            mTrackNames.Add( t->GetName() + _( " - Right" ) );
            t = iter.Next();
            numTracks++;
         }
         else
            mTrackNames.Add( t->GetName() );
      }
   }

   // JKC: This is an attempt to fix a 'watching brief' issue, where the slider is
   // sometimes not slidable.  My suspicion is that a mixer may incorrectly
   // state the number of channels - so we assume there are always at least two.
   // The downside is that if someone is exporting to a mono device, the dialog
   // will allow them to output to two channels. Hmm.  We may need to revisit this.
   if (maxNumChannels < 2 )
      maxNumChannels = 2;
   if (maxNumChannels > 32)
      maxNumChannels = 32;

   mMixerSpec = new MixerSpec( numTracks, maxNumChannels );

   wxBoxSizer *vertSizer = new wxBoxSizer( wxVERTICAL );

   wxWindow *mixerPanel = new ExportMixerPanel( mMixerSpec, mTrackNames, this, 
         ID_MIXERPANEL, wxDefaultPosition, wxSize( 400, -1 ) );
   vertSizer->Add( mixerPanel, 1, wxEXPAND | wxALIGN_CENTRE | wxALL, 5 );

   wxBoxSizer *horSizer = new wxBoxSizer( wxHORIZONTAL );
   
   mChannelsText = new wxStaticText( this, -1, 
         wxString::Format( _( "Output Channels: %2d" ), 
            mMixerSpec->GetNumChannels() ) );
   horSizer->Add( mChannelsText, 0, wxALIGN_LEFT | wxALL, 5 );

   wxSlider *channels = new wxSlider( this, ID_SLIDER_CHANNEL, 
         mMixerSpec->GetNumChannels(), 1, mMixerSpec->GetMaxNumChannels(),
         wxDefaultPosition, wxSize( 300, -1 ) );
   horSizer->Add( channels, 0, wxEXPAND | wxALL, 5 );
   
   vertSizer->Add( horSizer, 0, wxALIGN_CENTRE | wxALL, 5 );

   vertSizer->Add( CreateStdButtonSizer(this, eCancelButton|eOkButton), 0, wxEXPAND );

   SetAutoLayout( true );
   SetSizer( vertSizer );
   vertSizer->Fit( this );
   vertSizer->SetSizeHints( this );

   SetSizeHints( 640, 480, 20000, 20000 );

   SetSize( 640, 480 );
}
Exemple #12
0
bool Exporter::ExamineTracks()
{
   // Init
   mNumSelected = 0;
   mNumLeft = 0;
   mNumRight = 0;
   mNumMono = 0;

   // First analyze the selected audio, perform sanity checks, and provide
   // information as appropriate.

   // Tally how many are right, left, mono, and make sure at
   // least one track is selected (if selectedOnly==true)

   float earliestBegin = mT1;
   float latestEnd = mT0;

   TrackList *tracks = mProject->GetTracks();
   TrackListIterator iter1(tracks);
   Track *tr = iter1.First();

   while (tr) {
      if (tr->GetKind() == Track::Wave) {
         if (tr->GetSelected() || !mSelectedOnly) {

            mNumSelected++;

            if (tr->GetChannel() == Track::LeftChannel) {
               mNumLeft++;
            }
            else if (tr->GetChannel() == Track::RightChannel) {
               mNumRight++;
            }
            else if (tr->GetChannel() == Track::MonoChannel) {
               // It's a mono channel, but it may be panned
               float pan = ((WaveTrack*)tr)->GetPan();
               
               if (pan == -1.0)
                  mNumLeft++;
               else if (pan == 1.0)
                  mNumRight++;
               else if (pan == 0)
                  mNumMono++;
               else {
                  // Panned partially off-center. Mix as stereo.
                  mNumLeft++;
                  mNumRight++;
               }
            }

            if (tr->GetOffset() < earliestBegin) {
               earliestBegin = tr->GetOffset();
            }

            if (tr->GetEndTime() > latestEnd) {
               latestEnd = tr->GetEndTime();
            }

         }
      }

      tr = iter1.Next();
   }

   if (mSelectedOnly && mNumSelected == 0) {
      wxMessageBox(_("No tracks are selected! Use Ctrl-A (Select All)\nChoose Export... to export all tracks."),
                    _("Unable to export"),
                    wxOK | wxICON_INFORMATION);
      return false;
   }

   if (mT0 < earliestBegin)
      mT0 = earliestBegin;

   if (mT1 > latestEnd)
      mT1 = latestEnd;

   return true;
}