Пример #1
0
void GameHandler::VerifyGameDB(GameHandler *handler)
{
    int counter = 0;
    GameScanMap::Iterator iter;

    MSqlQuery query(MSqlQuery::InitCon());
    query.prepare("SELECT romname,rompath,gamename FROM gamemetadata "
                  "WHERE system = :SYSTEM");

    query.bindValue(":SYSTEM",handler->SystemName());

    if (!query.exec())
        MythDB::DBError("GameHandler::VerifyGameDB - "
                        "select", query);

    QString message = QObject::tr("Verifying %1 files")
                                    .arg(handler->SystemName());

    CreateProgress(message);

    if (m_progressDlg)
        m_progressDlg->SetTotal(query.size());

    // For every file we know about, check to see if it still exists.
    if (query.isActive() && query.size() > 0)
    {
        while (query.next())
        {
            QString RomName = query.value(0).toString();
            QString RomPath = query.value(1).toString();
            QString GameName = query.value(2).toString();
            if (RomName != QString::null)
            {
                if ((iter = m_GameMap.find(RomName)) != m_GameMap.end())
                {
                    // If it's both on disk and in the database we're done with it.
                    m_GameMap.erase(iter);
                }
                else
                {
                    // If it's only in the database add it to our list and mark it for
                    // removal.
                    m_GameMap[RomName] = GameScan(RomName,RomPath + "/" + RomName,inDatabase,
                                         GameName,RomPath);
                }
            }
            if (m_progressDlg)
                m_progressDlg->SetProgress(++counter);
        }
    }

    if (m_progressDlg)
    {
        m_progressDlg->Close();
        m_progressDlg = NULL;
}
}
Пример #2
0
int MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks,
                                Tags *tags)
{
   outTracks.clear();

   CreateProgress();

   /* Prepare decoder data, initialize decoder */

   mPrivateData.file        = mFile;
   mPrivateData.inputBuffer = new unsigned char [INPUT_BUFFER_SIZE];
   mPrivateData.progress    = mProgress.get();
   mPrivateData.updateResult= eProgressSuccess;
   mPrivateData.id3checked  = false;
   mPrivateData.numChannels = 0;
   mPrivateData.trackFactory= trackFactory;

   mad_decoder_init(&mDecoder, &mPrivateData, input_cb, 0, 0, output_cb, error_cb, 0);

   /* and send the decoder on its way! */

   bool res = (mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC) == 0) &&
              (mPrivateData.numChannels > 0) &&
              !(mPrivateData.updateResult == eProgressCancelled) &&
              !(mPrivateData.updateResult == eProgressFailed);

   mad_decoder_finish(&mDecoder);

   delete[] mPrivateData.inputBuffer;

   if (!res) {
      /* failure */
      /* printf("failure\n"); */
      return (mPrivateData.updateResult);
   }

   /* success */
   /* printf("success\n"); */

      /* copy the WaveTrack pointers into the Track pointer list that
       * we are expected to fill */
   for(const auto &channel : mPrivateData.channels) {
      channel->Flush();
   }
   outTracks.swap(mPrivateData.channels);

   /* Read in any metadata */
   ImportID3(tags);

   return mPrivateData.updateResult;
}
Пример #3
0
void GameHandler::processGames(GameHandler *handler)
{
    QString thequery;
    int maxcount = 0;
    MSqlQuery query(MSqlQuery::InitCon());

    if ((!handler->SystemRomPath().isEmpty()) && (handler->GameType() != "PC"))
    {
        QDir d(handler->SystemRomPath());
        if (d.exists())
            maxcount = buildFileCount(handler->SystemRomPath(),handler);
        else
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                QString("ROM Path does not exist: %1")
                    .arg(handler->SystemRomPath()));
            return;
        }
    }
    else
        maxcount = 100;

    if (handler->GameType() == "PC")
    {
        MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");

        QString message = QObject::tr("Scanning for %1 games...")
                                                .arg(handler->SystemName());
        MythUIBusyDialog *busyDialog = new MythUIBusyDialog(message, popupStack,
                                                "gamescanbusy");

        if (busyDialog->Create())
            popupStack->AddScreen(busyDialog, false);
        else
        {
            delete busyDialog;
            busyDialog = NULL;
        }

        m_GameMap[handler->SystemCmdLine()] =
                GameScan(handler->SystemCmdLine(),
                    handler->SystemCmdLine(),
                    inFileSystem,
                    handler->SystemName(),
                    handler->SystemCmdLine().left(handler->SystemCmdLine().lastIndexOf(QRegExp("/"))));

        if (busyDialog)
            busyDialog->Close();

        LOG(VB_GENERAL, LOG_INFO, LOC +
            QString("PC Game %1").arg(handler->SystemName()));
    }
    else
    {
        QString message = QObject::tr("Scanning for %1 games...")
                                                .arg(handler->SystemName());
        CreateProgress(message);

        if (m_progressDlg)
            m_progressDlg->SetTotal(maxcount);

        int filecount = 0;
        buildFileList(handler->SystemRomPath(), handler, &filecount);

        if (m_progressDlg)
        {
            m_progressDlg->Close();
            m_progressDlg = NULL;
        }
    }

    VerifyGameDB(handler);

    // If we still have some games in the list then update the database
    if (!m_GameMap.empty())
    {
        InitMetaDataMap(handler->GameType());

        UpdateGameDB(handler);

        romDB.clear();
        handler->setRebuild(true);
    }
    else
        handler->setRebuild(false);
}
Пример #4
0
void GameHandler::UpdateGameDB(GameHandler *handler)
{
    int counter = 0;
    MSqlQuery query(MSqlQuery::InitCon());

    QString message = QObject::tr("Updating %1(%2) ROM database")
                                    .arg(handler->SystemName())
                                    .arg(handler->GameType());

    CreateProgress(message);

    if (m_progressDlg)
        m_progressDlg->SetTotal(m_GameMap.size());

    GameScanMap::Iterator iter;

    QString GameName, Genre, Country, CRC32, Year, Plot;
    QString Publisher, Version, Fanart, Boxart, ScreenShot;
    QString thequery, queryvalues;

    int removalprompt = gCoreContext->GetSetting("GameRemovalPrompt").toInt();
    int indepth = gCoreContext->GetSetting("GameDeepScan").toInt();
    QString screenShotPath = gCoreContext->GetSetting("mythgame.screenshotdir");

    for (iter = m_GameMap.begin(); iter != m_GameMap.end(); ++iter)
    {

        if (iter.value().FoundLoc() == inFileSystem)
        {
            if (indepth)
            {
                GetMetadata(handler, iter.value().RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
                            &Plot, &Publisher, &Version, &Fanart, &Boxart);
            }
            else
            {
                Genre = QObject::tr("Unknown") + handler->GameType();
                Country = QObject::tr("Unknown");
                CRC32.clear();
                Year = QObject::tr("19xx");
                GameName = QObject::tr("Unknown");
                Plot = QObject::tr("Unknown");
                Publisher = QObject::tr("Unknown");
                Version = QObject::tr("0");
                Fanart.clear();
                Boxart.clear();
            }

            if (GameName == QObject::tr("Unknown"))
                GameName = iter.value().GameName();

            int suffixPos = iter.value().Rom().lastIndexOf(QChar('.'));
            QString baseName = iter.value().Rom();

            if (suffixPos > 0)
                baseName = iter.value().Rom().left(suffixPos);

            baseName = screenShotPath + "/" + baseName;

            if (QFile(baseName + ".png").exists())
                ScreenShot = baseName + ".png";
            else if (QFile(baseName + ".jpg").exists())
                ScreenShot = baseName + ".jpg";
            else if (QFile(baseName + ".gif").exists())
                ScreenShot = baseName + ".gif";
            else
                ScreenShot.clear();

#if 0
            LOG(VB_GENERAL, LOG_INFO, QString("file %1 - genre %2 ")
                    .arg(iter.data().Rom()).arg(Genre));
            LOG(VB_GENERAL, LOG_INFO, QString("screenshot %1").arg(ScreenShot));
#endif

            query.prepare("INSERT INTO gamemetadata "
                          "(system, romname, gamename, genre, year, gametype, "
                          "rompath, country, crc_value, diskcount, display, plot, "
                          "publisher, version, fanart, boxart, screenshot) "
                          "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
                          ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
                          ":FANART, :BOXART, :SCREENSHOT)");

            query.bindValue(":SYSTEM",handler->SystemName());
            query.bindValue(":ROMNAME",iter.value().Rom());
            query.bindValue(":GAMENAME",GameName);
            query.bindValue(":GENRE",Genre);
            query.bindValue(":YEAR",Year);
            query.bindValue(":GAMETYPE",handler->GameType());
            query.bindValue(":ROMPATH",iter.value().RomPath());
            query.bindValue(":COUNTRY",Country);
            query.bindValue(":CRC32", CRC32);
            query.bindValue(":PLOT", Plot);
            query.bindValue(":PUBLISHER", Publisher);
            query.bindValue(":VERSION", Version);
            query.bindValue(":FANART", Fanart);
            query.bindValue(":BOXART", Boxart);
            query.bindValue(":SCREENSHOT", ScreenShot);

            if (!query.exec())
                MythDB::DBError("GameHandler::UpdateGameDB - "
                                "insert gamemetadata", query);
        }
        else if ((iter.value().FoundLoc() == inDatabase) && (removalprompt))
        {

            promptForRemoval( iter.value() );
        }

        if (m_progressDlg)
            m_progressDlg->SetProgress(++counter);
    }

    if (m_progressDlg)
    {
        m_progressDlg->Close();
        m_progressDlg = NULL;
}
}
Пример #5
0
int FLACImportFileHandle::Import(TrackFactory *trackFactory,
                                 Track ***outTracks,
                                 int *outNumTracks,
                                 Tags *tags)
{
   wxASSERT(mStreamInfoDone);

   CreateProgress();

   mChannels = new WaveTrack *[mNumChannels];

   unsigned long c;
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c] = trackFactory->NewWaveTrack(mFormat, mSampleRate);

      if (mNumChannels == 2) {
         switch (c) {
         case 0:
            mChannels[c]->SetChannel(Track::LeftChannel);
            mChannels[c]->SetLinked(true);
            break;
         case 1:
            mChannels[c]->SetChannel(Track::RightChannel);
            break;
         }
      }
      else {
         mChannels[c]->SetChannel(Track::MonoChannel);
      }
   }


//Start OD
   bool useOD = false;
#ifdef EXPERIMENTAL_OD_FLAC
   useOD=true;
#endif

   // TODO: Vigilant Sentry: Variable res unused after assignment (error code DA1)
   //    Should check the result.
   #ifdef LEGACY_FLAC
      bool res = (mFile->process_until_end_of_file() != 0);
   #else
      bool res = true;
      if(!useOD)
         res = (mFile->process_until_end_of_stream() != 0);
   #endif

   //add the task to the ODManager
   if(useOD)
   {
      sampleCount fileTotalFrames = mNumSamples;
      sampleCount maxBlockSize = mChannels[0]->GetMaxBlockSize();
      for (sampleCount i = 0; i < fileTotalFrames; i += maxBlockSize) {
         sampleCount blockLen = maxBlockSize;
         if (i + blockLen > fileTotalFrames)
            blockLen = fileTotalFrames - i;

         for (c = 0; c < mNumChannels; c++)
            mChannels[c]->AppendCoded(mFilename, i, blockLen, c,ODTask::eODFLAC);

         mUpdateResult = mProgress->Update(i, fileTotalFrames);
         if (mUpdateResult != eProgressSuccess)
            break;
      }

      bool moreThanStereo = mNumChannels>2;
      for (c = 0; c < mNumChannels; c++)
      {
         mDecoderTask->AddWaveTrack(mChannels[c]);
         if(moreThanStereo)
         {
            //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
            ODManager::Instance()->AddNewTask(mDecoderTask);
            mDecoderTask=new ODDecodeFlacTask; //TODO: see if we need to use clone to keep the metadata.
         }
      }
      //if we have mono or a linked track (stereo), we add ONE task for the one linked wave track
      if(!moreThanStereo)
         ODManager::Instance()->AddNewTask(mDecoderTask);
   }
//END OD


   if (mUpdateResult == eProgressFailed || mUpdateResult == eProgressCancelled) {
      for(c = 0; c < mNumChannels; c++) {
         delete mChannels[c];
      }
      delete[] mChannels;

      return mUpdateResult;
   }

   *outNumTracks = mNumChannels;
   *outTracks = new Track *[mNumChannels];
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c]->Flush();
      (*outTracks)[c] = mChannels[c];
   }
   delete[] mChannels;

   tags->Clear();
   size_t cnt = mFile->mComments.GetCount();
   for (c = 0; c < cnt; c++) {
      wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
      wxString value = mFile->mComments[c].AfterFirst(wxT('='));
      if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
         long val;
         if (value.Length() == 4 && value.ToLong(&val)) {
            name = TAG_YEAR;
         }
      }
      tags->SetTag(name, value);
   }

   return mUpdateResult;
}
Пример #6
0
int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
              Track ***outTracks,
              int *outNumTracks,
              Tags *tags)
{

   CreateProgress();

   // Remove stream contexts which are not marked for importing and adjust mScs and mNumStreams accordingly
   for (int i = 0; i < mNumStreams;)
   {
      if (!mScs[i]->m_use)
      {
         delete mScs[i];
         for (int j = i; j < mNumStreams - 1; j++)
         {
            mScs[j] = mScs[j+1];
         }
         mNumStreams--;
      }
      else i++;
   }

   mChannels = new WaveTrack **[mNumStreams];
   mNumSamples = 0;

   for (int s = 0; s < mNumStreams; s++)
   {
      // As you can see, it's really a number of frames.
      // TODO: use something other than nb_frames for progress reporting (nb_frames is not available for some formats). Maybe something from the format context?
      mNumSamples += mScs[s]->m_stream->nb_frames;
      // There is a possibility that number of channels will change over time, but we do not have WaveTracks for new channels. Remember the number of channels and stick to it.
      mScs[s]->m_initialchannels = mScs[s]->m_stream->codec->channels;
      mChannels[s] = new WaveTrack *[mScs[s]->m_stream->codec->channels];
      int c;
      for (c = 0; c < mScs[s]->m_stream->codec->channels; c++)
      {
         mChannels[s][c] = trackFactory->NewWaveTrack(int16Sample, mScs[s]->m_stream->codec->sample_rate);

         if (mScs[s]->m_stream->codec->channels == 2)
         {
            switch (c)
            {
            case 0:
               mChannels[s][c]->SetChannel(Track::LeftChannel);
               mChannels[s][c]->SetLinked(true);
               break;
            case 1:
               mChannels[s][c]->SetChannel(Track::RightChannel);
               mChannels[s][c]->SetTeamed(true);
               break;
            }
         }
         else
         {
            mChannels[s][c]->SetChannel(Track::MonoChannel);
         }
      }
   }

   // Handles the start_time by creating silence. This may or may not be correct.
   // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
   /// TODO: Nag FFmpeg devs about start_time until they finally say WHAT is this and HOW to handle it.
   for (int s = 0; s < mNumStreams; s++)
   {
      int64_t stream_delay = 0;
      if (mScs[s]->m_stream->start_time != int64_t(AV_NOPTS_VALUE))
      {
         stream_delay = mScs[s]->m_stream->start_time;
         wxLogMessage(wxT("Stream %d start_time = %d, that would be %f milliseconds."), s, mScs[s]->m_stream->start_time, double(mScs[s]->m_stream->start_time)/AV_TIME_BASE*1000);
      }
      if (stream_delay != 0)
      {
         for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++)
         {
            WaveTrack *t = mChannels[s][c];
            t->InsertSilence(0,double(stream_delay)/AV_TIME_BASE);
         }
      }
   }

   // This is the heart of the importing process
   streamContext *sc = NULL;
   // The result of Import() to be returend. It will be something other than zero if user canceled or some error appears.
   int res = 0;
   // Read next frame.
   while ((sc = ReadNextFrame()) != NULL && (res == 0))
   {
      // ReadNextFrame returns 1 if stream is not to be imported
      if (sc != (streamContext*)1)
      {
         // Decode frame until it is not possible to decode any further
         while (sc->m_pktRemainingSiz > 0 && (res == 0))
         {
            if (DecodeFrame(sc,false) < 0)
               break;

            // If something useable was decoded - write it to mChannels
            if (sc->m_frameValid)
               res = WriteData(sc);
         }

         // Cleanup after frame decoding
         if (sc->m_pktValid)
         {
            av_free_packet(&sc->m_pkt);
            sc->m_pktValid = 0;
         }    
      }
   }

   // Flush the decoders.
   if ((mNumStreams != 0) && (res == 0))
   {
      for (int i = 0; i < mNumStreams; i++)
      {
         if (DecodeFrame(mScs[i], true) == 0)
         {
            WriteData(mScs[i]);

            if (mScs[i]->m_pktValid)
            {
               av_free_packet(&mScs[i]->m_pkt);
               mScs[i]->m_pktValid = 0;
            }				
         }
      }
   }

   // Something bad happened - destroy everything!
   if (res)
   {
      for (int s = 0; s < mNumStreams; s++)
      {
         delete[] mChannels[s];
      }
      delete[] mChannels;

      if (mCancelled)
         return eImportCancelled;
      else
         return eImportFailed;
   }

   *outNumTracks = 0;
   for (int s = 0; s < mNumStreams; s++)
   {
      *outNumTracks += mScs[s]->m_stream->codec->channels;
   }

   // Create new tracks
   *outTracks = new Track *[*outNumTracks];

   // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
   int trackindex = 0;
   for (int s = 0; s < mNumStreams; s++)
   {
      for(int c = 0; c < mScs[s]->m_stream->codec->channels; c++)
      {
         mChannels[s][c]->Flush();
         (*outTracks)[trackindex++] = mChannels[s][c];
      }
      delete[] mChannels[s];
   }
   delete[] mChannels;

   // Save metadata
   WriteMetadata(mFormatContext,tags);

   return eImportSuccess;
}
Пример #7
0
ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
                               TrackHolders &outTracks,
                               Tags *tags)
{
   outTracks.clear();

   OSErr err = noErr;
   MovieAudioExtractionRef maer = NULL;
   auto updateResult = ProgressResult::Success;
   auto totSamples =
      (sampleCount) GetMovieDuration(mMovie); // convert from TimeValue
   decltype(totSamples) numSamples = 0;
   Boolean discrete = true;
   UInt32 quality = kQTAudioRenderQuality_Max;
   AudioStreamBasicDescription desc;
   UInt32 maxSampleSize;
   bool res = false;

   auto cleanup = finally( [&] {
      if (maer) {
         MovieAudioExtractionEnd(maer);
      }
   } );

   CreateProgress();

   do
   {
      err = MovieAudioExtractionBegin(mMovie, 0, &maer);
      if (err != noErr) {
         AudacityMessageBox(_("Unable to start QuickTime extraction"));
         break;
      }
   
      err = MovieAudioExtractionSetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTMovieAudioExtractionAudioPropertyID_RenderQuality,
                                            sizeof(quality),
                                            &quality);
      if (err != noErr) {
         AudacityMessageBox(_("Unable to set QuickTime render quality"));
         break;
      }
   
      err = MovieAudioExtractionSetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Movie,
                                            kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
                                            sizeof(discrete),
                                            &discrete);
      if (err != noErr) {
         AudacityMessageBox(_("Unable to set QuickTime discrete channels property"));
         break;
      }
   
      err = MovieAudioExtractionGetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTAudioPropertyID_MaxAudioSampleSize,
                                            sizeof(maxSampleSize),
                                            &maxSampleSize,
                                            NULL);
      if (err != noErr) {
         AudacityMessageBox(_("Unable to get QuickTime sample size property"));
         break;
      }
   
      err = MovieAudioExtractionGetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                            sizeof(desc),
                                            &desc,
                                            NULL);
      if (err != noErr) {
         AudacityMessageBox(_("Unable to retrieve stream description"));
         break;
      }
   
      auto numchan = desc.mChannelsPerFrame;
      const size_t bufsize = 5 * desc.mSampleRate;
   
      // determine sample format
      sampleFormat format;
      switch (maxSampleSize)
      {
         case 16:
            format = int16Sample;
            break;
   
         case 24:
            format = int24Sample;
            break;
   
         default:
            format = floatSample;
            break;
      }

      // Allocate an array of pointers, whose size is not known statically,
      // and prefixed with the AudioBufferList structure.
      MallocPtr< AudioBufferList > abl{
         static_cast< AudioBufferList * >(
            calloc( 1, offsetof( AudioBufferList, mBuffers ) +
               (sizeof(AudioBuffer) * numchan))) };
      abl->mNumberBuffers = numchan;
   
      TrackHolders channels{ numchan };

      const auto size = sizeof(float) * bufsize;
      ArraysOf<unsigned char> holders{ numchan, size };
      for (size_t c = 0; c < numchan; c++) {
         auto &buffer = abl->mBuffers[c];
         auto &holder = holders[c];
         auto &channel = channels[c];

         buffer.mNumberChannels = 1;
         buffer.mDataByteSize = size;

         buffer.mData = holder.get();
   
         channel = trackFactory->NewWaveTrack( format );
         channel->SetRate( desc.mSampleRate );
   
         if (numchan == 2) {
            if (c == 0) {
               channel->SetChannel(Track::LeftChannel);
               channel->SetLinked(true);
            }
            else if (c == 1) {
               channel->SetChannel(Track::RightChannel);
            }
         }
      }
   
      do {
         UInt32 flags = 0;
         UInt32 numFrames = bufsize;
   
         err = MovieAudioExtractionFillBuffer(maer,
                                              &numFrames,
                                              abl.get(),
                                              &flags);
         if (err != noErr) {
            AudacityMessageBox(_("Unable to get fill buffer"));
            break;
         }
   
         for (size_t c = 0; c < numchan; c++) {
            channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames);
         }
   
         numSamples += numFrames;
   
         updateResult = mProgress->Update(
            numSamples.as_long_long(),
            totSamples.as_long_long() );
   
         if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) {
            break;
         }
      } while (updateResult == ProgressResult::Success);
   
      res = (updateResult == ProgressResult::Success && err == noErr);
   
      if (res) {
         for (const auto &channel: channels) {
            channel->Flush();
         }
   
         outTracks.swap(channels);
      }

      //
      // Extract any metadata
      //
      if (res) {
         AddMetadata(tags);
      }
   } while (false);

// done:

   return (res ? ProgressResult::Success : ProgressResult::Failed);
}
Пример #8
0
int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
              Track ***outTracks,
              int *outNumTracks,
              Tags *tags)
{

   CreateProgress();

   // Remove stream contexts which are not marked for importing and adjust mScs and mNumStreams accordingly
   for (int i = 0; i < mNumStreams;)
   {
      if (!mScs[i]->m_use)
      {
         delete mScs[i];
         for (int j = i; j < mNumStreams - 1; j++)
         {
            mScs[j] = mScs[j+1];
         }
         mNumStreams--;
      }
      else i++;
   }

   mChannels = new WaveTrack **[mNumStreams];

   for (int s = 0; s < mNumStreams; s++)
   {
      switch (mScs[s]->m_stream->codec->sample_fmt)
      {
         case SAMPLE_FMT_U8:
         case SAMPLE_FMT_S16:
            mScs[s]->m_osamplesize = sizeof(int16_t);
            mScs[s]->m_osamplefmt = int16Sample;
         break;
         default:
            mScs[s]->m_osamplesize = sizeof(float);
            mScs[s]->m_osamplefmt = floatSample;
         break;
      }

      // There is a possibility that number of channels will change over time, but we do not have WaveTracks for new channels. Remember the number of channels and stick to it.
      mScs[s]->m_initialchannels = mScs[s]->m_stream->codec->channels;
      mChannels[s] = new WaveTrack *[mScs[s]->m_stream->codec->channels];
      int c;
      for (c = 0; c < mScs[s]->m_stream->codec->channels; c++)
      {
         mChannels[s][c] = trackFactory->NewWaveTrack(mScs[s]->m_osamplefmt, mScs[s]->m_stream->codec->sample_rate);

         if (mScs[s]->m_stream->codec->channels == 2)
         {
            switch (c)
            {
            case 0:
               mChannels[s][c]->SetChannel(Track::LeftChannel);
               mChannels[s][c]->SetLinked(true);
               break;
            case 1:
               mChannels[s][c]->SetChannel(Track::RightChannel);
               break;
            }
         }
         else
         {
            mChannels[s][c]->SetChannel(Track::MonoChannel);
         }
      }
   }

   // Handles the start_time by creating silence. This may or may not be correct.
   // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
   /// TODO: Nag FFmpeg devs about start_time until they finally say WHAT is this and HOW to handle it.
   for (int s = 0; s < mNumStreams; s++)
   {
      int64_t stream_delay = 0;
      if (mScs[s]->m_stream->start_time != int64_t(AV_NOPTS_VALUE) && mScs[s]->m_stream->start_time > 0)
      {
         stream_delay = mScs[s]->m_stream->start_time;
         wxLogDebug(wxT("Stream %d start_time = %d, that would be %f milliseconds."), s, mScs[s]->m_stream->start_time, double(mScs[s]->m_stream->start_time)/AV_TIME_BASE*1000);
      }
      if (stream_delay != 0)
      {
         for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++)
         {
            WaveTrack *t = mChannels[s][c];
            t->InsertSilence(0,double(stream_delay)/AV_TIME_BASE);
         }
      }
   }
   // This is the heart of the importing process
   // The result of Import() to be returend. It will be something other than zero if user canceled or some error appears.
   int res = eProgressSuccess;

#ifdef EXPERIMENTAL_OD_FFMPEG
   mUsingOD = false;
   gPrefs->Read(wxT("/Library/FFmpegOnDemand"), &mUsingOD);
   //at this point we know the file is good and that we have to load the number of channels in mScs[s]->m_stream->codec->channels;
   //so for OD loading we create the tracks and releasee the modal lock after starting the ODTask.
   if (mUsingOD) {
      std::vector<ODDecodeFFmpegTask*> tasks;
      //append blockfiles to each stream and add an individual ODDecodeTask for each one.
      for (int s = 0; s < mNumStreams; s++) {
         ODDecodeFFmpegTask* odTask=new ODDecodeFFmpegTask(mScs,mNumStreams,mChannels,mFormatContext, s);
         odTask->CreateFileDecoder(mFilename);

         //each stream has different duration.  We need to know it if seeking is to be allowed.
         sampleCount sampleDuration = 0;
         if (mScs[s]->m_stream->duration > 0)
            sampleDuration = ((sampleCount)mScs[s]->m_stream->duration * mScs[s]->m_stream->time_base.num) *mScs[s]->m_stream->codec->sample_rate / mScs[s]->m_stream->time_base.den;
         else
            sampleDuration = ((sampleCount)mFormatContext->duration *mScs[s]->m_stream->codec->sample_rate) / AV_TIME_BASE;

         //      printf(" OD duration samples %qi, sr %d, secs %d\n",sampleDuration, (int)mScs[s]->m_stream->codec->sample_rate,(int)sampleDuration/mScs[s]->m_stream->codec->sample_rate);
         
         //for each wavetrack within the stream add coded blockfiles
         for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++) {
            WaveTrack *t = mChannels[s][c];
            odTask->AddWaveTrack(t);
         
            sampleCount maxBlockSize = t->GetMaxBlockSize();
            //use the maximum blockfile size to divide the sections (about 11secs per blockfile at 44.1khz)
            for (sampleCount i = 0; i < sampleDuration; i += maxBlockSize) {
               sampleCount blockLen = maxBlockSize;
               if (i + blockLen > sampleDuration)
                  blockLen = sampleDuration - i;
            
               t->AppendCoded(mFilename, i, blockLen, c,ODTask::eODFFMPEG);
            
               // This only works well for single streams since we assume 
               // each stream is of the same duration and channels
               res = mProgress->Update(i+sampleDuration*c+ sampleDuration*mScs[s]->m_stream->codec->channels*s, 
                                       sampleDuration*mScs[s]->m_stream->codec->channels*mNumStreams);
               if (res != eProgressSuccess)
                  break;
            }
         }
         tasks.push_back(odTask);
      }
      //Now we add the tasks and let them run, or delete them if the user cancelled
      for(int i=0; i < (int)tasks.size(); i++) {
         if(res==eProgressSuccess)
            ODManager::Instance()->AddNewTask(tasks[i]);
         else
            {
               delete tasks[i];
            }
      }
   } else {
#endif
   streamContext *sc = NULL;

   // Read next frame.
   while ((sc = ReadNextFrame()) != NULL && (res == eProgressSuccess))
   {
      // ReadNextFrame returns 1 if stream is not to be imported
      if (sc != (streamContext*)1)
      {
         // Decode frame until it is not possible to decode any further
         while (sc->m_pktRemainingSiz > 0 && (res == eProgressSuccess || res == eProgressStopped))
         {
            if (DecodeFrame(sc,false) < 0)
               break;

            // If something useable was decoded - write it to mChannels
            if (sc->m_frameValid)
               res = WriteData(sc);
         }

         // Cleanup after frame decoding
         if (sc->m_pktValid)
         {
            av_free_packet(&sc->m_pkt);
            sc->m_pktValid = 0;
         }    
      }
   }

   // Flush the decoders.
   if ((mNumStreams != 0) && (res == eProgressSuccess || res == eProgressStopped))
   {
      for (int i = 0; i < mNumStreams; i++)
      {
         if (DecodeFrame(mScs[i], true) == 0)
         {
            WriteData(mScs[i]);

            if (mScs[i]->m_pktValid)
            {
               av_free_packet(&mScs[i]->m_pkt);
               mScs[i]->m_pktValid = 0;
            }               
         }
      }
   }
#ifdef EXPERIMENTAL_OD_FFMPEG
   } // else -- !mUsingOD == true
#endif   //EXPERIMENTAL_OD_FFMPEG

   // Something bad happened - destroy everything!
   if (res == eProgressCancelled || res == eProgressFailed)
   {
      for (int s = 0; s < mNumStreams; s++)
      {
         delete[] mChannels[s];
      }
      delete[] mChannels;

      return res;
   }
   //else if (res == 2), we just stop the decoding as if the file has ended

   *outNumTracks = 0;
   for (int s = 0; s < mNumStreams; s++)
   {
      *outNumTracks += mScs[s]->m_initialchannels;
   }

   // Create new tracks
   *outTracks = new Track *[*outNumTracks];

   // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
   int trackindex = 0;
   for (int s = 0; s < mNumStreams; s++)
   {
      for(int c = 0; c < mScs[s]->m_initialchannels; c++)
      {
         mChannels[s][c]->Flush();
         (*outTracks)[trackindex++] = mChannels[s][c];
      }
      delete[] mChannels[s];
   }
   delete[] mChannels;

   // Save metadata
   WriteMetadata(tags);

   return res;
}
Пример #9
0
int OggImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks,
                                int *outNumTracks, Tags *tags)
{
   wxASSERT(mFile->IsOpened());

   CreateProgress();

   //Number of streams used may be less than mVorbisFile->links,
   //but this way bitstream matches array index.
   mChannels = new WaveTrack **[mVorbisFile->links];

   int i,c;
   for (i = 0; i < mVorbisFile->links; i++)
   {
      //Stream is not used
      if (mStreamUsage[i] == 0)
      {
         //This is just a padding to keep bitstream number and
         //array indices matched.
         mChannels[i] = NULL;
         continue;
      }

      vorbis_info *vi = ov_info(mVorbisFile, i);
      vorbis_comment *vc = ov_comment(mVorbisFile, i);

      mChannels[i] = new WaveTrack *[vi->channels];

      for (c = 0; c < vi->channels; c++) {
         mChannels[i][c] = trackFactory->NewWaveTrack(int16Sample, vi->rate);

         if (vi->channels == 2) {
            switch (c) {
         case 0:
            mChannels[i][c]->SetChannel(Track::LeftChannel);
            mChannels[i][c]->SetLinked(true);
            break;
         case 1:
            mChannels[i][c]->SetChannel(Track::RightChannel);
            mChannels[i][c]->SetTeamed(true);
            break;
            }
         }
         else {
            mChannels[i][c]->SetChannel(Track::MonoChannel);
         }
      }
   }

/* The number of bytes to get from the codec in each run */
#define CODEC_TRANSFER_SIZE 4096

/* The number of samples to read between calls to the callback.
 * Balance between responsiveness of the GUI and throughput of import. */
#define SAMPLES_PER_CALLBACK 100000

   short *mainBuffer = new short[CODEC_TRANSFER_SIZE];

   /* determine endianness (clever trick courtesy of Nicholas Devillard,
    * (http://www.eso.org/~ndevilla/endian/) */
   int testvar = 1, endian;
   if(*(char *)&testvar)
      endian = 0;  // little endian
   else
      endian = 1;  // big endian

   /* number of samples currently in each channel's buffer */
   bool cancelled = false;
   long bytesRead = 0;
   long samplesRead = 0;
   int bitstream = 0;
   int samplesSinceLastCallback = 0;

   // You would think that the stream would already be seeked to 0, and
   // indeed it is if the file is legit.  But I had several ogg files on
   // my hard drive that have malformed headers, and this added call
   // causes them to be read correctly.  Otherwise they have lots of
   // zeros inserted at the beginning
   ov_pcm_seek(mVorbisFile, 0);
   
   do {
      /* get data from the decoder */
      bytesRead = ov_read(mVorbisFile, (char *) mainBuffer,
                          CODEC_TRANSFER_SIZE,
                          endian,
                          2,    // word length (2 for 16 bit samples)
                          1,    // signed
                          &bitstream);

      if (bytesRead < 0) {
         /* Malformed Ogg Vorbis file. */
         /* TODO: Return some sort of meaningful error. */
         break;
      }

      samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);

      /* give the data to the wavetracks */
      if (mStreamUsage[bitstream] != 0)
      {
         for (c = 0; c < mVorbisFile->vi[bitstream].channels; c++)
            mChannels[bitstream][c]->Append((char *)(mainBuffer + c),
            int16Sample,
            samplesRead,
            mVorbisFile->vi[bitstream].channels);
      }

      samplesSinceLastCallback += samplesRead;
      if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) {
          cancelled = !mProgress->Update(ov_time_tell(mVorbisFile),
                                         ov_time_total(mVorbisFile, bitstream));
          samplesSinceLastCallback -= SAMPLES_PER_CALLBACK;

      }
   } while (!cancelled && bytesRead != 0);

   delete[]mainBuffer;

   bool res = (!cancelled && bytesRead >= 0);

   if (!res) {
      for (i = 0; i < mVorbisFile->links; i++)
      {
         if (mChannels[i])
         {
            for(c = 0; c < mVorbisFile->vi[bitstream].channels; c++) {
               if (mChannels[i][c])
                  delete mChannels[i][c];
            }
            delete[] mChannels[i];
         }
      }
      delete[] mChannels;
      return (cancelled ? eImportCancelled : eImportFailed);
   }

   *outNumTracks = 0;
   for (int s = 0; s < mVorbisFile->links; s++)
   {
      if (mStreamUsage[s] != 0)
         *outNumTracks += mVorbisFile->vi[s].channels;
   }

   *outTracks = new Track *[*outNumTracks];
   
   int trackindex = 0;
   for (i = 0; i < mVorbisFile->links; i++)
   {
      if (mChannels[i])
      {
         for (c = 0; c < mVorbisFile->vi[i].channels; c++) {
            mChannels[i][c]->Flush();
            (*outTracks)[trackindex++] = mChannels[i][c];
         }
         delete[] mChannels[i];
      }      
   }
   delete[] mChannels;

   //\todo { Extract comments from each stream? }
   if (mVorbisFile->vc[0].comments > 0) {
      tags->Clear();
      for (c = 0; c < mVorbisFile->vc[0].comments; c++) {
         wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]);
         wxString name = comment.BeforeFirst(wxT('='));
         wxString value = comment.AfterFirst(wxT('='));
         if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
            long val;
            if (value.Length() == 4 && value.ToLong(&val)) {
               name = TAG_YEAR;
            }
         }
         tags->SetTag(name, value);
      }
   }

   return eImportSuccess;
}
Пример #10
0
int MP3ImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks,
                                int *outNumTracks, Tags *tags)
{
   int chn;

   CreateProgress();

   /* Prepare decoder data, initialize decoder */

   mPrivateData.file        = mFile;
   mPrivateData.inputBuffer = new unsigned char [INPUT_BUFFER_SIZE];
   mPrivateData.progress    = mProgress;
   mPrivateData.channels    = NULL;
   mPrivateData.cancelled   = false;
   mPrivateData.id3checked  = false;
   mPrivateData.numChannels = 0;
   mPrivateData.trackFactory= trackFactory;

   mad_decoder_init(&mDecoder, &mPrivateData, input_cb, 0, 0, output_cb, error_cb, 0);

   /* and send the decoder on its way! */

   bool res = (mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC) == 0) &&
              (mPrivateData.numChannels > 0) &&
              (!mPrivateData.cancelled);

   mad_decoder_finish(&mDecoder);

   delete[] mPrivateData.inputBuffer;

   if (!res) {
      /* failure */
      /* printf("failure\n"); */

      /* delete everything */
      for (chn = 0; chn < mPrivateData.numChannels; chn++) {
         delete mPrivateData.channels[chn];
      }
      delete[] mPrivateData.channels;

      return (mPrivateData.cancelled ? eImportCancelled : eImportFailed);
   }

   /* success */
   /* printf("success\n"); */

      /* copy the WaveTrack pointers into the Track pointer list that
       * we are expected to fill */
   *outNumTracks = mPrivateData.numChannels;
      *outTracks = new Track* [mPrivateData.numChannels];
      for(chn = 0; chn < mPrivateData.numChannels; chn++) {
         mPrivateData.channels[chn]->Flush();
         (*outTracks)[chn] = mPrivateData.channels[chn];
      }
      delete[] mPrivateData.channels;

   /* Read in any metadata */
   ImportID3(tags);

      return eImportSuccess;
   }
Пример #11
0
int QTImportFileHandle::Import(TrackFactory *trackFactory,
                               Track ***outTracks,
                               int *outNumTracks,
                               Tags *tags)
{
   OSErr err = noErr;
   MovieAudioExtractionRef maer = NULL;
   int updateResult = eProgressSuccess;
   sampleCount totSamples = (sampleCount) GetMovieDuration(mMovie);
   sampleCount numSamples = 0;
   Boolean discrete = true;
   UInt32 quality = kQTAudioRenderQuality_Max;
   AudioStreamBasicDescription desc;
   UInt32 maxSampleSize;
   UInt32 numchan;
   UInt32 bufsize;
   bool res = false;

   CreateProgress();

   do
   {
      err = MovieAudioExtractionBegin(mMovie, 0, &maer);
      if (err != noErr) {
         wxMessageBox(_("Unable to start QuickTime extraction"));
         break;
      }
   
      err = MovieAudioExtractionSetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTMovieAudioExtractionAudioPropertyID_RenderQuality,
                                            sizeof(quality),
                                            &quality);
      if (err != noErr) {
         wxMessageBox(_("Unable to set QuickTime render quality"));
         break;
      }
   
      err = MovieAudioExtractionSetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Movie,
                                            kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
                                            sizeof(discrete),
                                            &discrete);
      if (err != noErr) {
         wxMessageBox(_("Unable to set QuickTime discrete channels property"));
         break;
      }
   
      err = MovieAudioExtractionGetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTAudioPropertyID_MaxAudioSampleSize,
                                            sizeof(maxSampleSize),
                                            &maxSampleSize,
                                            NULL);
      if (err != noErr) {
         wxMessageBox(_("Unable to get QuickTime sample size property"));
         break;
      }
   
      err = MovieAudioExtractionGetProperty(maer,
                                            kQTPropertyClass_MovieAudioExtraction_Audio,
                                            kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                            sizeof(desc),
                                            &desc,
                                            NULL);
      if (err != noErr) {
         wxMessageBox(_("Unable to retrieve stream description"));
         break;
      }
   
      numchan = desc.mChannelsPerFrame;
      bufsize = 5 * desc.mSampleRate;
   
      // determine sample format
      sampleFormat format;
      switch (maxSampleSize)
      {
         case 16:
            format = int16Sample;
            break;
   
         case 24:
            format = int24Sample;
            break;
   
         default:
            format = floatSample;
            break;
      }
   
      AudioBufferList *abl = (AudioBufferList *)
         calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numchan));
      abl->mNumberBuffers = numchan;
   
      WaveTrack **channels = new WaveTrack *[numchan];
   
      int c;
      for (c = 0; c < numchan; c++) {
         abl->mBuffers[c].mNumberChannels = 1;
         abl->mBuffers[c].mDataByteSize = sizeof(float) * bufsize;
         abl->mBuffers[c].mData = malloc(abl->mBuffers[c].mDataByteSize);
   
         channels[c] = trackFactory->NewWaveTrack(format);
         channels[c]->SetRate(desc.mSampleRate);
   
         if (numchan == 2) {
            if (c == 0) {
               channels[c]->SetChannel(Track::LeftChannel);
               channels[c]->SetLinked(true);
            }
            else if (c == 1) {
               channels[c]->SetChannel(Track::RightChannel);
            }
         }
      }
   
      do {
         UInt32 flags = 0;
         UInt32 numFrames = bufsize;
   
         err = MovieAudioExtractionFillBuffer(maer,
                                              &numFrames,
                                              abl,
                                              &flags);
         if (err != noErr) {
            wxMessageBox(_("Unable to get fill buffer"));
            break;
         }
   
         for (c = 0; c < numchan; c++) {
            channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames);
         }
   
         numSamples += numFrames;
   
         updateResult = mProgress->Update((wxULongLong_t)numSamples,
                                          (wxULongLong_t)totSamples);
   
         if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) {
            break;
         }
      } while (updateResult == eProgressSuccess);
   
      res = (updateResult == eProgressSuccess && err == noErr);
   
      if (res) {
         for (c = 0; c < numchan; c++) {
            channels[c]->Flush();
         }
   
         *outTracks = (Track **) channels;
         *outNumTracks = numchan;
      }
      else {
         for (c = 0; c < numchan; c++) {
            delete channels[c];
         }
   
         delete [] channels;
      }
   
      for (c = 0; c < numchan; c++) {
         free(abl->mBuffers[c].mData);
      }
      free(abl);
   
      //
      // Extract any metadata
      //
      if (res) {
         AddMetadata(tags);
      }
   } while (false);

done:

   if (maer) {
      MovieAudioExtractionEnd(maer);
   }

   return (res ? eProgressSuccess : eProgressFailed);
}
Пример #12
0
int FLACImportFileHandle::Import(TrackFactory *trackFactory,
				  Track ***outTracks,
				  int *outNumTracks,
              Tags *tags)
{
   wxASSERT(mStreamInfoDone);

   CreateProgress();

   mChannels = new WaveTrack *[mNumChannels];

   unsigned long c;
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c] = trackFactory->NewWaveTrack(mFormat, mSampleRate);
      
      if (mNumChannels == 2) {
         switch (c) {
         case 0:
            mChannels[c]->SetChannel(Track::LeftChannel);
            mChannels[c]->SetLinked(true);
            break;
         case 1:
            mChannels[c]->SetChannel(Track::RightChannel);
            mChannels[c]->SetTeamed(true);
            break;
         }
      }
      else {
         mChannels[c]->SetChannel(Track::MonoChannel);
      }
   }

#ifdef LEGACY_FLAC
   bool res = (mFile->process_until_end_of_file() != 0);
#else
   bool res = (mFile->process_until_end_of_stream() != 0);
#endif

   if (!res) {
      for(c = 0; c < mNumChannels; c++) {
         delete mChannels[c];
      }
      delete[] mChannels;

      return (mCancelled ? eImportCancelled : eImportFailed);
   }
   
   *outNumTracks = mNumChannels;
   *outTracks = new Track *[mNumChannels];
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c]->Flush();
      (*outTracks)[c] = mChannels[c];
   }
   delete[] mChannels;

   tags->Clear();
   size_t cnt = mFile->mComments.GetCount();
   for (c = 0; c < cnt; c++) {
      tags->SetTag(mFile->mComments[c].BeforeFirst(wxT('=')),
                   mFile->mComments[c].AfterFirst(wxT('=')));
   }

   return eImportSuccess;
}
Пример #13
0
// ----------------------------------------------------------------------------
// Import streams
int
GStreamerImportFileHandle::Import(TrackFactory *trackFactory,
                                  Track ***outTracks,
                                  int *outNumTracks,
                                  Tags *tags)
{
   // Save track factory pointer
   mTrackFactory = trackFactory;

   // Create the progrress dialog
   CreateProgress();

   // Block streams that are to be bypassed
   g_mutex_lock(&mStreamsLock);
   bool haveStreams = false;
   for (guint i = 0; i < mStreams->len; i++)
   {
      GStreamContext *c = (GStreamContext *) g_ptr_array_index(mStreams, i);

      // Did the user choose to skip this stream?
      if (!c->mUse)
      {
         // Get the audioconvert sink pad and unlink
         GstPad *convsink = gst_element_get_static_pad(c->mConv, "sink");
         GstPad *convpeer = gst_pad_get_peer(convsink);
         gst_pad_unlink(convpeer, convsink);
         gst_object_unref(convpeer);

         // Set bitbucket callbacks so the prerolled sample won't get processed
         // when we change the state to PLAYING
         gst_app_sink_set_callbacks(GST_APP_SINK(c->mSink), &AppSinkBitBucket, this, NULL);

         // Set state to playing for conv and sink so EOS gets processed
         gst_element_set_state(c->mConv, GST_STATE_PLAYING);
         gst_element_set_state(c->mSink, GST_STATE_PLAYING);

         // Send an EOS event to the pad to force them to drain
         gst_pad_send_event(convsink, gst_event_new_eos());

         // Resync state with pipeline
         gst_element_sync_state_with_parent(c->mConv);
         gst_element_sync_state_with_parent(c->mSink);

         // Done with the pad
         gst_object_unref(convsink);

         // Unlink audioconvert and appsink
         gst_element_unlink(c->mConv, c->mSink);

         // Remove them from the bin
         gst_bin_remove_many(GST_BIN(mPipeline), c->mConv, c->mSink, NULL);

         // All done with them
         c->mConv = NULL;
         c->mSink = NULL;

         continue;
      }

      // We have a stream to process
      haveStreams = true;
   }
   g_mutex_unlock(&mStreamsLock);

   // Can't do much if we don't have any streams to process
   if (!haveStreams)
   {
      wxMessageBox(wxT("File doesn't contain any audio streams."),
                   wxT("GStreamer Importer"));
      return eProgressFailed;
   }

   // Get the ball rolling...
   GstStateChangeReturn state = gst_element_set_state(mPipeline, GST_STATE_PLAYING);
   if (state == GST_STATE_CHANGE_FAILURE)
   {
      wxMessageBox(wxT("Unable to import file, state change failed."),
                   wxT("GStreamer Importer"));
      return eProgressFailed;
   }

   // Get the duration of the stream
   gint64 duration;
   gst_element_query_duration(mPipeline, GST_FORMAT_TIME, &duration);

   // Handle bus messages and update progress while files is importing
   bool success = true;
   int updateResult = eProgressSuccess;
   while (ProcessBusMessage(success) && success && updateResult == eProgressSuccess)
   {
      gint64 position;

      // Update progress indicator and give user chance to abort
      if (gst_element_query_position(mPipeline, GST_FORMAT_TIME, &position))
      {
         updateResult = mProgress->Update((wxLongLong_t) position,
                                          (wxLongLong_t) duration);
      }
   }

   // Disable pipeline
   gst_element_set_state(mPipeline, GST_STATE_NULL);

   // Something bad happened
   if (!success || updateResult == eProgressFailed || updateResult == eProgressCancelled)
   {
      return updateResult;
   }

   // Grah the streams lock
   g_mutex_lock(&mStreamsLock);

   // Count the total number of tracks collected
   *outNumTracks = 0;
   for (guint s = 0; s < mStreams->len; s++)
   {
      GStreamContext *c = (GStreamContext*)g_ptr_array_index(mStreams, s);
      if (c->mChannels)
      {
         *outNumTracks += c->mNumChannels;
      }
   }

   // Create new tracks
   *outTracks = new Track *[*outNumTracks];

   // Copy audio from mChannels to newly created tracks (destroying mChannels in process)
   int trackindex = 0;
   for (guint s = 0; s < mStreams->len; s++)
   {
      GStreamContext *c = (GStreamContext*)g_ptr_array_index(mStreams, s);
      if (c->mChannels)
      {
         for (int ch = 0; ch < c->mNumChannels; ch++)
         {
            c->mChannels[ch]->Flush();
            (*outTracks)[trackindex++] = c->mChannels[ch];
         }

         delete [] c->mChannels;
         c->mChannels = NULL;
      }
   }
   g_mutex_unlock(&mStreamsLock);

   // Set any tags found in the stream
   *tags = mTags;

   return updateResult;
}
Пример #14
0
int PCMImportFileHandle::Import(TrackFactory *trackFactory,
                                Track ***outTracks,
                                int *outNumTracks,
                                Tags *tags)
{
    wxASSERT(mFile);

    CreateProgress();

    WaveTrack **channels = new WaveTrack *[mInfo.channels];

    int c;
    for (c = 0; c < mInfo.channels; c++) {
        channels[c] = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate);

        if (mInfo.channels > 1)
            switch (c) {
            case 0:
                channels[c]->SetChannel(Track::LeftChannel);
                break;
            case 1:
                channels[c]->SetChannel(Track::RightChannel);
                break;
            default:
                channels[c]->SetChannel(Track::MonoChannel);
            }
    }

    if (mInfo.channels == 2) {
        channels[0]->SetLinked(true);
        channels[1]->SetTeamed(true);
    }

    sampleCount fileTotalFrames = (sampleCount)mInfo.frames;
    sampleCount maxBlockSize = channels[0]->GetMaxBlockSize();
    bool cancelled = false;

    wxString copyEdit =
        gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("edit"));

    // Fall back to "edit" if it doesn't match anything else
    bool doEdit = true;
    if (copyEdit.IsSameAs(wxT("copy"), false))
        doEdit = false;

    // If the format is not seekable, we must use 'copy' mode,
    // because 'edit' mode depends on the ability to seek to an
    // arbitrary location in the file.
    if (!mInfo.seekable)
        doEdit = false;

    if (doEdit) {
        wxLogDebug(wxT("Importing PCM Start\n"));

        // If this mode has been selected, we form the tracks as
        // aliases to the files we're editing, i.e. ("foo.wav", 12000-18000)
        // instead of actually making fresh copies of the samples.

        for (sampleCount i = 0; i < fileTotalFrames; i += maxBlockSize) {

            sampleCount blockLen = maxBlockSize;
            if (i + blockLen > fileTotalFrames)
                blockLen = fileTotalFrames - i;

            for (c = 0; c < mInfo.channels; c++)
                channels[c]->AppendAlias(mFilename, i, blockLen, c);

            cancelled = !mProgress->Update(i, fileTotalFrames);
            if (cancelled)
                break;
        }

#ifdef EXPERIMENTAL_ONDEMAND
        //now go over the wavetrack/waveclip/sequence and load all the blockfiles into a ComputeSummaryTask.
        //Add this task to the ODManager and the Track itself.
        wxLogDebug(wxT("Importing PCM \n"));
        for (c = 0; c < mInfo.channels; c++)
        {
            ODComputeSummaryTask* computeTask;
            computeTask=new ODComputeSummaryTask;

            computeTask->SetWaveTrack(channels[c]);

            ODManager::Instance()->AddTaskToWaveTrack(computeTask,channels[c]);
        }


#endif

    }
    else {
        // Otherwise, we're in the "copy" mode, where we read in the actual
        // samples from the file and store our own local copy of the
        // samples in the tracks.

        samplePtr srcbuffer = NewSamples(maxBlockSize * mInfo.channels,
                                         mFormat);
        samplePtr buffer = NewSamples(maxBlockSize, mFormat);

        unsigned long framescompleted = 0;

        long block;
        do {
            block = maxBlockSize;

            if (mFormat == int16Sample)
                block = sf_readf_short(mFile, (short *)srcbuffer, block);
            else
                block = sf_readf_float(mFile, (float *)srcbuffer, block);

            if (block) {
                for(c=0; c<mInfo.channels; c++) {
                    if (mFormat==int16Sample) {
                        for(int j=0; j<block; j++)
                            ((short *)buffer)[j] =
                                ((short *)srcbuffer)[mInfo.channels*j+c];
                    }
                    else {
                        for(int j=0; j<block; j++)
                            ((float *)buffer)[j] =
                                ((float *)srcbuffer)[mInfo.channels*j+c];
                    }

                    channels[c]->Append(buffer, mFormat, block);
                }
                framescompleted += block;
            }

            cancelled = !mProgress->Update((long long unsigned)framescompleted,
                                           (long long unsigned)fileTotalFrames);
            if (cancelled)
                break;

        } while (block > 0);
    }

    if (cancelled) {
        for (c = 0; c < mInfo.channels; c++)
            delete channels[c];
        delete[] channels;

        return eImportCancelled;
    }

    *outNumTracks = mInfo.channels;
    *outTracks = new Track *[mInfo.channels];
    for(c = 0; c < mInfo.channels; c++) {
        channels[c]->Flush();
        (*outTracks)[c] = channels[c];
    }
    delete[] channels;

    const char *str;

    str = sf_get_string(mFile, SF_STR_TITLE);
    if (str) {
        tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
    }

    str = sf_get_string(mFile, SF_STR_ARTIST);
    if (str) {
        tags->SetTag(TAG_ARTIST, UTF8CTOWX(str));
    }

    str = sf_get_string(mFile, SF_STR_COMMENT);
    if (str) {
        tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str));
    }

    str = sf_get_string(mFile, SF_STR_DATE);
    if (str) {
        tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
    }

    str = sf_get_string(mFile, SF_STR_COPYRIGHT);
    if (str) {
        tags->SetTag(wxT("Copyright"), UTF8CTOWX(str));
    }

    str = sf_get_string(mFile, SF_STR_SOFTWARE);
    if (str) {
        tags->SetTag(wxT("Software"), UTF8CTOWX(str));
    }

    return eImportSuccess;
}
Пример #15
0
int OggImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTracks,
                                Tags *tags)
{
   outTracks.clear();

   wxASSERT(mFile->IsOpened());

   CreateProgress();

   //Number of streams used may be less than mVorbisFile->links,
   //but this way bitstream matches array index.
   mChannels.resize(mVorbisFile->links);

   int i = -1;
   for (auto &link: mChannels)
   {
      ++i;

      //Stream is not used
      if (mStreamUsage[i] == 0)
      {
         //This is just a padding to keep bitstream number and
         //array indices matched.
         continue;
      }

      vorbis_info *vi = ov_info(mVorbisFile, i);

      link.resize(vi->channels);

      int c = - 1;
      for (auto &channel : link) {
         ++c;

         channel = trackFactory->NewWaveTrack(mFormat, vi->rate);

         if (vi->channels == 2) {
            switch (c) {
         case 0:
            channel->SetChannel(Track::LeftChannel);
            channel->SetLinked(true);
            break;
         case 1:
            channel->SetChannel(Track::RightChannel);
            break;
            }
         }
         else {
            channel->SetChannel(Track::MonoChannel);
         }
      }
   }

/* The number of bytes to get from the codec in each run */
#define CODEC_TRANSFER_SIZE 4096

/* The number of samples to read between calls to the callback.
 * Balance between responsiveness of the GUI and throughput of import. */
#define SAMPLES_PER_CALLBACK 100000

   short *mainBuffer = new short[CODEC_TRANSFER_SIZE];

   /* determine endianness (clever trick courtesy of Nicholas Devillard,
    * (http://www.eso.org/~ndevilla/endian/) */
   int testvar = 1, endian;
   if(*(char *)&testvar)
      endian = 0;  // little endian
   else
      endian = 1;  // big endian

   /* number of samples currently in each channel's buffer */
   int updateResult = eProgressSuccess;
   long bytesRead = 0;
   long samplesRead = 0;
   int bitstream = 0;
   int samplesSinceLastCallback = 0;

   // You would think that the stream would already be seeked to 0, and
   // indeed it is if the file is legit.  But I had several ogg files on
   // my hard drive that have malformed headers, and this added call
   // causes them to be read correctly.  Otherwise they have lots of
   // zeros inserted at the beginning
   ov_pcm_seek(mVorbisFile, 0);

   do {
      /* get data from the decoder */
      bytesRead = ov_read(mVorbisFile, (char *) mainBuffer,
                          CODEC_TRANSFER_SIZE,
                          endian,
                          2,    // word length (2 for 16 bit samples)
                          1,    // signed
                          &bitstream);

      if (bytesRead == OV_HOLE) {
         wxFileName ff(mFilename);
         wxLogError(wxT("Ogg Vorbis importer: file %s is malformed, ov_read() reported a hole"),
                    ff.GetFullName().c_str());
         /* http://lists.xiph.org/pipermail/vorbis-dev/2001-February/003223.html
          * is the justification for doing this - best effort for malformed file,
          * hence the message.
          */
         continue;
      } else if (bytesRead < 0) {
         /* Malformed Ogg Vorbis file. */
         /* TODO: Return some sort of meaningful error. */
         wxLogError(wxT("Ogg Vorbis importer: ov_read() returned error %i"),
                    bytesRead);
         break;
      }

      samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);

      /* give the data to the wavetracks */
      auto iter = mChannels.begin();
      std::advance(iter, bitstream);
      if (mStreamUsage[bitstream] != 0)
      {
         auto iter2 = iter->begin();
         for (int c = 0; c < mVorbisFile->vi[bitstream].channels; ++iter2, ++c)
            iter2->get()->Append((char *)(mainBuffer + c),
            int16Sample,
            samplesRead,
            mVorbisFile->vi[bitstream].channels);
      }

      samplesSinceLastCallback += samplesRead;
      if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) {
          updateResult = mProgress->Update(ov_time_tell(mVorbisFile),
                                         ov_time_total(mVorbisFile, bitstream));
          samplesSinceLastCallback -= SAMPLES_PER_CALLBACK;

      }
   } while (updateResult == eProgressSuccess && bytesRead != 0);

   delete[]mainBuffer;

   int res = updateResult;
   if (bytesRead < 0)
     res = eProgressFailed;

   if (res == eProgressFailed || res == eProgressCancelled) {
      return res;
   }

   for (auto &link : mChannels)
   {
      for (auto &channel : link) {
         channel->Flush();
         outTracks.push_back(std::move(channel));
      }
   }

   //\todo { Extract comments from each stream? }
   if (mVorbisFile->vc[0].comments > 0) {
      tags->Clear();
      for (int c = 0; c < mVorbisFile->vc[0].comments; c++) {
         wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]);
         wxString name = comment.BeforeFirst(wxT('='));
         wxString value = comment.AfterFirst(wxT('='));
         if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
            long val;
            if (value.Length() == 4 && value.ToLong(&val)) {
               name = TAG_YEAR;
            }
         }
         tags->SetTag(name, value);
      }
   }

   return res;
}
Пример #16
0
int FLACImportFileHandle::Import(TrackFactory *trackFactory,
				  Track ***outTracks,
				  int *outNumTracks,
              Tags *tags)
{
   wxASSERT(mStreamInfoDone);

   CreateProgress();

   mChannels = new WaveTrack *[mNumChannels];

   unsigned long c;
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c] = trackFactory->NewWaveTrack(mFormat, mSampleRate);
      
      if (mNumChannels == 2) {
         switch (c) {
         case 0:
            mChannels[c]->SetChannel(Track::LeftChannel);
            mChannels[c]->SetLinked(true);
            break;
         case 1:
            mChannels[c]->SetChannel(Track::RightChannel);
            break;
         }
      }
      else {
         mChannels[c]->SetChannel(Track::MonoChannel);
      }
   }

#ifdef LEGACY_FLAC
   bool res = (mFile->process_until_end_of_file() != 0);
#else
   bool res = (mFile->process_until_end_of_stream() != 0);
#endif

   if (mUpdateResult == eProgressFailed || mUpdateResult == eProgressCancelled) {
      for(c = 0; c < mNumChannels; c++) {
         delete mChannels[c];
      }
      delete[] mChannels;

      return mUpdateResult;
   }
   
   *outNumTracks = mNumChannels;
   *outTracks = new Track *[mNumChannels];
   for (c = 0; c < mNumChannels; c++) {
      mChannels[c]->Flush();
      (*outTracks)[c] = mChannels[c];
   }
   delete[] mChannels;

   tags->Clear();
   size_t cnt = mFile->mComments.GetCount();
   for (c = 0; c < cnt; c++) {
      wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
      wxString value = mFile->mComments[c].AfterFirst(wxT('='));
      if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
         long val;
         if (value.Length() == 4 && value.ToLong(&val)) {
            name = TAG_YEAR;
         }
      }
      tags->SetTag(name, value);
   }

   return mUpdateResult;
}