1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-06 23:02:42 +02:00

Some preliminaries for playable MIDI tracks

This commit is contained in:
Paul Licameli 2017-03-29 14:01:00 -04:00
commit 8c796e2603
21 changed files with 374 additions and 478 deletions

View File

@ -3790,7 +3790,7 @@ void AudioIO::OutputEvent()
data1 = mNextEvent->get_pitch();
if (mNextIsNoteOn) {
data2 = mNextEvent->get_loud(); // get velocity
int offset = mNextEventTrack->GetGain();
int offset = mNextEventTrack->GetVelocity();
data2 += offset; // offset comes from per-track slider
// clip velocity to insure a legal note-on value
data2 = (data2 < 0 ? 1 : (data2 > 127 ? 127 : data2));

View File

@ -4608,11 +4608,13 @@ bool AudacityProject::HandlePasteNothingSelected()
pNewTrack = mTrackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate());
}
break;
#ifdef USE_MIDI
case Track::Note:
pNewTrack = mTrackFactory->NewNoteTrack();
break;
#endif // USE_MIDI
case Track::Note:
pNewTrack = mTrackFactory->NewNoteTrack();
break;
#endif // USE_MIDI
case Track::Label:
pNewTrack = mTrackFactory->NewLabelTrack();
break;
@ -6150,12 +6152,7 @@ void AudacityProject::HandleAlign(int index, bool moveSel)
while (t) {
// We only want Wave and Note tracks here.
#if defined(USE_MIDI)
if (t->GetSelected() && ((t->GetKind() == Track::Wave) ||
(t->GetKind() == Track::Note)))
#else
if (t->GetSelected() && (t->GetKind() == Track::Wave))
#endif
if (t->GetSelected() && dynamic_cast<const AudioTrack*>(t))
{
offset = t->GetOffset();
if (t->GetLinked()) { // Left channel of stereo track.
@ -6236,12 +6233,7 @@ void AudacityProject::HandleAlign(int index, bool moveSel)
while (t) {
// This shifts different tracks in different ways, so no sync-lock move.
// Only align Wave and Note tracks end to end.
#if defined(USE_MIDI)
if (t->GetSelected() && ((t->GetKind() == Track::Wave) ||
(t->GetKind() == Track::Note)))
#else
if (t->GetSelected() && (t->GetKind() == Track::Wave))
#endif
if (t->GetSelected() && dynamic_cast<const AudioTrack*>(t))
{
t->SetOffset(newPos); // Move the track
@ -6897,8 +6889,9 @@ void AudacityProject::OnRemoveTracks()
while (t) {
if (t->GetSelected()) {
if (mMixerBoard && (t->GetKind() == Track::Wave))
mMixerBoard->RemoveTrackCluster((WaveTrack*)t);
auto playable = dynamic_cast<PlayableTrack*>(t);
if (mMixerBoard && playable)
mMixerBoard->RemoveTrackCluster(playable);
if (!f)
f = l; // Capture the track preceeding the first removed track
t = iter.RemoveCurrent();
@ -7095,8 +7088,9 @@ void AudacityProject::OnMuteAllTracks()
while (t)
{
if (t->GetKind() == Track::Wave)
t->SetMute(true);
auto pt = dynamic_cast<PlayableTrack *>(t);
if (pt)
pt->SetMute(true);
t = iter.Next();
}
@ -7114,7 +7108,9 @@ void AudacityProject::OnUnMuteAllTracks()
while (t)
{
t->SetMute(false);
auto pt = dynamic_cast<PlayableTrack *>(t);
if (pt)
pt->SetMute(false);
t = iter.Next();
}

View File

@ -23,9 +23,9 @@
#include "AColor.h"
#include "AudioIO.h"
#ifdef EXPERIMENTAL_MIDI_OUT
#include "NoteTrack.h"
#endif
#include "NoteTrack.h"
#include "Project.h"
#include "TrackPanel.h" // for EVT_TRACK_PANEL_TIMER
#include "UndoManager.h"
@ -155,30 +155,17 @@ END_EVENT_TABLE()
MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
MixerBoard* grandParent, AudacityProject* project,
WaveTrack* pLeftTrack, WaveTrack* pRightTrack /*= NULL*/,
PlayableTrack* pTrack,
const wxPoint& pos /*= wxDefaultPosition*/,
const wxSize& size /*= wxDefaultSize*/)
: wxPanelWrapper(parent, -1, pos, size)
, mTrack{ pTrack }
{
mMixerBoard = grandParent;
mProject = project;
#ifdef EXPERIMENTAL_MIDI_OUT
if (pLeftTrack->GetKind() == Track::Note) {
mLeftTrack = NULL;
mNoteTrack = (NoteTrack*) pLeftTrack;
mTrack = pLeftTrack;
} else {
wxASSERT(pLeftTrack->GetKind() == Track::Wave);
mTrack = mLeftTrack = pLeftTrack;
mNoteTrack = NULL;
}
#else
wxASSERT(pLeftTrack->GetKind() == Track::Wave);
mLeftTrack = pLeftTrack;
#endif
mRightTrack = pRightTrack;
wxASSERT( pTrack );
SetName(mLeftTrack->GetName());
SetName(mTrack->GetName());
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
@ -191,13 +178,8 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
wxPoint ctrlPos(kDoubleInset, kDoubleInset);
wxSize ctrlSize(size.GetWidth() - kQuadrupleInset, TRACK_NAME_HEIGHT);
mStaticText_TrackName =
#ifdef EXPERIMENTAL_MIDI_OUT
safenew wxStaticText(this, -1, mTrack->GetName(), ctrlPos, ctrlSize,
wxALIGN_CENTRE | wxST_NO_AUTORESIZE | wxSUNKEN_BORDER);
#else
safenew wxStaticText(this, -1, mLeftTrack->GetName(), ctrlPos, ctrlSize,
wxALIGN_CENTRE | 0x0001 | wxBORDER_SUNKEN);
#endif
//v Useful when different tracks are different colors, but not now.
// mStaticText_TrackName->SetBackgroundColour(this->GetTrackColor());
@ -208,8 +190,8 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
const int nGainSliderHeight =
size.GetHeight() - ctrlPos.y - kQuadrupleInset;
ctrlSize.Set(kLeftSideStackWidth - kQuadrupleInset, nGainSliderHeight);
#ifdef EXPERIMENTAL_MIDI_OUT
if (mNoteTrack) {
if (GetNote()) {
mSlider_Gain =
safenew MixerTrackSlider(
this, ID_SLIDER_GAIN,
@ -217,15 +199,16 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
_("Velocity"),
ctrlPos, ctrlSize, VEL_SLIDER, true,
true, 0.0, wxVERTICAL);
} else
#endif
mSlider_Gain =
safenew MixerTrackSlider(
this, ID_SLIDER_GAIN,
/* i18n-hint: title of the Gain slider, used to adjust the volume */
_("Gain"),
ctrlPos, ctrlSize, DB_SLIDER, true,
true, 0.0, wxVERTICAL);
}
else
mSlider_Gain =
safenew MixerTrackSlider(
this, ID_SLIDER_GAIN,
/* i18n-hint: title of the Gain slider, used to adjust the volume */
_("Gain"),
ctrlPos, ctrlSize, DB_SLIDER, true,
true, 0.0, wxVERTICAL);
mSlider_Gain->SetName(_("Gain"));
this->UpdateGain();
@ -236,11 +219,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
// musical instrument image
ctrlPos.x += kLeftSideStackWidth + kInset; // + kInset to center it in right side stack
ctrlSize.Set(MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH, MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH);
#ifdef EXPERIMENTAL_MIDI_OUT
wxBitmap* bitmap = mMixerBoard->GetMusicalInstrumentBitmap(mTrack->GetName());
#else
wxBitmap* bitmap = mMixerBoard->GetMusicalInstrumentBitmap(mLeftTrack);
#endif
wxBitmap* bitmap = mMixerBoard->GetMusicalInstrumentBitmap(mTrack);
wxASSERT(bitmap);
mBitmapButton_MusicalInstrument =
safenew wxBitmapButton(this, ID_BITMAPBUTTON_MUSICAL_INSTRUMENT, *bitmap,
@ -306,34 +285,24 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
(PAN_HEIGHT + kDoubleInset) -
(MUTE_SOLO_HEIGHT + (bSoloNone ? 0 : MUTE_SOLO_HEIGHT) + kDoubleInset);
ctrlSize.Set(kRightSideStackWidth, nMeterHeight);
#ifdef EXPERIMENTAL_MIDI_OUT
if (mLeftTrack) {
#endif
mMeter =
safenew Meter(GetActiveProject(), // AudacityProject* project,
this, -1, // wxWindow* parent, wxWindowID id,
false, // bool isInput
ctrlPos, ctrlSize, // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
Meter::MixerTrackCluster); // Style style = HorizontalStereo,
mMeter->SetName(_("Signal Level Meter"));
#ifdef EXPERIMENTAL_MIDI_OUT
} else {
mMeter = NULL;
mMeter = NULL;
if (GetWave()) {
mMeter =
safenew Meter(GetActiveProject(), // AudacityProject* project,
this, -1, // wxWindow* parent, wxWindowID id,
false, // bool isInput
ctrlPos, ctrlSize, // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
Meter::MixerTrackCluster); // Style style = HorizontalStereo,
mMeter->SetName(_("Signal Level Meter"));
}
#endif
#if wxUSE_TOOLTIPS
#ifdef EXPERIMENTAL_MIDI_OUT
mStaticText_TrackName->SetToolTip(mTrack->GetName());
#else
mStaticText_TrackName->SetToolTip(mLeftTrack->GetName());
#endif
mToggleButton_Mute->SetToolTip(_("Mute"));
mToggleButton_Solo->SetToolTip(_("Solo"));
#ifdef EXPERIMENTAL_MIDI_OUT
if (mLeftTrack)
#endif
mMeter->SetToolTip(_("Signal Level Meter"));
if (GetWave())
mMeter->SetToolTip(_("Signal Level Meter"));
#endif // wxUSE_TOOLTIPS
#ifdef __WXMAC__
@ -344,9 +313,29 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
#endif
}
WaveTrack *MixerTrackCluster::GetWave() const
{
return dynamic_cast< WaveTrack * >( mTrack );
}
WaveTrack *MixerTrackCluster::GetRight() const
{
auto left = GetWave();
if (left)
return static_cast<WaveTrack*>(left);
else
return nullptr;
}
NoteTrack *MixerTrackCluster::GetNote() const
{
return dynamic_cast< NoteTrack * >( mTrack );
}
void MixerTrackCluster::UpdatePrefs()
{
mMeter->UpdatePrefs(); // in case meter range has changed
if (mMeter)
mMeter->UpdatePrefs(); // in case meter range has changed
HandleResize(); // in case prefs "/GUI/Solo" changed
}
@ -382,30 +371,24 @@ void MixerTrackCluster::HandleResize() // For wxSizeEvents, update gain slider a
TRACK_NAME_HEIGHT + kDoubleInset +
nRequiredHeightAboveMeter;
const int nMeterHeight = nGainSliderHeight - nRequiredHeightAboveMeter;
#ifdef EXPERIMENTAL_MIDI_OUT
if (mMeter)
#endif
mMeter->SetSize(-1, nMeterY, -1, nMeterHeight);
mMeter->SetSize(-1, nMeterY, -1, nMeterHeight);
}
void MixerTrackCluster::HandleSliderGain(const bool bWantPushState /*= false*/)
{
float fValue = mSlider_Gain->Get();
if (mLeftTrack)
mLeftTrack->SetGain(fValue);
if (GetWave())
GetWave()->SetGain(fValue);
#ifdef EXPERIMENTAL_MIDI_OUT
else
mNoteTrack->SetGain(fValue);
GetNote()->SetVelocity(fValue);
#endif
if (mRightTrack)
mRightTrack->SetGain(fValue);
if (GetRight())
GetRight()->SetGain(fValue);
// Update the TrackPanel correspondingly.
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->RefreshTPTrack(mTrack);
#else
mProject->RefreshTPTrack(mLeftTrack);
#endif
if (bWantPushState)
mProject->TP_PushState(_("Moved gain slider"), _("Gain"), UndoPush::CONSOLIDATE );
}
@ -413,17 +396,13 @@ void MixerTrackCluster::HandleSliderGain(const bool bWantPushState /*= false*/)
void MixerTrackCluster::HandleSliderPan(const bool bWantPushState /*= false*/)
{
float fValue = mSlider_Pan->Get();
if (mLeftTrack) // test in case track is a NoteTrack
mLeftTrack->SetPan(fValue);
if (mRightTrack)
mRightTrack->SetPan(fValue);
if (GetWave()) // test in case track is a NoteTrack
GetWave()->SetPan(fValue);
if (GetRight())
GetRight()->SetPan(fValue);
// Update the TrackPanel correspondingly.
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->RefreshTPTrack(mTrack);
#else
mProject->RefreshTPTrack(mLeftTrack);
#endif
if (bWantPushState)
mProject->TP_PushState(_("Moved pan slider"), _("Pan"), UndoPush::CONSOLIDATE );
@ -431,10 +410,8 @@ void MixerTrackCluster::HandleSliderPan(const bool bWantPushState /*= false*/)
void MixerTrackCluster::ResetMeter(const bool bResetClipping)
{
#ifdef EXPERIMENTAL_MIDI_OUT
if (mMeter)
#endif
mMeter->Reset(mLeftTrack->GetRate(), bResetClipping);
mMeter->Reset(GetWave()->GetRate(), bResetClipping);
}
@ -450,33 +427,20 @@ void MixerTrackCluster::UpdateForStateChange()
void MixerTrackCluster::UpdateName()
{
#ifdef EXPERIMENTAL_MIDI_OUT
const wxString newName = mTrack->GetName();
#else
const wxString newName = mLeftTrack->GetName();
#endif
SetName(newName);
mStaticText_TrackName->SetLabel(newName);
#if wxUSE_TOOLTIPS
mStaticText_TrackName->SetToolTip(newName);
#endif
mBitmapButton_MusicalInstrument->SetBitmapLabel(
#ifdef EXPERIMENTAL_MIDI_OUT
*(mMixerBoard->GetMusicalInstrumentBitmap(newName)));
#else
*(mMixerBoard->GetMusicalInstrumentBitmap(mLeftTrack)));
#endif
*(mMixerBoard->GetMusicalInstrumentBitmap(mTrack)));
}
void MixerTrackCluster::UpdateMute()
{
#ifdef EXPERIMENTAL_MIDI_OUT
mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
if (mTrack->GetMute())
#else
mToggleButton_Mute->SetAlternateIdx(mLeftTrack->GetSolo() ? 1 : 0);
if (mLeftTrack->GetMute())
#endif
mToggleButton_Mute->PushDown();
else
mToggleButton_Mute->PopUp();
@ -484,11 +448,7 @@ void MixerTrackCluster::UpdateMute()
void MixerTrackCluster::UpdateSolo()
{
#ifdef EXPERIMENTAL_MIDI_OUT
bool bIsSolo = mTrack->GetSolo();
#else
bool bIsSolo = mLeftTrack->GetSolo();
#endif
if (bIsSolo)
mToggleButton_Solo->PushDown();
else
@ -499,55 +459,36 @@ void MixerTrackCluster::UpdateSolo()
void MixerTrackCluster::UpdatePan()
{
#ifdef EXPERIMENTAL_MIDI_OUT
if (mNoteTrack) {
if (!GetWave()) {
mSlider_Pan->Hide();
return;
}
#endif
mSlider_Pan->Set(mLeftTrack->GetPan());
mSlider_Pan->Set(GetWave()->GetPan());
}
void MixerTrackCluster::UpdateGain()
{
#ifdef EXPERIMENTAL_MIDI_OUT
if (mNoteTrack) {
if (!GetWave()) {
mSlider_Gain->SetStyle(VEL_SLIDER);
mSlider_Gain->Set(mNoteTrack->GetGain());
mSlider_Gain->Set(GetNote()->GetVelocity());
return;
}
mSlider_Gain->SetStyle(DB_SLIDER);
#endif
mSlider_Gain->Set(mLeftTrack->GetGain());
mSlider_Gain->Set(GetWave()->GetGain());
}
void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
{
#ifdef EXPERIMENTAL_MIDI_OUT
// NoteTracks do not (currently) register on meters. It would probably be
// a good idea to display 16 channel "active" lights rather than a meter
if (!mLeftTrack)
if (!GetWave())
return;
#else
wxASSERT(mLeftTrack && (mLeftTrack->GetKind() == Track::Wave));
#endif
//vvv Vaughan, 2010-11-27:
// NOTE TO ROGER DANNENBERG:
// I undid the mTrack hack in this conditional, as the rest of the method still assumed it's a wavetrack
// so dereferencing mLeftTrack would have gotten a NULL pointer fault.
// I really think MixerTrackCluster should be factored for NoteTracks.
// REPLY: I think bSuccess prevents dereferencing mLeftTrack, but I will
// check. We should talk about whether it's better to factor
// MixerTrackCluster or more fully hide track types from MixerTrackCluster.
// For now, out change plan produced the following:
// Vaughan, 2011=10-15: There's no bSuccess here, so I don't know what you mean.
// But this change is consistent with the others for EXPERIMENTAL_MIDI_OUT, so I accept it.
if ((t0 < 0.0) || (t1 < 0.0) || (t0 >= t1) || // bad time value or nothing to show
#ifdef EXPERIMENTAL_MIDI_OUT
((mMixerBoard->HasSolo() || mTrack->GetMute()) && !mTrack->GetSolo())
#else
((mMixerBoard->HasSolo() || mLeftTrack->GetMute()) && !mLeftTrack->GetSolo())
#endif
)
{
//v Vaughan, 2011-02-25: Moved the update back to TrackPanel::OnTimer() as it helps with
@ -577,7 +518,7 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
//Floats rmsRight{kFramesPerBuffer};
//
//#ifdef EXPERIMENTAL_MIDI_OUT
// bool bSuccess = (mLeftTrack != NULL);
// bool bSuccess = (GetWave() != nullptr);
//#else
// bool bSuccess = true;
//#endif
@ -589,8 +530,8 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
//while (bSuccess && (i < kFramesPerBuffer))
//{
// bSuccess &=
// mLeftTrack->GetMinMax(&min, &(maxLeft[i]), dFrameT0, dFrameT1) &&
// mLeftTrack->GetRMS(&(rmsLeft[i]), dFrameT0, dFrameT1);
// mTrack->GetMinMax(&min, &(maxLeft[i]), dFrameT0, dFrameT1) &&
// mTrack->GetRMS(&(rmsLeft[i]), dFrameT0, dFrameT1);
// if (bSuccess && mRightTrack)
// bSuccess &=
// mRightTrack->GetMinMax(&min, &(maxRight[i]), dFrameT0, dFrameT1) &&
@ -613,7 +554,7 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
//{
// for (i = 0; i < kFramesPerBuffer; i++)
// {
// float gain = mLeftTrack->GetChannelGain(0);
// float gain = mTrack->GetChannelGain(0);
// maxLeft[i] *= gain;
// rmsLeft[i] *= gain;
// if (mRightTrack)
@ -621,17 +562,18 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
// maxRight[i] *= gain;
// rmsRight[i] *= gain;
// }
// mMeter->UpdateDisplay(
// if ( mMeter ) mMeter->UpdateDisplay(
// 2, // If mono, show left track values in both meters, as in MeterToolBar, rather than kNumChannels.
// kFramesPerBuffer,
// maxLeft, rmsLeft,
// maxRight, rmsRight,
// mLeftTrack->TimeToLongSamples(t1 - t0));
// mTrack->TimeToLongSamples(t1 - t0));
//}
//
auto startSample = (sampleCount)((mLeftTrack->GetRate() * t0) + 0.5);
auto scnFrames = (sampleCount)((mLeftTrack->GetRate() * (t1 - t0)) + 0.5);
const auto pTrack = GetWave();
auto startSample = (sampleCount)((pTrack->GetRate() * t0) + 0.5);
auto scnFrames = (sampleCount)((pTrack->GetRate() * (t1 - t0)) + 0.5);
// Expect that the difference of t1 and t0 is the part of a track played
// in about 1/20 second (ticks of TrackPanel timer), so this won't overflow
@ -640,7 +582,7 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
Floats tempFloatsArray{ nFrames };
decltype(tempFloatsArray) meterFloatsArray;
// Don't throw on read error in this drawing update routine
bool bSuccess = mLeftTrack->Get((samplePtr)tempFloatsArray.get(),
bool bSuccess = pTrack->Get((samplePtr)tempFloatsArray.get(),
floatSample, startSample, nFrames, fillZero, false);
if (bSuccess)
{
@ -653,9 +595,9 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
for (int index = 0; index < nFrames; index++)
meterFloatsArray[2 * index] = tempFloatsArray[index];
if (mRightTrack)
if (GetRight())
// Again, don't throw
bSuccess = mRightTrack->Get((samplePtr)tempFloatsArray.get(),
bSuccess = GetRight()->Get((samplePtr)tempFloatsArray.get(),
floatSample, startSample, nFrames, fillZero, false);
if (bSuccess)
@ -669,14 +611,14 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
if (bSuccess)
{
//vvv Need to apply envelope, too? See Mixer::MixSameRate.
float gain = mLeftTrack->GetChannelGain(0);
float gain = pTrack->GetChannelGain(0);
if (gain < 1.0)
for (int index = 0; index < nFrames; index++)
meterFloatsArray[2 * index] *= gain;
if (mRightTrack)
gain = mRightTrack->GetChannelGain(1);
if (GetRight())
gain = GetRight()->GetChannelGain(1);
else
gain = mLeftTrack->GetChannelGain(1);
gain = pTrack->GetChannelGain(1);
if (gain < 1.0)
for (int index = 0; index < nFrames; index++)
meterFloatsArray[(2 * index) + 1] *= gain;
@ -705,13 +647,7 @@ wxColour MixerTrackCluster::GetTrackColor()
void MixerTrackCluster::HandleSelect(bool bShiftDown, bool bControlDown)
{
#ifdef EXPERIMENTAL_MIDI_OUT
Track *pTrack = mTrack;
#else
Track *pTrack = mLeftTrack;
#endif
mProject->GetTrackPanel()->HandleListSelection(pTrack, bShiftDown, bControlDown);
mProject->GetTrackPanel()->HandleListSelection(mTrack, bShiftDown, bControlDown);
}
void MixerTrackCluster::OnMouseEvent(wxMouseEvent& event)
@ -735,11 +671,7 @@ void MixerTrackCluster::OnPaint(wxPaintEvent & WXUNUSED(event))
wxSize clusterSize = this->GetSize();
wxRect bev(0, 0, clusterSize.GetWidth() - 1, clusterSize.GetHeight() - 1);
#ifdef EXPERIMENTAL_MIDI_OUT
auto selected = mTrack->GetSelected();
#else
auto selected = mLeftTrack->GetSelected();
#endif
for (unsigned int i = 0; i < 4; i++) // 4 gives a big bevel, but there were complaints about visibility otherwise.
{
@ -782,13 +714,8 @@ void MixerTrackCluster::OnSlider_Pan(wxCommandEvent& WXUNUSED(event))
void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
{
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->HandleTrackMute(mTrack, mToggleButton_Mute->WasShiftDown());
mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
#else
mProject->HandleTrackMute(mLeftTrack, mToggleButton_Mute->WasShiftDown());
mToggleButton_Mute->SetAlternateIdx(mLeftTrack->GetSolo() ? 1 : 0);
#endif
// Update the TrackPanel correspondingly.
if (mProject->IsSoloSimple())
@ -799,22 +726,13 @@ void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
}
else
// Update only the changed track.
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->RefreshTPTrack(mTrack);
#else
mProject->RefreshTPTrack(mLeftTrack);
#endif
}
void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
{
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->HandleTrackSolo(mTrack, mToggleButton_Solo->WasShiftDown());
bool bIsSolo = mTrack->GetSolo();
#else
mProject->HandleTrackSolo(mLeftTrack, mToggleButton_Solo->WasShiftDown());
bool bIsSolo = mLeftTrack->GetSolo();
#endif
mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
// Update the TrackPanel correspondingly.
@ -827,11 +745,7 @@ void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
}
else
// Update only the changed track.
#ifdef EXPERIMENTAL_MIDI_OUT
mProject->RefreshTPTrack(mTrack);
#else
mProject->RefreshTPTrack(mLeftTrack);
#endif
}
@ -1006,22 +920,18 @@ void MixerBoard::UpdateTrackClusters()
this->CreateMuteSoloImages();
const int nClusterHeight = mScrolledWindow->GetClientSize().GetHeight() - kDoubleInset;
const size_t nClusterCount = mMixerTrackClusters.GetCount();
size_t nClusterCount = mMixerTrackClusters.GetCount();
unsigned int nClusterIndex = 0;
TrackListIterator iterTracks(mTracks);
MixerTrackCluster* pMixerTrackCluster = NULL;
Track* pLeftTrack;
Track* pTrack;
Track* pRightTrack;
pLeftTrack = iterTracks.First();
while (pLeftTrack) {
pRightTrack = pLeftTrack->GetLinked() ? iterTracks.Next() : NULL;
pTrack = iterTracks.First();
while (pTrack) {
pRightTrack = pTrack->GetLinked() ? iterTracks.Next() : NULL;
if (pLeftTrack->GetKind() == Track::Wave
#ifdef EXPERIMENTAL_MIDI_OUT
|| pLeftTrack->GetKind() == Track::Note
#endif
)
if (auto pPlayableTrack = dynamic_cast<PlayableTrack*>(pTrack))
{
if (nClusterIndex < nClusterCount)
{
@ -1029,20 +939,8 @@ void MixerBoard::UpdateTrackClusters()
// Track clusters are maintained in the same order as the WaveTracks.
// Track pointers can change for the "same" track for different states
// on the undo stack, so update the pointers and display name.
#ifdef EXPERIMENTAL_MIDI_OUT
if (pLeftTrack->GetKind() == Track::Note) {
mMixerTrackClusters[nClusterIndex]->mNoteTrack = (NoteTrack*)pLeftTrack;
mMixerTrackClusters[nClusterIndex]->mLeftTrack = NULL;
} else {
mMixerTrackClusters[nClusterIndex]->mNoteTrack = NULL;
mMixerTrackClusters[nClusterIndex]->mLeftTrack = (WaveTrack*)pLeftTrack;
}
#else
mMixerTrackClusters[nClusterIndex]->mLeftTrack = (WaveTrack*)pLeftTrack;
#endif
mMixerTrackClusters[nClusterIndex]->mTrack = pPlayableTrack;
// Assume linked track is wave or null
mMixerTrackClusters[nClusterIndex]->mRightTrack =
static_cast<WaveTrack*>(pRightTrack);
mMixerTrackClusters[nClusterIndex]->UpdateForStateChange();
}
else
@ -1057,16 +955,14 @@ void MixerBoard::UpdateTrackClusters()
wxSize clusterSize(kMixerTrackClusterWidth, nClusterHeight);
pMixerTrackCluster =
safenew MixerTrackCluster(mScrolledWindow, this, mProject,
static_cast<WaveTrack*>(pLeftTrack),
// Assume linked track is wave or null
static_cast<WaveTrack*>(pRightTrack),
pPlayableTrack,
clusterPos, clusterSize);
if (pMixerTrackCluster)
mMixerTrackClusters.Add(pMixerTrackCluster);
}
nClusterIndex++;
}
pLeftTrack = iterTracks.Next();
pTrack = iterTracks.Next();
}
if (pMixerTrackCluster)
@ -1075,19 +971,14 @@ void MixerBoard::UpdateTrackClusters()
this->UpdateWidth();
this->ResizeTrackClusters();
}
else if (nClusterIndex < nClusterCount)
else while (nClusterIndex < nClusterCount)
{
// We've got too many clusters.
// This can happen only on things like Undo New Audio Track or Undo Import
// that don't call RemoveTrackCluster explicitly.
// We've already updated the track pointers for the clusters to the left, so just remove all the rest.
// Keep nClusterIndex constant and successively DELETE from left to right.
for (unsigned int nCounter = nClusterIndex; nCounter < nClusterCount; nCounter++)
#ifdef EXPERIMENTAL_MIDI_OUT
this->RemoveTrackCluster(mMixerTrackClusters[nClusterIndex]->mTrack);
#else
this->RemoveTrackCluster(mMixerTrackClusters[nClusterIndex]->mLeftTrack);
#endif
// Successively DELETE from right to left.
RemoveTrackCluster(--nClusterCount);
}
}
@ -1100,13 +991,8 @@ int MixerBoard::GetTrackClustersWidth()
kDoubleInset; // plus final right margin
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::MoveTrackCluster(const Track* pTrack,
void MixerBoard::MoveTrackCluster(const PlayableTrack* pTrack,
bool bUp) // Up in TrackPanel is left in MixerBoard.
#else
void MixerBoard::MoveTrackCluster(const WaveTrack* pTrack,
bool bUp) // Up in TrackPanel is left in MixerBoard.
#endif
{
MixerTrackCluster* pMixerTrackCluster;
int nIndex = FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
@ -1140,18 +1026,21 @@ void MixerBoard::MoveTrackCluster(const WaveTrack* pTrack,
}
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::RemoveTrackCluster(const Track* pTrack)
#else
void MixerBoard::RemoveTrackCluster(const WaveTrack* pTrack)
#endif
void MixerBoard::RemoveTrackCluster(const PlayableTrack* pTrack)
{
// Find and destroy.
MixerTrackCluster* pMixerTrackCluster;
int nIndex = this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
if (pMixerTrackCluster == NULL)
return; // Couldn't find it.
RemoveTrackCluster(nIndex);
}
void MixerBoard::RemoveTrackCluster(size_t nIndex)
{
auto pMixerTrackCluster = mMixerTrackClusters[nIndex];
mMixerTrackClusters.RemoveAt(nIndex);
pMixerTrackCluster->Destroy(); // DELETE is unsafe on wxWindow.
@ -1170,32 +1059,17 @@ void MixerBoard::RemoveTrackCluster(const WaveTrack* pTrack)
}
this->UpdateWidth();
#ifdef EXPERIMENTAL_MIDI_OUT
// Sanity check: if there is still a MixerTrackCluster with pTrack, then
// we deleted the first but should have deleted the last:
FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
wxASSERT(pMixerTrackCluster == NULL);
#endif
}
#ifdef EXPERIMENTAL_MIDI_OUT
wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(const wxString & name)
#else
wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(const WaveTrack* pLeftTrack)
#endif
wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(const Track* pTrack)
{
if (mMusicalInstruments.empty())
return NULL;
// random choice: return mMusicalInstruments[(int)pLeftTrack % mMusicalInstruments.GetCount()].mBitmap;
// random choice: return mMusicalInstruments[(int)pTrack % mMusicalInstruments.GetCount()].mBitmap;
#ifdef EXPERIMENTAL_MIDI_OUT
const wxString strTrackName(wxString{ name }.MakeLower());
#else
const wxString strTrackName(pLeftTrack->GetName().MakeLower());
#endif
const wxString strTrackName(pTrack->GetName().MakeLower());
size_t nBestItemIndex = 0;
unsigned int nBestScore = 0;
unsigned int nInstrIndex = 0;
@ -1236,17 +1110,15 @@ bool MixerBoard::HasSolo()
{
TrackListIterator iterTracks(mTracks);
Track* pTrack;
for (pTrack = iterTracks.First(); pTrack; pTrack = iterTracks.Next())
if (pTrack->GetSolo())
for (pTrack = iterTracks.First(); pTrack; pTrack = iterTracks.Next()) {
auto pPlayable = dynamic_cast<PlayableTrack *>( pTrack );
if (pPlayable && pPlayable->GetSolo())
return true;
}
return false;
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::RefreshTrackCluster(const Track* pTrack, bool bEraseBackground /*= true*/)
#else
void MixerBoard::RefreshTrackCluster(const WaveTrack* pTrack, bool bEraseBackground )
#endif
void MixerBoard::RefreshTrackCluster(const PlayableTrack* pTrack, bool bEraseBackground /*= true*/)
{
MixerTrackCluster* pMixerTrackCluster;
this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
@ -1277,11 +1149,7 @@ void MixerBoard::ResetMeters(const bool bResetClipping)
mMixerTrackClusters[i]->ResetMeter(bResetClipping);
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::UpdateName(const Track* pTrack)
#else
void MixerBoard::UpdateName(const WaveTrack* pTrack)
#endif
void MixerBoard::UpdateName(const PlayableTrack* pTrack)
{
MixerTrackCluster* pMixerTrackCluster;
this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
@ -1289,11 +1157,7 @@ void MixerBoard::UpdateName(const WaveTrack* pTrack)
pMixerTrackCluster->UpdateName();
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::UpdateMute(const Track* pTrack /*= NULL*/) // NULL means update for all tracks.
#else
void MixerBoard::UpdateMute(const WaveTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
#endif
void MixerBoard::UpdateMute(const PlayableTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
{
if (pTrack == NULL)
{
@ -1309,11 +1173,7 @@ void MixerBoard::UpdateMute(const WaveTrack* pTrack /*= NULL*/) // NULL means up
}
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::UpdateSolo(const Track* pTrack /*= NULL*/) // NULL means update for all tracks.
#else
void MixerBoard::UpdateSolo(const WaveTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
#endif
void MixerBoard::UpdateSolo(const PlayableTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
{
if (pTrack == NULL)
{
@ -1329,11 +1189,7 @@ void MixerBoard::UpdateSolo(const WaveTrack* pTrack /*= NULL*/) // NULL means up
}
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::UpdatePan(const Track* pTrack)
#else
void MixerBoard::UpdatePan(const WaveTrack* pTrack)
#endif
void MixerBoard::UpdatePan(const PlayableTrack* pTrack)
{
MixerTrackCluster* pMixerTrackCluster;
FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
@ -1341,11 +1197,7 @@ void MixerBoard::UpdatePan(const WaveTrack* pTrack)
pMixerTrackCluster->UpdatePan();
}
#ifdef EXPERIMENTAL_MIDI_OUT
void MixerBoard::UpdateGain(const Track* pTrack)
#else
void MixerBoard::UpdateGain(const WaveTrack* pTrack)
#endif
void MixerBoard::UpdateGain(const PlayableTrack* pTrack)
{
MixerTrackCluster* pMixerTrackCluster;
FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
@ -1471,22 +1323,13 @@ void MixerBoard::CreateMuteSoloImages()
mImageSoloDisabled = std::make_unique<wxImage>(mMuteSoloWidth, MUTE_SOLO_HEIGHT); // Leave empty because unused.
}
#ifdef EXPERIMENTAL_MIDI_OUT
int MixerBoard::FindMixerTrackCluster(const Track* pTrack,
MixerTrackCluster** hMixerTrackCluster) const
#else
int MixerBoard::FindMixerTrackCluster(const WaveTrack* pLeftTrack,
int MixerBoard::FindMixerTrackCluster(const PlayableTrack* pTrack,
MixerTrackCluster** hMixerTrackCluster) const
#endif
{
*hMixerTrackCluster = NULL;
for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
{
#ifdef EXPERIMENTAL_MIDI_OUT
if (mMixerTrackClusters[i]->mTrack == pTrack)
#else
if (mMixerTrackClusters[i]->mLeftTrack == pLeftTrack)
#endif
{
*hMixerTrackCluster = mMixerTrackClusters[i];
return i;

View File

@ -62,10 +62,11 @@ public:
class AudacityProject;
class Meter;
class MixerBoard;
#ifdef EXPERIMENTAL_MIDI_OUT
class Track;
class NoteTrack;
#endif
class PlayableTrack;
class WaveTrack;
class MixerTrackCluster final : public wxPanelWrapper
@ -73,11 +74,15 @@ class MixerTrackCluster final : public wxPanelWrapper
public:
MixerTrackCluster(wxWindow* parent,
MixerBoard* grandParent, AudacityProject* project,
WaveTrack* pLeftTrack, WaveTrack* pRightTrack = NULL,
PlayableTrack* pTrack,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize);
virtual ~MixerTrackCluster() {}
WaveTrack *GetWave() const;
WaveTrack *GetRight() const;
NoteTrack *GetNote() const;
void UpdatePrefs();
void HandleResize(); // For wxSizeEvents, update gain slider and meter.
@ -115,23 +120,7 @@ private:
public:
#ifdef EXPERIMENTAL_MIDI_OUT
// mTrack is redundant, but simplifies code that operates on either
// mLeftTrack or mNoteTrack.
Track* mTrack; // either mLeftTrack or mNoteTrack, whichever is not NULL
#endif
WaveTrack* mLeftTrack; // NULL if Note Track
WaveTrack* mRightTrack; // NULL if mono
//vvv Vaughan, 2010-11-05:
// I suggest that when this is no longer experimental, rather than all these #ifdef's,
// this be done by factoring, i.e., add two subclasses to MixerTrackCluster,
// MixerNoteTrackCluster and MixerWaveTrackCluster, such that all the common
// code is in the parent, and these #ifdef's are only around
// MixerNoteTrackCluster rather than sprinkled throughout MixerTrackCluster.
#ifdef EXPERIMENTAL_MIDI_OUT
NoteTrack* mNoteTrack; // NULL if Wave Track
#endif
PlayableTrack * mTrack;
private:
MixerBoard* mMixerBoard;
@ -213,45 +202,26 @@ public:
void UpdateTrackClusters();
int GetTrackClustersWidth();
#ifdef EXPERIMENTAL_MIDI_OUT
void MoveTrackCluster(const Track* pTrack, bool bUp); // Up in TrackPanel is left in MixerBoard.
void RemoveTrackCluster(const Track* pTrack);
void MoveTrackCluster(const PlayableTrack* pTrack, bool bUp); // Up in TrackPanel is left in MixerBoard.
void RemoveTrackCluster(const PlayableTrack* pTrack);
void RemoveTrackCluster(size_t nIndex);
wxBitmap* GetMusicalInstrumentBitmap(const wxString & name);
#else
void MoveTrackCluster(const WaveTrack* pTrack, bool bUp); // Up in TrackPanel is left in MixerBoard.
void RemoveTrackCluster(const WaveTrack* pTrack);
wxBitmap* GetMusicalInstrumentBitmap(const WaveTrack* pLeftTrack);
#endif
wxBitmap* GetMusicalInstrumentBitmap(const Track *pTrack);
bool HasSolo();
#ifdef EXPERIMENTAL_MIDI_OUT
void RefreshTrackCluster(const Track* pTrack, bool bEraseBackground = true);
#else
void RefreshTrackCluster(const WaveTrack* pTrack, bool bEraseBackground = true);
#endif
void RefreshTrackCluster(const PlayableTrack* pTrack, bool bEraseBackground = true);
void RefreshTrackClusters(bool bEraseBackground = true);
void ResizeTrackClusters();
void ResetMeters(const bool bResetClipping);
#ifdef EXPERIMENTAL_MIDI_OUT
void UpdateName(const Track* pTrack);
void UpdateMute(const Track* pTrack = NULL); // NULL means update for all tracks.
void UpdateSolo(const Track* pTrack = NULL); // NULL means update for all tracks.
void UpdatePan(const Track* pTrack);
void UpdateGain(const Track* pTrack);
#else
void UpdateName(const WaveTrack* pTrack);
void UpdateMute(const WaveTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdateSolo(const WaveTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdatePan(const WaveTrack* pTrack);
void UpdateGain(const WaveTrack* pTrack);
#endif
void UpdateName(const PlayableTrack* pTrack);
void UpdateMute(const PlayableTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdateSolo(const PlayableTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdatePan(const PlayableTrack* pTrack);
void UpdateGain(const PlayableTrack* pTrack);
void UpdateMeters(const double t1, const bool bLoopedPlay);
@ -259,13 +229,8 @@ public:
private:
void CreateMuteSoloImages();
#ifdef EXPERIMENTAL_MIDI_OUT
int FindMixerTrackCluster(const Track* pTrack,
int FindMixerTrackCluster(const PlayableTrack* pTrack,
MixerTrackCluster** hMixerTrackCluster) const;
#else
int FindMixerTrackCluster(const WaveTrack* pLeftTrack,
MixerTrackCluster** hMixerTrackCluster) const;
#endif
void LoadMusicalInstruments();
// event handlers

View File

@ -103,8 +103,8 @@ NoteTrack::Holder TrackFactory::NewNoteTrack()
return std::make_unique<NoteTrack>(mDirManager);
}
NoteTrack::NoteTrack(const std::shared_ptr<DirManager> &projDirManager):
Track(projDirManager)
NoteTrack::NoteTrack(const std::shared_ptr<DirManager> &projDirManager)
: NoteTrackBase(projDirManager)
{
SetDefaultName(_("Note Track"));
SetName(GetDefaultName());
@ -113,7 +113,7 @@ Track(projDirManager)
mSerializationLength = 0;
#ifdef EXPERIMENTAL_MIDI_OUT
mGain = 0;
mVelocity = 0;
#endif
mBottomNote = 24;
mPitchHeight = 5;
@ -160,7 +160,7 @@ Track::Holder NoteTrack::Duplicate() const
duplicate->mVisibleChannels = mVisibleChannels;
duplicate->SetOffset(GetOffset());
#ifdef EXPERIMENTAL_MIDI_OUT
duplicate->SetGain(GetGain());
duplicate->SetVelocity(GetVelocity());
#endif
// This std::move is needed to "upcast" the pointer type
return std::move(duplicate);
@ -782,7 +782,7 @@ bool NoteTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
else if (!wxStrcmp(attr, wxT("velocity")) &&
XMLValueChecker::IsGoodString(strValue) &&
Internat::CompatibleToDouble(strValue, &dblValue))
mGain = (float) dblValue;
mVelocity = (float) dblValue;
#endif
else if (!wxStrcmp(attr, wxT("bottomnote")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
@ -831,6 +831,10 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
saveme->mSeq->write(data, true);
xmlFile.StartTag(wxT("notetrack"));
xmlFile.WriteAttr(wxT("name"), saveme->mName);
#ifdef EXPERIMENTAL_MIDI_OUT
xmlFile.WriteAttr(wxT("mute"), mMute);
xmlFile.WriteAttr(wxT("solo"), mSolo);
#endif
xmlFile.WriteAttr(wxT("offset"), saveme->GetOffset());
xmlFile.WriteAttr(wxT("visiblechannels"), saveme->mVisibleChannels);
xmlFile.WriteAttr(wxT("height"), saveme->GetActualHeight());
@ -838,7 +842,7 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected());
#ifdef EXPERIMENTAL_MIDI_OUT
xmlFile.WriteAttr(wxT("velocity"), (double) saveme->mGain);
xmlFile.WriteAttr(wxT("velocity"), (double) saveme->mVelocity);
#endif
xmlFile.WriteAttr(wxT("bottomnote"), saveme->mBottomNote);
xmlFile.WriteAttr(wxT("data"), wxString(data.str().c_str(), wxConvUTF8));

View File

@ -50,7 +50,17 @@ class wxRect;
class DirManager;
class Alg_seq; // from "allegro.h"
class AUDACITY_DLL_API NoteTrack final : public Track {
using NoteTrackBase =
#ifdef EXPERIMENTAL_MIDI_OUT
PlayableTrack
#else
AudioTrack
#endif
;
class AUDACITY_DLL_API NoteTrack final
: public NoteTrackBase
{
public:
friend class TrackArtist;
@ -98,8 +108,8 @@ class AUDACITY_DLL_API NoteTrack final : public Track {
bool Shift(double t) /* not override */;
#ifdef EXPERIMENTAL_MIDI_OUT
float GetGain() const { return mGain; }
void SetGain(float gain) { mGain = gain; }
float GetVelocity() const { return mVelocity; }
void SetVelocity(float velocity) { mVelocity = velocity; }
#endif
double NearestBeatTime(double time, double *beat) const;
@ -202,7 +212,7 @@ class AUDACITY_DLL_API NoteTrack final : public Track {
long mSerializationLength;
#ifdef EXPERIMENTAL_MIDI_OUT
float mGain; // velocity offset
float mVelocity; // velocity offset
#endif
// mBottom is the Y offset of pitch 0 (normally off screen)

View File

@ -3655,6 +3655,8 @@ void AudacityProject::WriteXML(XMLWriter &xmlFile, bool bWantSaveCompressed)
while (t) {
if ((t->GetKind() == Track::Wave) && bWantSaveCompressed)
{
auto wt = static_cast<const WaveTrack *>(t);
//vvv This should probably be a method, WaveTrack::WriteCompressedTrackXML().
xmlFile.StartTag(wxT("import"));
xmlFile.WriteAttr(wxT("filename"), mStrOtherNamesArray[ndx]); // Assumes mTracks order hasn't changed!
@ -3665,8 +3667,8 @@ void AudacityProject::WriteXML(XMLWriter &xmlFile, bool bWantSaveCompressed)
// xmlFile.WriteAttr(wxT("linked"), t->GetLinked());
xmlFile.WriteAttr(wxT("offset"), t->GetOffset(), 8);
xmlFile.WriteAttr(wxT("mute"), t->GetMute());
xmlFile.WriteAttr(wxT("solo"), t->GetSolo());
xmlFile.WriteAttr(wxT("mute"), wt->GetMute());
xmlFile.WriteAttr(wxT("solo"), wt->GetSolo());
xmlFile.WriteAttr(wxT("height"), t->GetActualHeight());
xmlFile.WriteAttr(wxT("minimized"), t->GetMinimized());
@ -3975,10 +3977,12 @@ bool AudacityProject::Save(bool overwrite /* = true */ ,
((pTrack != NULL) && (pSavedTrack != NULL));
pTrack = iter.Next(), pSavedTrack = savedTrackIter.Next())
{
pWaveTrack = (WaveTrack*)pTrack;
pWaveTrack = static_cast<WaveTrack*>(pTrack);
auto pSavedWaveTrack = static_cast<const WaveTrack*>(pSavedTrack);
pWaveTrack->SetSelected(pSavedTrack->GetSelected());
pWaveTrack->SetMute(pSavedTrack->GetMute());
pWaveTrack->SetSolo(pSavedTrack->GetSolo());
pWaveTrack->SetMute(pSavedWaveTrack->GetMute());
pWaveTrack->SetSolo(pSavedWaveTrack->GetSolo());
pWaveTrack->SetGain(((WaveTrack*)pSavedTrack)->GetGain());
pWaveTrack->SetPan(((WaveTrack*)pSavedTrack)->GetPan());
@ -5396,12 +5400,13 @@ void AudacityProject::RemoveTrack(Track * toRemove)
wxString name = toRemove->GetName();
Track *partner = toRemove->GetLink();
if (toRemove->GetKind() == Track::Wave)
auto playable = dynamic_cast<PlayableTrack*>(toRemove);
if (playable)
{
// Update mixer board displayed tracks.
MixerBoard* pMixerBoard = this->GetMixerBoard();
if (pMixerBoard)
pMixerBoard->RemoveTrackCluster((WaveTrack*)toRemove); // Will remove partner shown in same cluster.
pMixerBoard->RemoveTrackCluster(playable); // Will remove partner shown in same cluster.
}
mTracks->Remove(toRemove);
@ -5429,36 +5434,45 @@ void AudacityProject::HandleTrackMute(Track *t, const bool exclusive)
if (exclusive)
{
TrackListIterator iter(GetTracks());
Track *i = iter.First();
while (i) {
if (i == t) {
i->SetMute(true);
if(i->GetLinked()) { // also mute the linked track
i = iter.Next();
Track *it = iter.First();
while (it) {
auto i = dynamic_cast<PlayableTrack *>(it);
if (i) {
if (i == t) {
i->SetMute(true);
if(i->GetLinked()) { // also mute the linked track
it = iter.Next();
i->SetMute(true);
}
}
else {
i->SetMute(false);
}
i->SetSolo(false);
}
else {
i->SetMute(false);
}
i->SetSolo(false);
i = iter.Next();
it = iter.Next();
}
}
else
{
// Normal click toggles this track.
t->SetMute(!t->GetMute());
auto pt = dynamic_cast<PlayableTrack *>( t );
if (!pt)
return;
pt->SetMute(!pt->GetMute());
if(t->GetLinked()) // set mute the same on both, if a pair
{
bool muted = t->GetMute();
bool muted = pt->GetMute();
TrackListIterator iter(GetTracks());
Track *i = iter.First();
while (i != t) { // search for this track
i = iter.Next();
}
i = iter.Next(); // get the next one, since linked
i->SetMute(muted); // and mute it as well
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi)
pi->SetMute(muted); // and mute it as well
}
if (IsSoloSimple() || IsSoloNone())
@ -5470,18 +5484,23 @@ void AudacityProject::HandleTrackMute(Track *t, const bool exclusive)
// We also set a solo indicator if we have just one track / stereo pair playing.
// otherwise clear solo on everything.
while (i) {
if( !i->GetMute())
{
nPlaying += 1;
if(i->GetLinked())
i = iter.Next(); // don't count this one as it is linked
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi) {
if( !pi->GetMute())
{
nPlaying += 1;
if(i->GetLinked())
i = iter.Next(); // don't count this one as it is linked
}
}
i = iter.Next();
}
i = iter.First();
while (i) {
i->SetSolo( (nPlaying==1) && !i->GetMute() ); // will set both of a stereo pair
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi)
pi->SetSolo( (nPlaying==1) && !pi->GetMute() ); // will set both of a stereo pair
i = iter.Next();
}
}
@ -5491,8 +5510,12 @@ void AudacityProject::HandleTrackMute(Track *t, const bool exclusive)
// Type of solo (standard or simple) follows the set preference, unless
// alternate == true, which causes the opposite behavior.
void AudacityProject::HandleTrackSolo(Track *t, const bool alternate)
void AudacityProject::HandleTrackSolo(Track *const t, const bool alternate)
{
const auto pt = dynamic_cast<PlayableTrack *>( t );
if (!pt)
return;
bool bSoloMultiple = !IsSoloSimple() ^ alternate;
// Standard and Simple solo have opposite defaults:
@ -5502,17 +5525,19 @@ void AudacityProject::HandleTrackSolo(Track *t, const bool alternate)
// when in standard radio button mode.
if ( bSoloMultiple )
{
t->SetSolo( !t->GetSolo() );
pt->SetSolo( !pt->GetSolo() );
if(t->GetLinked())
{
bool soloed = t->GetSolo();
bool soloed = pt->GetSolo();
TrackListIterator iter(GetTracks());
Track *i = iter.First();
while (i != t) { // search for this track
i = iter.Next();
}
i = iter.Next(); // get the next one, since linked
i->SetSolo(soloed); // and solo it as well
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi)
pi->SetSolo(soloed); // and solo it as well
}
}
else
@ -5521,26 +5546,32 @@ void AudacityProject::HandleTrackSolo(Track *t, const bool alternate)
// OR unmute and unsolo everything.
TrackListIterator iter(GetTracks());
Track *i = iter.First();
bool bWasSolo = t->GetSolo();
bool bWasSolo = pt->GetSolo();
while (i) {
if( i==t )
{
i->SetSolo(!bWasSolo);
pt->SetSolo(!bWasSolo);
if( IsSoloSimple() )
i->SetMute(false);
pt->SetMute(false);
if(t->GetLinked())
{
i = iter.Next();
i->SetSolo(!bWasSolo);
if( IsSoloSimple() )
i->SetMute(false);
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi) {
pi->SetSolo(!bWasSolo);
if( IsSoloSimple() )
pi->SetMute(false);
}
}
}
else
{
i->SetSolo(false);
if( IsSoloSimple() )
i->SetMute(!bWasSolo);
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi) {
pi->SetSolo(false);
if( IsSoloSimple() )
pi->SetMute(!bWasSolo);
}
}
i = iter.Next();
}

View File

@ -50,8 +50,6 @@ Track::Track(const std::shared_ptr<DirManager> &projDirManager)
mList = NULL;
mSelected = false;
mLinked = false;
mMute = false;
mSolo = false;
mY = 0;
mHeight = 150;
@ -92,8 +90,6 @@ void Track::Init(const Track &orig)
mSelected = orig.mSelected;
mLinked = orig.mLinked;
mMute = orig.mMute;
mSolo = orig.mSolo;
mHeight = orig.mHeight;
mMinimized = orig.mMinimized;
mChannel = orig.mChannel;
@ -112,8 +108,6 @@ void Track::SetSelected(bool s)
void Track::Merge(const Track &orig)
{
mSelected = orig.mSelected;
mMute = orig.mMute;
mSolo = orig.mSolo;
}
Track::~Track()
@ -331,6 +325,22 @@ bool Track::SyncLockAdjust(double oldT1, double newT1)
return true;
}
void PlayableTrack::Init( const PlayableTrack &orig )
{
mMute = orig.mMute;
mSolo = orig.mSolo;
AudioTrack::Init( orig );
}
void PlayableTrack::Merge( const Track &orig )
{
auto pOrig = dynamic_cast<const PlayableTrack *>(&orig);
wxASSERT( pOrig );
mMute = pOrig->mMute;
mSolo = pOrig->mSolo;
AudioTrack::Merge( *pOrig );
}
// TrackListIterator
TrackListIterator::TrackListIterator(TrackList * val)
: l(val)
@ -1208,7 +1218,9 @@ unsigned TrackList::GetNumExportChannels(bool selectionOnly) const
for (tr = iter.First(this); tr != NULL; tr = iter.Next()) {
// Want only unmuted wave tracks.
if ((tr->GetKind() != Track::Wave) || tr->GetMute())
auto wt = static_cast<const WaveTrack *>(tr);
if ((tr->GetKind() != Track::Wave) ||
wt->GetMute())
continue;
// do we only want selected ones?
@ -1265,8 +1277,9 @@ namespace {
for (; p != end; ++p) {
const auto &track = *p;
auto wt = static_cast<const WaveTrack *>(&*track);
if (track->GetKind() == Track::Wave &&
(includeMuted || !track->GetMute()) &&
(includeMuted || !wt->GetMute()) &&
(track->GetSelected() || !selectionOnly)) {
waveTrackArray.push_back(static_cast<WaveTrack*>(track.get()));
}

View File

@ -140,8 +140,6 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
protected:
int mChannel;
double mOffset;
bool mMute;
bool mSolo;
mutable std::shared_ptr<DirManager> mDirManager;
@ -189,14 +187,10 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
void SetDefaultName( const wxString &n ) { mDefaultName = n; }
bool GetSelected() const { return mSelected; }
bool GetMute () const { return mMute; }
bool GetLinked () const { return mLinked; }
bool GetSolo () const { return mSolo; }
virtual void SetSelected(bool s);
void SetMute (bool m) { mMute = m; }
void SetLinked (bool l);
void SetSolo (bool s) { mSolo = s; }
int GetChannel() const { return mChannel; }
virtual double GetOffset() const = 0;
@ -251,6 +245,34 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
bool IsSyncLockSelected() const;
};
class AudioTrack /* not final */ : public Track
{
public:
AudioTrack(const std::shared_ptr<DirManager> &projDirManager)
: Track{ projDirManager } {}
AudioTrack(const Track &orig) : Track{ orig } {}
};
class PlayableTrack /* not final */ : public AudioTrack
{
public:
PlayableTrack(const std::shared_ptr<DirManager> &projDirManager)
: AudioTrack{ projDirManager } {}
PlayableTrack(const Track &orig) : AudioTrack{ orig } {}
bool GetMute () const { return mMute; }
bool GetSolo () const { return mSolo; }
void SetMute (bool m) { mMute = m; }
void SetSolo (bool s) { mSolo = s; }
void Init( const PlayableTrack &init );
void Merge( const Track &init ) override;
protected:
bool mMute { false };
bool mSolo { false };
};
class AUDACITY_DLL_API TrackListIterator /* not final */
{
public:

View File

@ -336,7 +336,8 @@ void TrackArtist::DrawTracks(TrackList * tracks,
bool hasSolo = false;
for (t = iter.First(); t; t = iter.Next()) {
if (t->GetSolo()) {
auto pt = dynamic_cast<const PlayableTrack *>(t);
if (pt && pt->GetSolo()) {
hasSolo = true;
break;
}
@ -457,7 +458,8 @@ void TrackArtist::DrawTrack(const Track * t,
clip->ClearDisplayRect();
}
bool muted = (hasSolo || t->GetMute()) && !t->GetSolo();
bool muted = (hasSolo || wt->GetMute()) &&
!wt->GetSolo();
#if defined(__WXMAC__)
wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
@ -493,7 +495,11 @@ void TrackArtist::DrawTrack(const Track * t,
#ifdef USE_MIDI
case Track::Note:
{
bool muted = (hasSolo || t->GetMute()) && !t->GetSolo();
auto nt = static_cast<const NoteTrack *>(t);
bool muted = false;
#ifdef EXPERIMENTAL_MIDI_OUT
muted = (hasSolo || nt->GetMute()) && !nt->GetSolo();
#endif
DrawNoteTrack((NoteTrack *)t, dc, rect, selectedRegion, zoomInfo, muted);
break;
}

View File

@ -4963,7 +4963,7 @@ void TrackPanel::HandleSliders(wxMouseEvent &event, bool pan)
// mCapturedTrack is not wave...
if (!pan) {
// .. so assume it is note
static_cast<NoteTrack*>(mCapturedTrack)->SetGain(newValue);
static_cast<NoteTrack*>(mCapturedTrack)->SetVelocity(newValue);
#ifdef EXPERIMENTAL_MIXER_BOARD
if (pMixerBoard)
// probably should modify UpdateGain to take a track that is
@ -5212,27 +5212,17 @@ void TrackPanel::HandleRearrange(wxMouseEvent & event)
if (event.m_y < mMoveUpThreshold || event.m_y < 0) {
mTracks->MoveUp(mCapturedTrack);
--mRearrangeCount;
#ifdef EXPERIMENTAL_MIDI_OUT
if (pMixerBoard && (mCapturedTrack->GetKind() == Track::Wave ||
mCapturedTrack->GetKind() == Track::Note))
pMixerBoard->MoveTrackCluster(mCapturedTrack, true /* up */);
#else
if (pMixerBoard && (mCapturedTrack->GetKind() == Track::Wave))
pMixerBoard->MoveTrackCluster((WaveTrack*)mCapturedTrack, true /* up */);
#endif
if (pMixerBoard)
if(auto pPlayable = dynamic_cast< const PlayableTrack* >( mCapturedTrack ))
pMixerBoard->MoveTrackCluster(pPlayable, true /* up */);
}
else if (event.m_y > mMoveDownThreshold || event.m_y > GetRect().GetHeight()) {
mTracks->MoveDown(mCapturedTrack);
++mRearrangeCount;
/* i18n-hint: a direction as in up or down.*/
#ifdef EXPERIMENTAL_MIDI_OUT
if (pMixerBoard && (mCapturedTrack->GetKind() == Track::Wave ||
mCapturedTrack->GetKind() == Track::Note))
pMixerBoard->MoveTrackCluster(mCapturedTrack, false /* down */);
#else
if (pMixerBoard && (mCapturedTrack->GetKind() == Track::Wave))
pMixerBoard->MoveTrackCluster((WaveTrack*)mCapturedTrack, false /* down */);
#endif
if (pMixerBoard)
if(auto pPlayable = dynamic_cast< const PlayableTrack* >( mCapturedTrack ))
pMixerBoard->MoveTrackCluster(pPlayable, false /* down */);
}
else
{
@ -9298,18 +9288,19 @@ void TrackInfo::DrawMuteSolo(wxDC * dc, const wxRect & rect, Track * t,
return; // don't draw mute and solo buttons, because they don't fit into track label
AColor::MediumTrackInfo( dc, t->GetSelected());
auto pt = dynamic_cast<const PlayableTrack *>(t);
if( solo )
{
if( t->GetSolo() )
if( pt && pt->GetSolo() )
{
AColor::Solo(dc, t->GetSolo(), t->GetSelected());
AColor::Solo(dc, pt->GetSolo(), t->GetSelected());
}
}
else
{
if( t->GetMute() )
if( pt && pt->GetMute() )
{
AColor::Mute(dc, t->GetMute(), t->GetSelected(), t->GetSolo());
AColor::Mute(dc, pt->GetMute(), t->GetSelected(), pt->GetSolo());
}
}
//(solo) ? AColor::Solo(dc, t->GetSolo(), t->GetSelected()) :
@ -9328,7 +9319,11 @@ void TrackInfo::DrawMuteSolo(wxDC * dc, const wxRect & rect, Track * t,
dc->GetTextExtent(str, &textWidth, &textHeight);
dc->DrawText(str, bev.x + (bev.width - textWidth) / 2, bev.y + (bev.height - textHeight) / 2);
AColor::BevelTrackInfo(*dc, (solo?t->GetSolo():t->GetMute()) == down, bev);
AColor::BevelTrackInfo(
*dc,
(solo ? pt->GetSolo() : (pt && pt->GetMute())) == down,
bev
);
if (solo && !down) {
// Update the mute button, which may be grayed out depending on
@ -9377,7 +9372,7 @@ void TrackInfo::DrawVelocitySlider(wxDC *dc, NoteTrack *t, wxRect rect) const
auto &gain = mGain; // mGains[index];
gain->SetStyle(VEL_SLIDER);
GainSlider(index)->Move(wxPoint(gainRect.x, gainRect.y));
GainSlider(index)->Set(t->GetGain());
GainSlider(index)->Set(t->GetVelocity());
GainSlider(index)->OnPaint(*dc
// , t->GetSelected()
);

View File

@ -363,7 +363,8 @@ wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
#endif
// LLL: Remove these during "refactor"
if( t->GetMute() )
auto pt = dynamic_cast<PlayableTrack *>(t);
if( pt && pt->GetMute() )
{
// The following comment also applies to the solo, selected,
// and synclockselected states.
@ -376,7 +377,7 @@ wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
name->Append( wxT(" ") + wxString(_( " Mute On" )) );
}
if( t->GetSolo() )
if( pt && pt->GetSolo() )
{
/* i18n-hint: This is for screen reader software and indicates that
on this track solo is on.*/
@ -547,12 +548,13 @@ wxAccStatus TrackPanelAx::GetValue( int WXUNUSED(childId), wxString* WXUNUSED(st
}
// LLL: Remove these during "refactor"
if( t->GetMute() )
auto pt = dynamic_cast<PlayableTrack *>(t);
if( pt && pt->GetMute() )
{
strValue->Append( _( " Mute On" ) );
}
if( t->GetSolo() )
if( pt && pt->GetSolo() )
{
strValue->Append( _( " Solo On" ) );
}

View File

@ -79,7 +79,7 @@ WaveTrack::Holder TrackFactory::NewWaveTrack(sampleFormat format, double rate)
}
WaveTrack::WaveTrack(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format, double rate) :
Track(projDirManager)
PlayableTrack(projDirManager)
{
if (format == (sampleFormat)0)
{
@ -116,7 +116,7 @@ WaveTrack::WaveTrack(const std::shared_ptr<DirManager> &projDirManager, sampleFo
}
WaveTrack::WaveTrack(const WaveTrack &orig):
Track(orig)
PlayableTrack(orig)
, mpSpectrumSettings(orig.mpSpectrumSettings
? std::make_unique<SpectrogramSettings>(*orig.mpSpectrumSettings)
: nullptr
@ -141,7 +141,7 @@ WaveTrack::WaveTrack(const WaveTrack &orig):
// Copy the track metadata but not the contents.
void WaveTrack::Init(const WaveTrack &orig)
{
Track::Init(orig);
PlayableTrack::Init(orig);
mFormat = orig.mFormat;
mRate = orig.mRate;
mGain = orig.mGain;
@ -171,7 +171,7 @@ void WaveTrack::Merge(const Track &orig)
SetWaveformSettings
(wt.mpWaveformSettings ? std::make_unique<WaveformSettings>(*wt.mpWaveformSettings) : nullptr);
}
Track::Merge(orig);
PlayableTrack::Merge(orig);
}
WaveTrack::~WaveTrack()

View File

@ -69,7 +69,7 @@ class Regions : public std::vector < Region > {};
class Envelope;
class AUDACITY_DLL_API WaveTrack final : public Track {
class AUDACITY_DLL_API WaveTrack final : public PlayableTrack {
private:

View File

@ -161,10 +161,12 @@ bool GetProjectInfoCommand::testLinked(const Track * track) const
bool GetProjectInfoCommand::testSolo(const Track * track) const
{
return track->GetSolo();
auto pt = dynamic_cast<const PlayableTrack *>(track);
return pt && pt->GetSolo();
}
bool GetProjectInfoCommand::testMute(const Track * track) const
{
return track->GetMute();
auto pt = dynamic_cast<const PlayableTrack *>(track);
return pt && pt->GetMute();
}

View File

@ -127,15 +127,17 @@ bool GetTrackInfoCommand::Apply(CommandExecutionContext context)
}
else if (mode.IsSameAs(wxT("Solo")))
{
if (t->GetKind() == Track::Wave)
SendBooleanStatus(t->GetSolo());
auto pt = dynamic_cast<const PlayableTrack *>(t);
if (pt)
SendBooleanStatus(pt->GetSolo());
else
SendBooleanStatus(false);
}
else if (mode.IsSameAs(wxT("Mute")))
{
if (t->GetKind() == Track::Wave)
SendBooleanStatus(t->GetMute());
auto pt = dynamic_cast<const PlayableTrack *>(t);
if (pt)
SendBooleanStatus(pt->GetMute());
else
SendBooleanStatus(false);
}

View File

@ -98,12 +98,14 @@ void SetProjectInfoCommand::setSelected(Track * trk, bool param) const
void SetProjectInfoCommand::setSolo(Track * trk, bool param) const
{
if(trk->GetKind() == Track::Wave)
trk->SetSolo(param);
auto pt = dynamic_cast<PlayableTrack *>(trk);
if (pt)
pt->SetSolo(param);
}
void SetProjectInfoCommand::setMute(Track * trk, bool param) const
{
if(trk->GetKind() == Track::Wave)
trk->SetMute(param);
auto pt = dynamic_cast<PlayableTrack *>(trk);
if (pt)
pt->SetMute(param);
}

View File

@ -425,7 +425,9 @@ bool Exporter::ExamineTracks()
while (tr) {
if (tr->GetKind() == Track::Wave) {
if ( (tr->GetSelected() || !mSelectedOnly) && !tr->GetMute() ) { // don't count muted tracks
auto wt = static_cast<const WaveTrack *>(tr);
if ( (tr->GetSelected() || !mSelectedOnly) &&
!wt->GetMute() ) { // don't count muted tracks
mNumSelected++;
if (tr->GetChannel() == Track::LeftChannel) {
@ -1255,7 +1257,9 @@ ExportMixerDialog::ExportMixerDialog( const TrackList *tracks, bool selectedOnly
for( const Track *t = iter.First(); t; t = iter.Next() )
{
if( t->GetKind() == Track::Wave && ( t->GetSelected() || !selectedOnly ) && !t->GetMute() )
auto wt = static_cast<const WaveTrack *>(t);
if( t->GetKind() == Track::Wave && ( t->GetSelected() || !selectedOnly ) &&
!wt->GetMute() )
{
numTracks++;
const wxString sTrackName = (t->GetName()).Left(20);

View File

@ -154,7 +154,8 @@ void ExportMultiple::CountTracksAndLabels()
// Count WaveTracks, and for linked pairs, count only the second of the pair.
case Track::Wave:
{
if (!pTrack->GetMute() && !pTrack->GetLinked()) // Don't count muted tracks.
auto wt = static_cast<const WaveTrack *>(pTrack);
if (!wt->GetMute() && !pTrack->GetLinked()) // Don't count muted tracks.
mNumWaveTracks++;
break;
}
@ -811,7 +812,9 @@ ProgressResult ExportMultiple::ExportMultipleByTrack(bool byName,
for (tr = iter.First(mTracks); tr != NULL; tr = iter.Next()) {
// Want only non-muted wave tracks.
if ((tr->GetKind() != Track::Wave) || tr->GetMute())
auto wt = static_cast<const WaveTrack *>(tr);
if ((tr->GetKind() != Track::Wave) ||
wt->GetMute())
continue;
// Get the times for the track
@ -898,7 +901,8 @@ ProgressResult ExportMultiple::ExportMultipleByTrack(bool byName,
for (tr = iter.First(mTracks); tr != NULL; tr = iter.Next()) {
// Want only non-muted wave tracks.
if ((tr->GetKind() != Track::Wave) || (tr->GetMute() == true)) {
auto wt = static_cast<const WaveTrack *>(tr);
if ((tr->GetKind() != Track::Wave) || (wt->GetMute())) {
continue;
}

View File

@ -418,11 +418,7 @@ void ControlToolBar::EnableDisableButtons()
if (p) {
TrackListIterator iter( p->GetTracks() );
for (Track *t = iter.First(); t; t = iter.Next()) {
if (t->GetKind() == Track::Wave
#if defined(USE_MIDI)
|| t->GetKind() == Track::Note
#endif
) {
if (dynamic_cast<const AudioTrack*>(t)) {
tracks = true;
break;
}

View File

@ -44,9 +44,8 @@ class TipPanel;
#define DB_SLIDER 2 // -36...36 dB
#define PAN_SLIDER 3 // -1.0...1.0
#define SPEED_SLIDER 4 // 0.01 ..3.0
#ifdef EXPERIMENTAL_MIDI_OUT
#define VEL_SLIDER 5 // -50..50
#endif
#define DB_MIN -36.0f
#define DB_MAX 36.0f