📄 changespeed.cpp
字号:
/********************************************************************** Audacity: A Digital Audio Editor ChangeSpeed.cpp Vaughan Johnson, Dominic Mazzoni Change Speed effect, that affects both pitch & tempo.**********************************************************************/#include "ChangeSpeed.h"#include "../Audacity.h" // for USE_LIBSAMPLERATE#include "../Envelope.h"#include "../Prefs.h"#include <math.h>#include <wx/button.h>#include <wx/msgdlg.h> // for wxMessageBox#include <wx/sizer.h>#include <wx/stattext.h>#include <wx/valtext.h>//// EffectChangeSpeed//EffectChangeSpeed::EffectChangeSpeed(){ // control values m_PercentChange = 0.0; m_FromVinyl = 0; m_ToVinyl = 0; }wxString EffectChangeSpeed::GetEffectDescription() { // Note: This is useful only after change amount has been set. return wxString::Format(_("Applied effect: %s %.1f%%"), this->GetEffectName().c_str(), m_PercentChange); } bool EffectChangeSpeed::PromptUser(){ ChangeSpeedDialog dlog(this, mParent, -1, _("Change Speed")); dlog.m_PercentChange = m_PercentChange; dlog.m_FromVinyl = m_FromVinyl; dlog.m_ToVinyl = m_ToVinyl; // Don't need to call TransferDataToWindow, although other // Audacity dialogs (from which I derived this one) do it, because // ShowModal calls stuff that eventually calls wxWindowBase::OnInitDialog, // which calls dlog.TransferDataToWindow(); dlog.CentreOnParent(); dlog.ShowModal(); if (!dlog.GetReturnCode()) return false; m_PercentChange = dlog.m_PercentChange; m_FromVinyl = dlog.m_FromVinyl; m_ToVinyl = dlog.m_ToVinyl; return true;}bool EffectChangeSpeed::TransferParameters( Shuttle & shuttle ){ shuttle.TransferDouble(wxT("Percentage"),m_PercentChange,0.0); return true;}bool EffectChangeSpeed::Process(){ // Similar to EffectSoundTouch::Process() //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack *track = (WaveTrack *) iter.First(); mCurTrackNum = 0; m_maxNewLength = 0.0; double curT0; double curT1; while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: curT0 = mT0 < trackStart? trackStart: mT0; curT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (curT1 > curT0) { //Transform the marker timepoints to samples longSampleCount start = track->TimeToLongSamples(curT0); longSampleCount end = track->TimeToLongSamples(curT1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) return false; } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } mT1 = mT0 + m_maxNewLength; // Update selection. return true;}// ProcessOne() takes a track, transforms it to bunch of buffer-blocks,// and calls libsamplerate code on these blocks.bool EffectChangeSpeed::ProcessOne(WaveTrack * track, longSampleCount start, longSampleCount end){ if (track == NULL) return false; // initialization, per examples of Mixer::Mixer and // EffectSoundTouch::ProcessOne WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat()); //Get the length of the selection (as double). len is //used simple to calculate a progress meter, so it is easier //to make it a double now than it is to do it later double len = (double)(end - start); // Initiate processing buffers, most likely shorter than // the length of the selection being processed. sampleCount inBufferSize = track->GetMaxBlockSize(); float * inBuffer = new float[inBufferSize]; double factor = 100.0 / (100.0 + m_PercentChange); sampleCount outBufferSize = (sampleCount)((factor * inBufferSize) + 10); float * outBuffer = new float[outBufferSize]; // Set up the resampling stuff for this track. Resample resample(true, factor, factor); //Go through the track one buffer at a time. samplePos counts which //sample the current buffer starts at. bool bLoopSuccess = true; sampleCount blockSize; longSampleCount samplePos = start; while (samplePos < end) { //Get a blockSize of samples (smaller than the size of the buffer) blockSize = track->GetBestBlockSize(samplePos); //Adjust the block size if it is the final block in the track if (samplePos + blockSize > end) blockSize = end - samplePos; //Get the samples from the track and put them in the buffer track->Get((samplePtr) inBuffer, floatSample, samplePos, blockSize); int inUsed; int outgen = resample.Process(factor, inBuffer, blockSize, ((samplePos + blockSize) >= end), &inUsed, outBuffer, outBufferSize); if (outgen < 0) { bLoopSuccess = false; break; } if (outgen > 0) outputTrack->Append((samplePtr)outBuffer, floatSample, outgen); // Increment samplePos samplePos += inUsed; // Update the Progress meter if (TrackProgress(mCurTrackNum, (samplePos - start) / len)) { bLoopSuccess = false; break; } } // Flush the output WaveTrack (since it's buffered, too) outputTrack->Flush(); // Clean up the buffers delete [] inBuffer; delete [] outBuffer; // Take the output track and insert it in place of the original // sample data if (bLoopSuccess) { track->Clear(mT0, mT1); track->Paste(mT0, outputTrack); } double newLength = outputTrack->GetEndTime(); if (newLength > m_maxNewLength) m_maxNewLength = newLength; // Delete the outputTrack now that its data is inserted in place delete outputTrack; return bLoopSuccess;}//----------------------------------------------------------------------------// ChangeSpeedDialog//----------------------------------------------------------------------------// -99 for PERCENTCHANGE_MIN because -100% is nonsensical.#define PERCENTCHANGE_MIN -99#define PERCENTCHANGE_MAX 100 // warped above zero to actually go up to 400%#define PERCENTCHANGE_SLIDER_WARP 1.30105 // warp power takes max from 100 to 400.// the standard vinyl RPM choices// If the percent change is not one of these ratios, the choice control gets "n/a".#define CHOICE_33ANDATHIRD 0#define CHOICE_45 1#define CHOICE_78 2#define CHOICE_NA 3 enum { ID_TEXT_PERCENTCHANGE = 10001, ID_SLIDER_PERCENTCHANGE, ID_CHOICE_FROMVINYL, ID_CHOICE_TOVINYL, ID_BUTTON_PREVIEW};// event table for ChangeSpeedDialogBEGIN_EVENT_TABLE(ChangeSpeedDialog, wxDialog) EVT_BUTTON(wxID_OK, ChangeSpeedDialog::OnOk) EVT_BUTTON(wxID_CANCEL, ChangeSpeedDialog::OnCancel) EVT_TEXT(ID_TEXT_PERCENTCHANGE, ChangeSpeedDialog::OnText_PercentChange) EVT_SLIDER(ID_SLIDER_PERCENTCHANGE, ChangeSpeedDialog::OnSlider_PercentChange) EVT_CHOICE(ID_CHOICE_FROMVINYL, ChangeSpeedDialog::OnChoice_FromVinyl) EVT_CHOICE(ID_CHOICE_TOVINYL, ChangeSpeedDialog::OnChoice_ToVinyl) EVT_BUTTON(ID_BUTTON_PREVIEW, ChangeSpeedDialog::OnPreview)END_EVENT_TABLE()ChangeSpeedDialog::ChangeSpeedDialog(EffectChangeSpeed * effect, wxWindow * parent, wxWindowID id, const wxString & title, const wxPoint & position, const wxSize & size, long style): wxDialog(parent, id, title, position, size, style){ m_bLoopDetect = false; m_pEffect = effect; // NULL out these control members because there are some cases where the // event table handlers get called during this method, and those handlers that // can cause trouble check for NULL. m_pTextCtrl_PercentChange = NULL; m_pSlider_PercentChange = NULL; m_pChoice_FromVinyl = NULL; m_pChoice_ToVinyl = NULL; // effect parameters m_PercentChange = 0.0; m_FromVinyl = 0; m_ToVinyl = 0; // CREATE THE CONTROLS PROGRAMMATICALLY. wxStaticText * pStaticText; wxBoxSizer * pBoxSizer_Dialog = new wxBoxSizer(wxVERTICAL); // heading pStaticText = new wxStaticText(this, -1, _("Change Speed, affecting both Tempo and Pitch"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxALL, 8); pStaticText = new wxStaticText(this, -1, _("by Vaughan Johnson && Dominic Mazzoni"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxTOP | wxLEFT | wxRIGHT, 8); pStaticText = new wxStaticText(this, -1, _("using SampleRate, by Erik de Castro Lopo"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxBOTTOM | wxLEFT | wxRIGHT, 8); // percent change controls // Group percent controls with spacers, // rather than static box, so they don't look isolated. pBoxSizer_Dialog->Add(0, 8, 0); // spacer wxBoxSizer * pBoxSizer_PercentChange = new wxBoxSizer(wxHORIZONTAL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -