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