1#include "../CommonCommandFlags.h"
2#include "../LabelTrack.h"
11#include "../ProjectSettings.h"
14#include "../ProjectWindow.h"
15#include "../SelectUtilities.h"
18#include "../TrackPanelAx.h"
19#include "../TrackPanel.h"
20#include "../TrackUtilities.h"
25#include "../commands/CommandContext.h"
26#include "../commands/CommandManager.h"
27#include "../effects/EffectManager.h"
28#include "../effects/EffectUI.h"
30#include "../tracks/playabletrack/wavetrack/ui/WaveTrackControls.h"
31#include "../toolbars/ToolManager.h"
32#include "../widgets/ASlider.h"
36#include <wx/combobox.h>
38#ifdef EXPERIMENTAL_SCOREALIGN
39#include "../effects/ScoreAlignDialog.h"
40#include "audioreader.h"
41#include "scorealign.h"
42#include "scorealign-glue.h"
60 tracks.MakeUniqueTrackName(
_(
"Mix")),
61 &trackFactory, rate, defaultFormat, 0.0, 0.0);
67 auto last = *trackRange.rbegin();
68 auto insertionPoint = * ++
tracks.Find(last);
70 auto selectedCount = trackRange.size();
73 if (selectedCount > 0) {
74 firstName = (*trackRange.begin())->GetName();
75 firstColour = (*trackRange.begin())->GetWaveColorIndex();
79 while (!trackRange.empty())
81 tracks.Remove(**trackRange.first++);
85 const bool stereo = newTracks->
NChannels() > 1;
86 tracks.Append(std::move(*newTracks));
90 if (selectedCount == 1)
91 pNewTrack->SetName(firstName);
94 if (selectedCount >= 1) {
95 pNewTrack->SetSelected(!toNewTrack);
96 pNewTrack->SetWaveColorIndex(firstColour);
102 if (insertionPoint) {
103 std::vector<Track *> arr;
104 arr.reserve(
tracks.Size());
105 size_t iBegin = 0, ii = 0;
106 for (
const auto pTrack :
tracks) {
107 arr.push_back(pTrack);
108 if (pTrack == insertionPoint)
112 const auto end = arr.end(),
114 std::rotate(arr.begin() + iBegin, mid,
end);
119 if (selectedCount==1) {
120 auto msg =
XO(
"Rendered all audio in track '%s'").Format( firstName );
127 ?
XO(
"Mixed and rendered %d tracks into one new stereo track")
128 :
XO(
"Mixed and rendered %d tracks into one new mono track")
130 .
Format((
int)selectedCount);
134 trackPanel.SetFocus();
148 auto count = selectedRange.
size();
151 for (
auto left : count == 0 ? range : selectedRange )
152 left->SetPan( PanValue );
157 .
PushState(
XO(
"Panned audio track(s)"),
XO(
"Pan Track"), flags);
172static const std::vector< ComponentInterfaceSymbol >
173&
alignLabels() {
static std::vector< ComponentInterfaceSymbol > symbols{
174 {
wxT(
"StartToZero"),
XXO(
"Start to &Zero") },
175 {
wxT(
"StartToSelStart"),
XXO(
"Start to &Cursor/Selection Start") },
176 {
wxT(
"StartToSelEnd"),
XXO(
"Start to Selection &End") },
177 {
wxT(
"EndToSelStart"),
XXO(
"End to Cu&rsor/Selection Start") },
178 {
wxT(
"EndToSelEnd"),
XXO(
"End to Selection En&d") },
191 double newPos = -1.0;
198 auto firstTrackOffset = [&]{
return FindOffset( *trackRange.begin() ); };
199 auto minOffset = [&]{
return trackRange.min( FindOffset ); };
200 auto avgOffset = [&]{
201 return trackRange.sum( FindOffset ) /
202 std::max(
size_t(1), trackRange.size() ); };
204 auto maxEndOffset = [&]{
209 delta = -minOffset();
216 ?
XO(
"Aligned/Moved start to zero")
217 :
XO(
"Aligned start to zero");
218 shortAction = moveSel
221 ?
XO(
"Align/Move Start")
225 delta = selectedRegion.t0() - minOffset();
227 ?
XO(
"Aligned/Moved start to cursor/selection start")
228 :
XO(
"Aligned start to cursor/selection start");
229 shortAction = moveSel
230 ?
XO(
"Align/Move Start")
234 delta = selectedRegion.t1() - minOffset();
236 ?
XO(
"Aligned/Moved start to selection end")
237 :
XO(
"Aligned start to selection end");
238 shortAction = moveSel
239 ?
XO(
"Align/Move Start")
243 delta = selectedRegion.t0() - maxEndOffset();
245 ?
XO(
"Aligned/Moved end to cursor/selection start")
246 :
XO(
"Aligned end to cursor/selection start");
249 ?
XO(
"Align/Move End")
253 delta = selectedRegion.t1() - maxEndOffset();
255 ?
XO(
"Aligned/Moved end to selection end")
256 :
XO(
"Aligned end to selection end");
259 ?
XO(
"Align/Move End")
264 newPos = firstTrackOffset();
266 ?
XO(
"Aligned/Moved end to end")
267 :
XO(
"Aligned end to end");
270 ?
XO(
"Align/Move End to End")
271 :
XO(
"Align End to End");
274 newPos = avgOffset();
276 ?
XO(
"Aligned/Moved together")
277 :
XO(
"Aligned together");
280 ?
XO(
"Align/Move Together")
281 :
XO(
"Align Together");
292 newPos += (t->GetEndTime() - t->GetStartTime());
300 for (
auto t :
tracks.Any()
302 t->MoveTo(t->GetStartTime() + delta);
306 selectedRegion.move(delta);
311#ifdef EXPERIMENTAL_SCOREALIGN
326#define AUDIO_WORK_UNIT 0.004F
327#define MIDI_WORK_UNIT 0.0001F
328#define MATRIX_WORK_UNIT 0.000002F
329#define SMOOTHING_WORK_UNIT 0.000001F
335#define COLLECT_TIMING_DATA
338class ASAProgress final :
public SAProgress {
345 std::optional<ProgressDialog> mProgress;
346 #ifdef COLLECT_TIMING_DATA
348 wxDateTime mStartTime;
355 #ifdef COLLECT_TIMING_DATA
356 mTimeFile = fopen(
"timing-data.txt",
"w");
360 #ifdef COLLECT_TIMING_DATA
364 void set_phase(
int i)
override {
366 float work2, work3 = 0;
367 SAProgress::set_phase(i);
368 #ifdef COLLECT_TIMING_DATA
370 wxDateTime now = wxDateTime::UNow();
371 wxFprintf(mTimeFile,
"Phase %d begins at %s\n",
372 i, now.FormatTime());
374 ms = now.Subtract(mStartTime).GetMilliseconds().ToLong();
379 for (
int j = 0; j < 2; j++) {
380 mFrames[j] = durations[j] / frame_period;
383 for (
int j = 0; j < 2; j++) {
385 (is_audio[j] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[j];
386 mTotalWork += work[j];
388 mTotalCells = mFrames[0] * mFrames[1];
389 work2 = mTotalCells * MATRIX_WORK_UNIT;
395 wxMax(mFrames[0], mFrames[1]) * SMOOTHING_WORK_UNIT * 40;
398 #ifdef COLLECT_TIMING_DATA
400 " mTotalWork (an estimate) = %g\n", mTotalWork);
401 wxFprintf(mTimeFile,
" work0 = %g, frames %g, is_audio %d\n",
402 work[0], mFrames[0], is_audio[0]);
403 wxFprintf(mTimeFile,
" work1 = %g, frames %g, is_audio %d\n",
404 work[1], mFrames[1], is_audio[1]);
405 wxFprintf(mTimeFile,
"work2 = %g, work3 = %g\n", work2, work3);
407 mProgress.emplace(
XO(
"Synchronize MIDI with Audio"),
408 XO(
"Synchronizing MIDI and Audio Tracks"));
411 "Phase %d took %d ms for %g frames, coefficient = %g s/frame\n",
412 i - 1, ms, mFrames[i - 1], (ms * 0.001) / mFrames[i - 1]);
415 "Phase 2 took %d ms for %d cells, coefficient = %g s/cell\n",
416 ms, mCellCount, (ms * 0.001) / mCellCount);
419 "Phase 3 took %d ms for %d iterations on %g frames, "
420 "coefficient = %g s per frame per iteration\n",
421 ms, iterations, wxMax(mFrames[0], mFrames[1]),
422 (ms * 0.001) / (wxMax(mFrames[0], mFrames[1]) * iterations));
425 bool set_feature_progress(
float s)
override {
428 float f = s / frame_period;
429 work = (is_audio[0] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * f;
430 }
else if (phase == 1) {
431 float f = s / frame_period;
432 work = (is_audio[0] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[0] +
433 (is_audio[1] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * f;
435 auto updateResult = mProgress->Update((
int)(work), (
int)(mTotalWork));
438 bool set_matrix_progress(
int cells)
override {
441 (is_audio[0] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[0] +
442 (is_audio[1] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[1];
443 work += mCellCount * MATRIX_WORK_UNIT;
444 auto updateResult = mProgress->Update((
int)(work), (
int)(mTotalWork));
447 bool set_smoothing_progress(
int i)
override {
450 (is_audio[0] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[0] +
451 (is_audio[1] ? AUDIO_WORK_UNIT : MIDI_WORK_UNIT) * mFrames[1] +
452 MATRIX_WORK_UNIT * mFrames[0] * mFrames[1];
453 work += i * wxMax(mFrames[0], mFrames[1]) * SMOOTHING_WORK_UNIT;
454 auto updateResult = mProgress->Update((
int)(work), (
int)(mTotalWork));
460long mixer_process(
void *mixer,
float **buffer,
long n)
463 long frame_count = mix->
Process(std::max(0L, n));
477 auto GetTime = [](
const Track &t) {
478 return t.TypeSwitch<
double>(
480 auto stime = w.GetEndTime();
483 for (ndx = 0; ndx < w.GetNumClips(); ndx++) {
484 const auto c = w.GetClipByIndex(ndx);
485 if (c->GetVisibleSampleCount() == 0)
487 stime =
std::min(stime, c->GetPlayStartTime());
492 return l.GetStartTime();
497 std::vector<Track *> arr;
499 arr.reserve(
tracks.Size());
502 for (
const auto pTrack :
tracks) {
503 auto &track = *pTrack;
504 const auto size = arr.size();
506 for (; ndx <
size; ++ndx) {
507 Track &arrTrack = *arr[ndx];
519 track.GetName().CompareTo(arrTrack.
GetName()) > 0))
524 if (GetTime(track) < GetTime(arrTrack))
527 arr.insert(arr.begin() + ndx, &track);
537 float newValue = slider->
Get();
550 float newValue = slider->
Get();
602 rate.Printf(
wxT(
"%ld"),
lrint(projectRate));
620 S.StartVerticalLay(
true);
624 S.StartHorizontalLay(wxCENTER,
false);
626 cb =
S.AddCombo(
XXO(
"New sample rate (Hz):"),
630 S.EndHorizontalLay();
634 S.AddStandardButtons();
642 if (dlg.ShowModal() != wxID_OK)
646 if (cb->GetValue().ToLong(&lrate) && lrate >= 1 && lrate <= 1000000) {
647 newRate = (int)lrate;
652 XO(
"The entered value is invalid"),
661 auto msg =
XO(
"Resampling track %d").Format(++ndx);
671 wt->Resample(newRate, progress.get());
678 XO(
"Resampled audio track(s)"),
XO(
"Resample Track"), flags);
682 undoManager.StopConsolidating();
685 window.FinishAutoScroll();
705 for (
auto pt : iter) {
707 if (soloSimple || soloNone)
765 gPrefs->
Read(
wxT(
"/GUI/MoveSelectionWithTracks"), &bMoveWith,
false);
780 gPrefs->
Read(
wxT(
"/GUI/MoveSelectionWithTracks"), &bMoveWith,
false);
786#ifdef EXPERIMENTAL_SCOREALIGN
793 int numWaveTracksSelected = 0;
794 int numNoteTracksSelected = 0;
795 int numOtherTracksSelected = 0;
796 double endTime = 0.0;
802 numWaveTracksSelected++;
806 numNoteTracksSelected++;
809 numOtherTracksSelected++;
813 if(numWaveTracksSelected == 0 ||
814 numNoteTracksSelected != 1 ||
815 numOtherTracksSelected != 0){
817 XO(
"Please select at least one audio track and one MIDI track.") );
829 CloseScoreAlignDialog();
831 if (
params.mStatus != wxID_OK)
return;
837 auto holder = nt->Duplicate();
838 auto alignedNoteTrack =
static_cast<NoteTrack*
>(holder.get());
841 if (alignedNoteTrack->GetOffset() < 0) {
843 nt->
Clear(alignedNoteTrack->GetOffset(), 0);
844 }
else if (alignedNoteTrack->GetOffset() > 0) {
845 alignedNoteTrack->Shift(alignedNoteTrack->GetOffset());
847 alignedNoteTrack->MoveTo(0);
850 tracks->GetWaveTrackConstArray(
true );
868 ASAProgress progress;
876#ifndef SKIP_ACTUAL_SCORE_ALIGNMENT
877 result = scorealign((
void *) &mix, &mixer_process,
878 2 , 44100.0 , endTime,
879 &alignedNoteTrack->GetSeq(), &progress,
params);
885 if (result == SA_SUCCESS) {
886 tracks->Replace(nt, holder);
888 XO(
"Alignment completed: MIDI from %.2f to %.2f secs, Audio from %.2f to %.2f secs.")
893 .
PushState(
XO(
"Sync MIDI with Audio"),
XO(
"Sync MIDI with Audio"));
894 }
else if (result == SA_TOOSHORT) {
897"Alignment error: input too short: MIDI from %.2f to %.2f secs, Audio from %.2f to %.2f secs.")
901 }
else if (result == SA_CANCEL) {
941 trackPanel.Refresh(
false);
952 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
965 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
978 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
992 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
1005 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
1018 if (track) track->TypeSwitch( [&](
WaveTrack &wt) {
1030 trackPanel.OnTrackMenu();
1071 XO(
"Can't delete track with active audio"));
1078 trackPanel.UpdateViewIfNoTracks();
1079 trackPanel.Refresh(
false);
1089 if (
tracks.CanMoveUp(focusedTrack)) {
1091 trackPanel.Refresh(
false);
1102 if (
tracks.CanMoveDown(focusedTrack)) {
1104 trackPanel.Refresh(
false);
1115 if (
tracks.CanMoveUp(focusedTrack)) {
1117 trackPanel.Refresh(
false);
1128 if (
tracks.CanMoveDown(focusedTrack)) {
1130 trackPanel.Refresh(
false);
1173 XXO(
"Mix and Render to Ne&w Track"),
1222 {
wxT(
"EndToEnd"),
XXO(
"&Align End to End") },
1223 {
wxT(
"Together"),
XXO(
"Align &Together") },
1237 XXO(
"&Move Selection with Tracks (on/off)"),
1247 Menu(
wxT(
"MoveSelectionAndTracks"),
XO(
"Move Sele&ction and Tracks"), {
1255 #ifdef EXPERIMENTAL_SCOREALIGN
1256 Command(
wxT(
"ScoreAlign"),
XXO(
"Synchronize MIDI with Audio"),
1276 Command(
wxT(
"SyncLock"),
XXO(
"Keep tracks synchronized (Sync &Lock)"),
1295 Command(
wxT(
"TrackPan"),
XXO(
"Change P&an on Focused Track..."),
1298 Command(
wxT(
"TrackPanLeft"),
XXO(
"Pan &Left on Focused Track"),
1301 Command(
wxT(
"TrackPanRight"),
XXO(
"Pan &Right on Focused Track"),
1304 Command(
wxT(
"TrackGain"),
XXO(
"Change Gai&n on Focused Track..."),
1307 Command(
wxT(
"TrackGainInc"),
XXO(
"&Increase Gain on Focused Track"),
1310 Command(
wxT(
"TrackGainDec"),
XXO(
"&Decrease Gain on Focused Track"),
1313 Command(
wxT(
"TrackMenu"),
XXO(
"Op&en Menu on Focused Track..."),
1317 Command(
wxT(
"TrackMute"),
XXO(
"M&ute/Unmute Focused Track"),
1320 Command(
wxT(
"TrackSolo"),
XXO(
"&Solo/Unsolo Focused Track"),
1330 Command(
wxT(
"TrackMoveDown"),
XXO(
"Move Focused Track Do&wn"),
1333 Command(
wxT(
"TrackMoveTop"),
XXO(
"Move Focused Track to T&op"),
1336 Command(
wxT(
"TrackMoveBottom"),
XXO(
"Move Focused Track to &Bottom"),
1344 wxT(
"Optional/Extra/Part2"),
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
AttachedItem sAttachment1
AttachedItem sAttachment2
constexpr CommandFlag AlwaysEnabledFlag
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & StereoRequiredFlag()
const ReservedCommandFlag & EditableTracksSelectedFlag()
const ReservedCommandFlag & AnyTracksSelectedFlag()
const ReservedCommandFlag & TracksExistFlag()
const ReservedCommandFlag & WaveTracksSelectedFlag()
const ReservedCommandFlag & TrackPanelHasFocus()
EffectDistortionSettings params
XXO("&Cut/Copy/Paste Toolbar")
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
TrackListHolder MixAndRender(const TrackIterRange< const WaveTrack > &trackRange, const Mixer::WarpOptions &warpOptions, const wxString &newTrackName, WaveTrackFactory *trackFactory, double rate, sampleFormat format, double startTime, double endTime)
Mixes together all input tracks, applying any envelopes, amplitude gain, panning, and real-time effec...
EnumSetting< SoloBehavior > TracksBehaviorsSolo
audacity::BasicSettings * gPrefs
an object holding per-project preferred sample rate
BoolSetting SyncLockTracks
static Settings & settings()
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Track subclass holding data representing sound (as notes, or samples, or ...)
bool Toggle()
Write the negation of the previous value, and then return the current value.
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
double GetStartTime() const
Get the minimum of Start() values of intervals, or 0 when none.
size_t size() const
How many attachment pointers are in the Site.
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
TemporarySelection temporarySelection
AudacityProject & project
static EffectManager & Get()
const PluginID & GetEffectByIdentifier(const CommandID &strTarget)
Lightweight version of ASlider. In other words it does not have a window permanently associated with ...
void Decrease(float steps)
void Increase(float steps)
float Get(bool convert=true)
A LabelTrack is a Track that holds labels (LabelStruct).
Functions for doing the mixdown of the tracks.
size_t Process(size_t maxSamples)
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
A Track that is used for Midi notes. (Somewhat old code).
void Clear(double t0, double t1) override
AudioTrack subclass that can also be audibly replayed by the program.
const PluginDescriptor * GetPlugin(const PluginID &ID) const
static PluginManager & Get()
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=mainStatusBarField)
static ProjectWindow & Get(AudacityProject &project)
ScoreAlignDialog is \TODO.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
static bool IsSelectedOrSyncLockSelected(const Track *pTrack)
Abstract base class for an object holding data associated with points on a time axis.
void EnsureVisible(bool modifyState=false)
const wxString & GetName() const
Name is always the same for all channels of a group.
static TrackList & Get(AudacityProject &project)
auto Selected() -> TrackIterRange< TrackType >
static TrackPanel & Get(AudacityProject &project)
void RefreshTrack(Track *trk, bool refreshbacking=true)
Holds a msgid for the translation catalog; may also bind format arguments.
static UndoManager & Get(AudacityProject &project)
NotifyingSelectedRegion selectedRegion
static ViewInfo & Get(AudacityProject &project)
static LWSlider * PanSlider(CellularPanel &panel, const WaveTrack &wt)
static LWSlider * GainSlider(CellularPanel &panel, const WaveTrack &wt)
static WaveTrackFactory & Get(AudacityProject &project)
A Track that contains audio waveform data.
void SetPan(float newPan)
double GetEndTime() const override
Implement WideSampleSequence.
void SetGain(float newGain)
size_t NChannels() const override
May report more than one only when this is a leader track.
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
AUDACITY_DLL_API bool DoEffect(const PluginID &ID, const CommandContext &context, unsigned flags)
'Repeat Last Effect'.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
PROJECT_RATE_API sampleFormat SampleFormatChoice()
std::unique_ptr< detail::IndirectItem< Item > > Indirect(const std::shared_ptr< Item > &ptr)
A convenience function.
std::unique_ptr< BaseItem > BaseItemPtr
std::shared_ptr< BaseItem > BaseItemSharedPtr
void DoTrackMute(AudacityProject &project, Track *t, bool exclusive)
void DoMoveTrack(AudacityProject &project, Track *target, MoveChoice choice)
Move a track up, down, to top or to bottom.
void DoTrackSolo(AudacityProject &project, Track *t, bool exclusive)
void DoRemoveTrack(AudacityProject &project, Track *toRemove)
void DoRemoveTracks(AudacityProject &project)
CommandManager::Options Options
Options && CheckTest(const CheckFn &fn) &&
Options && LongName(const TranslatableString &value) &&
Immutable structure is an argument to Mixer's constructor.