bool EffectSilence::Process() { if (length <= 0.0) length = sDefaultGenerateLen; TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *)iter.First(); while (track) { WaveTrack *tmp = mFactory->NewWaveTrack(track->GetSampleFormat(), track->GetRate()); tmp->InsertSilence(0.0, length); tmp->Flush(); track->Clear(mT0, mT1); track->Paste(mT0, tmp); delete tmp; //Iterate to the next track track = (WaveTrack *)iter.Next(); } mT1 = mT0 + length; // Update selection. return true; }
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; }
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; }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { mRecord->PopUp(); return; } AudacityProject *p = GetActiveProject(); if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored. mRecord->SetShift(true); if( evt.GetInt() == 2 ) mRecord->SetShift(false); SetRecord(true, mRecord->WasShiftDown()); if (p) { TrackList *t = p->GetTracks(); TrackListIterator it(t); if(it.First() == NULL) mRecord->SetShift(false); double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; #ifdef EXPERIMENTAL_MIDI_OUT NoteTrackArray midiTracks; #endif bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true); if(duplex){ playbackTracks = t->GetWaveTrackArray(false); #ifdef EXPERIMENTAL_MIDI_OUT midiTracks = t->GetNoteTrackArray(false); #endif } else { playbackTracks = WaveTrackArray(); #ifdef EXPERIMENTAL_MIDI_OUT midiTracks = NoteTrackArray(); #endif } // If SHIFT key was down, the user wants append to tracks int recordingChannels = 0; bool shifted = mRecord->WasShiftDown(); if (shifted) { bool sel = false; double allt0 = t0; // Find the maximum end time of selected and all wave tracks // Find whether any tracks were selected. (If any are selected, // record only into them; else if tracks exist, record into all.) for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave) { WaveTrack *wt = static_cast<WaveTrack *>(tt); if (wt->GetEndTime() > allt0) { allt0 = wt->GetEndTime(); } if (tt->GetSelected()) { sel = true; if (wt->GetEndTime() > t0) { t0 = wt->GetEndTime(); } } } } // Use end time of all wave tracks if none selected if (!sel) { t0 = allt0; } // Pad selected/all wave tracks to make them all the same length // Remove recording tracks from the list of tracks for duplex ("overdub") // playback. for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) { WaveTrack *wt = static_cast<WaveTrack *>(tt); if (duplex) playbackTracks.Remove(wt); t1 = wt->GetEndTime(); if (t1 < t0) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->InsertSilence(0.0, t0 - t1); newTrack->Flush(); wt->Clear(t1, t0); bool bResult = wt->Paste(t1, newTrack); wxASSERT(bResult); // TO DO: Actually handle this. delete newTrack; } newRecordingTracks.Add(wt); } } t1 = 1000000000.0; // record for a long, long time (tens of years) } else { recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2); for (int c = 0; c < recordingChannels; c++) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->SetOffset(t0); if (recordingChannels > 2) newTrack->SetMinimized(true); if (recordingChannels == 2) { if (c == 0) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else { newTrack->SetChannel(Track::RightChannel); } } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } // msmeyer: StartStream calls a callback which triggers auto-save, so // we add the tracks where recording is done into now. We remove them // later if starting the stream fails for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) t->Add(newRecordingTracks[i]); } //Automated Input Level Adjustment Initialization #ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILAInitialize(); #endif int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, #ifdef EXPERIMENTAL_MIDI_OUT midiTracks, #endif t->GetTimeTrack(), p->GetRate(), t0, t1, p); bool success = (token != 0); if (success) { p->SetAudioIOToken(token); mBusyProject = p; } else { // msmeyer: Delete recently added tracks if opening stream fails if (!shifted) { for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) { t->Remove(newRecordingTracks[i]); delete newRecordingTracks[i]; } } // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. Please check the recording device settings and the project sample rate."), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); SetStop(false); SetRecord(false); } } }
void ControlToolBar::OnRecord(wxCommandEvent &evt) { if (gAudioIO->IsBusy()) { mRecord->PopUp(); return; } AudacityProject *p = GetActiveProject(); if (p && p->GetCleanSpeechMode()) { size_t numProjects = gAudacityProjects.Count(); bool tracks = (p && !p->GetTracks()->IsEmpty()); if (tracks || (numProjects > 1)) { wxMessageBox(_("Recording in CleanSpeech mode is not possible when a track, or more than one project, is already open."), _("Recording not permitted"), wxOK | wxICON_INFORMATION, this); mRecord->PopUp(); mRecord->Disable(); return; } } if( evt.GetInt() == 1 ) // used when called by keyboard shortcut. Default (0) ignored. mRecord->SetShift(true); if( evt.GetInt() == 2 ) mRecord->SetShift(false); SetRecord(true); if (p) { TrackList *t = p->GetTracks(); TrackListIterator it(t); if(it.First() == NULL) mRecord->SetShift(false); double t0 = p->GetSel0(); double t1 = p->GetSel1(); if (t1 == t0) t1 = 1000000000.0; // record for a long, long time (tens of years) /* TODO: set up stereo tracks if that is how the user has set up * their preferences, and choose sample format based on prefs */ WaveTrackArray newRecordingTracks, playbackTracks; /* REQUIRES PORTMIDI */ // NoteTrackArray midiTracks; bool duplex; gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true); if(duplex){ playbackTracks = t->GetWaveTrackArray(false); /* REQUIRES PORTMIDI */ // midiTracks = t->GetNoteTrackArray(false); } else { playbackTracks = WaveTrackArray(); /* REQUIRES PORTMIDI */ // midiTracks = NoteTrackArray(); } // If SHIFT key was down, the user wants append to tracks int recordingChannels = 0; bool shifted = mRecord->WasShiftDown(); if (shifted) { TrackListIterator it(t); WaveTrack *wt; bool sel = false; double allt0 = t0; // Find the maximum end time of selected and all wave tracks for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave) { wt = (WaveTrack *)tt; if (wt->GetEndTime() > allt0) { allt0 = wt->GetEndTime(); } if (tt->GetSelected()) { sel = true; if (duplex) playbackTracks.Remove(wt); if (wt->GetEndTime() > t0) { t0 = wt->GetEndTime(); } } } } // Use end time of all wave tracks if none selected if (!sel) { t0 = allt0; } // Pad selected/all wave tracks to make them all the same length for (Track *tt = it.First(); tt; tt = it.Next()) { if (tt->GetKind() == Track::Wave && (tt->GetSelected() || !sel)) { wt = (WaveTrack *)tt; t1 = wt->GetEndTime(); if (t1 < t0) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); newTrack->InsertSilence(0.0, t0 - t1); newTrack->Flush(); wt->Clear(t1, t0); wt->Paste(t1, newTrack); delete newTrack; } newRecordingTracks.Add(wt); } } t1 = 1000000000.0; // record for a long, long time (tens of years) } else { recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2); for (int c = 0; c < recordingChannels; c++) { WaveTrack *newTrack = p->GetTrackFactory()->NewWaveTrack(); int initialheight = newTrack->GetHeight(); newTrack->SetOffset(t0); if (recordingChannels <= 2) { newTrack->SetHeight(initialheight/recordingChannels); } else { newTrack->SetMinimized(true); } if (recordingChannels == 2) { if (c == 0) { newTrack->SetChannel(Track::LeftChannel); newTrack->SetLinked(true); } else { newTrack->SetChannel(Track::RightChannel); newTrack->SetTeamed(true); } } else { newTrack->SetChannel( Track::MonoChannel ); } newRecordingTracks.Add(newTrack); } // msmeyer: StartStream calls a callback which triggers auto-save, so // we add the tracks where recording is done into now. We remove them // later if starting the stream fails for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) t->Add(newRecordingTracks[i]); } int token = gAudioIO->StartStream(playbackTracks, newRecordingTracks, /* REQUIRES PORTMIDI */ // midiTracks, t->GetTimeTrack(), p->GetRate(), t0, t1, p); bool success = (token != 0); if (success) { p->SetAudioIOToken(token); mBusyProject = p; SetVUMeters(p); } else { // msmeyer: Delete recently added tracks if opening stream fails if (!shifted) { for (unsigned int i = 0; i < newRecordingTracks.GetCount(); i++) { t->Remove(newRecordingTracks[i]); delete newRecordingTracks[i]; } } // msmeyer: Show error message if stream could not be opened wxMessageBox(_("Error while opening sound device. " wxT("Please check the input device settings and the project sample rate.")), _("Error"), wxOK | wxICON_EXCLAMATION, this); SetPlay(false); SetStop(false); SetRecord(false); } } }