mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-18 17:10:05 +02:00
Iterations over channels in remaining places that won't generalize...
... simply to the more-than-stereo case. All these places have the comment: // TODO: more-than-two-channels
This commit is contained in:
commit
dbf44efc9f
@ -1891,20 +1891,17 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
||||
// group determination should mimic what is done in audacityAudioCallback()
|
||||
// when calling RealtimeProcess().
|
||||
int group = 0;
|
||||
for (size_t i = 0, cnt = mPlaybackTracks.size(); i < cnt; i++)
|
||||
for (size_t i = 0, cnt = mPlaybackTracks.size(); i < cnt;)
|
||||
{
|
||||
const WaveTrack *vt = mPlaybackTracks[i].get();
|
||||
|
||||
unsigned chanCnt = 1;
|
||||
if (vt->GetLinked())
|
||||
{
|
||||
i++;
|
||||
chanCnt++;
|
||||
}
|
||||
// TODO: more-than-two-channels
|
||||
unsigned chanCnt = TrackList::Channels(vt).size();
|
||||
i += chanCnt;
|
||||
|
||||
// Setup for realtime playback at the rate of the realtime
|
||||
// stream, not the rate of the track.
|
||||
em.RealtimeAddProcessor(group++, chanCnt, mRate);
|
||||
em.RealtimeAddProcessor(group++, std::min(2u, chanCnt), mRate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4992,26 +4989,38 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer,
|
||||
|
||||
bool drop = false;
|
||||
bool dropQuickly = false;
|
||||
bool linkFlag = false;
|
||||
for (unsigned t = 0; t < numPlaybackTracks; t++)
|
||||
{
|
||||
WaveTrack *vt = mPlaybackTracks[t].get();
|
||||
|
||||
chans[chanCnt] = vt;
|
||||
|
||||
if ( linkFlag ) {
|
||||
linkFlag = false;
|
||||
// TODO: more-than-two-channels
|
||||
auto nextTrack =
|
||||
t + 1 < numPlaybackTracks
|
||||
? mPlaybackTracks[t + 1].get()
|
||||
: nullptr;
|
||||
bool firstChannel = vt->IsLeader();
|
||||
bool lastChannel = !nextTrack || nextTrack->IsLeader();
|
||||
|
||||
if ( ! firstChannel )
|
||||
dropQuickly = dropQuickly && doneMicrofading( *vt );
|
||||
}
|
||||
else {
|
||||
drop = dropTrack( *vt );
|
||||
|
||||
linkFlag = vt->GetLinked();
|
||||
selected = vt->GetSelected();
|
||||
|
||||
if ( lastChannel ) {
|
||||
// TODO: more-than-two-channels
|
||||
#if 1
|
||||
// If we have a mono track, clear the right channel
|
||||
if (!linkFlag)
|
||||
memset(tempBufs[1], 0, framesPerBuffer * sizeof(float));
|
||||
#else
|
||||
// clear any other channels
|
||||
for ( size_t c = chanCnt + 1; c < numPlaybackChannels; ++c )
|
||||
memset(tempBufs[c], 0, framesPerBuffer * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
|
||||
dropQuickly = drop && doneMicrofading( *vt );
|
||||
}
|
||||
@ -5055,7 +5064,7 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer,
|
||||
maxLen = std::max(maxLen, len);
|
||||
|
||||
|
||||
if (linkFlag)
|
||||
if ( ! lastChannel )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -2318,14 +2318,13 @@ CommandFlag MenuCommandHandler::GetUpdateFlags
|
||||
flags |= PlayableTracksExistFlag;
|
||||
if (t->GetSelected()) {
|
||||
flags |= TracksSelectedFlag;
|
||||
if (t->GetLinked()) {
|
||||
// TODO: more-than-two-channels
|
||||
if (TrackList::Channels(t).size() > 1) {
|
||||
flags |= StereoRequiredFlag;
|
||||
}
|
||||
else {
|
||||
flags |= WaveTracksSelectedFlag;
|
||||
flags |= AudioTracksSelectedFlag;
|
||||
}
|
||||
}
|
||||
if( t->GetEndTime() > t->GetStartTime() )
|
||||
flags |= HasWaveDataFlag;
|
||||
}
|
||||
@ -5710,6 +5709,7 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
||||
bool bPastedSomething = false;
|
||||
|
||||
auto pC = clipTrackRange.begin();
|
||||
size_t nnChannels, ncChannels;
|
||||
while (*pN && *pC) {
|
||||
auto n = *pN;
|
||||
auto c = *pC;
|
||||
@ -5764,15 +5764,34 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
||||
_("Pasting one type of track into another is not allowed.")
|
||||
};
|
||||
|
||||
// We should need this check only each time we visit the leading
|
||||
// channel
|
||||
if ( n->IsLeader() ) {
|
||||
wxASSERT( c->IsLeader() ); // the iteration logic should ensure this
|
||||
|
||||
auto cChannels = TrackList::Channels(c);
|
||||
ncChannels = cChannels.size();
|
||||
auto nChannels = TrackList::Channels(n);
|
||||
nnChannels = nChannels.size();
|
||||
|
||||
// When trying to copy from stereo to mono track, show error and exit
|
||||
// TODO: Automatically offer user to mix down to mono (unfortunately
|
||||
// this is not easy to implement
|
||||
if (c->GetLinked() && !n->GetLinked())
|
||||
if (ncChannels > nnChannels)
|
||||
{
|
||||
if (ncChannels > 2) {
|
||||
// TODO: more-than-two-channels-message
|
||||
// Re-word the error message
|
||||
}
|
||||
// else
|
||||
|
||||
// Throw, so that any previous changes to the project in this loop
|
||||
// are discarded.
|
||||
throw SimpleMessageBoxException{
|
||||
_("Copying stereo audio into a mono track is not allowed.")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!ff)
|
||||
ff = n;
|
||||
@ -5807,11 +5826,17 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
||||
}
|
||||
);
|
||||
|
||||
--nnChannels;
|
||||
--ncChannels;
|
||||
|
||||
// When copying from mono to stereo track, paste the wave form
|
||||
// to both channels
|
||||
if (n->GetLinked() && !c->GetLinked())
|
||||
// TODO: more-than-two-channels
|
||||
// This will replicate the last pasted channel as many times as needed
|
||||
while (nnChannels > 0 && ncChannels == 0)
|
||||
{
|
||||
n = * ++ pN;
|
||||
--nnChannels;
|
||||
|
||||
n->TypeSwitch(
|
||||
[&](WaveTrack *wn){
|
||||
|
41
src/Mix.cpp
41
src/Mix.cpp
@ -49,29 +49,24 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
||||
uLeft.reset(), uRight.reset();
|
||||
|
||||
// This function was formerly known as "Quick Mix".
|
||||
const Track *t;
|
||||
bool mono = false; /* flag if output can be mono without loosing anything*/
|
||||
bool oneinput = false; /* flag set to true if there is only one input track
|
||||
(mono or stereo) */
|
||||
|
||||
TrackListIterator iter(tracks);
|
||||
SelectedTrackListOfKindIterator usefulIter(Track::Wave, tracks);
|
||||
const auto trackRange = tracks->Selected< const WaveTrack >();
|
||||
auto first = *trackRange.begin();
|
||||
// this only iterates tracks which are relevant to this function, i.e.
|
||||
// selected WaveTracks. The tracklist is (confusingly) the list of all
|
||||
// tracks in the project
|
||||
|
||||
int numWaves = 0; /* number of wave tracks in the selection */
|
||||
int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
|
||||
t = iter.First();
|
||||
while (t) {
|
||||
if (t->GetSelected() && t->GetKind() == Track::Wave) {
|
||||
for(auto wt : trackRange) {
|
||||
numWaves++;
|
||||
float pan = ((WaveTrack*)t)->GetPan();
|
||||
if (t->GetChannel() == Track::MonoChannel && pan == 0)
|
||||
float pan = wt->GetPan();
|
||||
if (wt->GetChannel() == Track::MonoChannel && pan == 0)
|
||||
numMono++;
|
||||
}
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
if (numMono == numWaves)
|
||||
mono = true;
|
||||
@ -88,13 +83,11 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
||||
double tstart, tend; // start and end times for one track.
|
||||
|
||||
WaveTrackConstArray waveArray;
|
||||
t = iter.First();
|
||||
|
||||
while (t) {
|
||||
if (t->GetSelected() && t->GetKind() == Track::Wave) {
|
||||
waveArray.push_back(Track::Pointer<const WaveTrack>(t));
|
||||
tstart = t->GetStartTime();
|
||||
tend = t->GetEndTime();
|
||||
for(auto wt : trackRange) {
|
||||
waveArray.push_back( Track::Pointer< const WaveTrack >( wt ) );
|
||||
tstart = wt->GetStartTime();
|
||||
tend = wt->GetEndTime();
|
||||
if (tend > mixEndTime)
|
||||
mixEndTime = tend;
|
||||
// try and get the start time. If the track is empty we will get 0,
|
||||
@ -111,22 +104,21 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
||||
} else if (tstart < mixStartTime)
|
||||
mixStartTime = tstart; // have a start, only make it smaller
|
||||
} // end if start and end are different
|
||||
} // end if track is a selected WaveTrack.
|
||||
/** @TODO: could we not use a SelectedTrackListOfKindIterator here? */
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
/* create the destination track (NEW track) */
|
||||
if ((numWaves == 1) || ((numWaves == 2) && (usefulIter.First()->GetLink() != NULL)))
|
||||
if (numWaves == TrackList::Channels(first).size())
|
||||
oneinput = true;
|
||||
// only one input track (either 1 mono or one linked stereo pair)
|
||||
|
||||
auto mixLeft = trackFactory->NewWaveTrack(format, rate);
|
||||
if (oneinput)
|
||||
mixLeft->SetName(usefulIter.First()->GetName()); /* set name of output track to be the same as the sole input track */
|
||||
mixLeft->SetName(first->GetName()); /* set name of output track to be the same as the sole input track */
|
||||
else
|
||||
mixLeft->SetName(_("Mix"));
|
||||
mixLeft->SetOffset(mixStartTime);
|
||||
|
||||
// TODO: more-than-two-channels
|
||||
decltype(mixLeft) mixRight{};
|
||||
if (mono) {
|
||||
mixLeft->SetChannel(Track::MonoChannel);
|
||||
@ -134,10 +126,11 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
||||
else {
|
||||
mixRight = trackFactory->NewWaveTrack(format, rate);
|
||||
if (oneinput) {
|
||||
if (usefulIter.First()->GetLink() != NULL) // we have linked track
|
||||
mixRight->SetName(usefulIter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/
|
||||
auto channels = TrackList::Channels(first);
|
||||
if (channels.size() > 1)
|
||||
mixRight->SetName((*channels.begin().advance(1))->GetName()); /* set name to match input track's right channel!*/
|
||||
else
|
||||
mixRight->SetName(usefulIter.First()->GetName()); /* set name to that of sole input channel */
|
||||
mixRight->SetName(first->GetName()); /* set name to that of sole input channel */
|
||||
}
|
||||
else
|
||||
mixRight->SetName(_("Mix"));
|
||||
|
@ -334,9 +334,10 @@ WaveTrack *MixerTrackCluster::GetWave() const
|
||||
|
||||
WaveTrack *MixerTrackCluster::GetRight() const
|
||||
{
|
||||
// TODO: more-than-two-channels
|
||||
auto left = GetWave();
|
||||
if (left)
|
||||
return static_cast<WaveTrack*>(left->GetLink());
|
||||
return * ++ TrackList::Channels(left).begin();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
@ -981,9 +982,9 @@ void MixerBoard::UpdateTrackClusters()
|
||||
size_t nClusterCount = mMixerTrackClusters.size();
|
||||
unsigned int nClusterIndex = 0;
|
||||
MixerTrackCluster* pMixerTrackCluster = NULL;
|
||||
Track* pTrack;
|
||||
|
||||
for (auto pPlayableTrack: mTracks->Leaders<PlayableTrack>()) {
|
||||
// TODO: more-than-two-channels
|
||||
auto spTrack = Track::Pointer<PlayableTrack>( pPlayableTrack );
|
||||
if (nClusterIndex < nClusterCount)
|
||||
{
|
||||
|
@ -703,16 +703,20 @@ void ScreenFrame::OnOneHour(wxCommandEvent & WXUNUSED(event))
|
||||
|
||||
void ScreenFrame::SizeTracks(int h)
|
||||
{
|
||||
TrackListIterator iter(mContext.GetProject()->GetTracks());
|
||||
for (Track * t = iter.First(); t; t = iter.Next()) {
|
||||
if (t->GetKind() == Track::Wave) {
|
||||
if (t->GetLink()) {
|
||||
t->SetHeight(h);
|
||||
}
|
||||
else {
|
||||
t->SetHeight(h*2);
|
||||
}
|
||||
}
|
||||
// h is the height for a channel
|
||||
// Set the height of a mono track twice as high
|
||||
|
||||
// TODO: more-than-two-channels
|
||||
// If there should be more-than-stereo tracks, this makes
|
||||
// each channel as high as for a stereo channel
|
||||
|
||||
auto tracks = mContext.GetProject()->GetTracks();
|
||||
for (auto t : tracks->Leaders<WaveTrack>()) {
|
||||
auto channels = TrackList::Channels(t);
|
||||
auto nChannels = channels.size();
|
||||
auto height = nChannels == 1 ? 2 * h : h;
|
||||
for (auto channel : channels)
|
||||
channel->SetHeight(height);
|
||||
}
|
||||
mContext.GetProject()->RedrawProject();
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ Paul Licameli split from TrackPanel.cpp
|
||||
|
||||
HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked)
|
||||
{
|
||||
// TODO: more-than-two-channels-message
|
||||
|
||||
static wxCursor resizeCursor{ wxCURSOR_SIZENS };
|
||||
|
||||
/// When in the resize area we can adjust size or relative size.
|
||||
@ -59,41 +61,35 @@ UIHandle::Result TrackPanelResizeHandle::Click
|
||||
}
|
||||
|
||||
TrackPanelResizeHandle::TrackPanelResizeHandle
|
||||
( const std::shared_ptr<Track> &track, int y, const AudacityProject *pProject )
|
||||
( const std::shared_ptr<Track> &track, int y )
|
||||
: mpTrack{ track }
|
||||
, mMouseClickY( y )
|
||||
{
|
||||
auto tracks = pProject->GetTracks();
|
||||
auto prev = * -- tracks->Find(track.get());
|
||||
auto next = * ++ tracks->Find(track.get());
|
||||
// TODO: more-than-two-channels
|
||||
|
||||
//STM: Determine whether we should rescale one or two tracks
|
||||
if (prev && prev->GetLink() == track.get()) {
|
||||
// mpTrack is the lower track
|
||||
mInitialTrackHeight = track->GetHeight();
|
||||
mInitialActualHeight = track->GetActualHeight();
|
||||
mInitialMinimized = track->GetMinimized();
|
||||
mInitialUpperTrackHeight = prev->GetHeight();
|
||||
mInitialUpperActualHeight = prev->GetActualHeight();
|
||||
auto channels = TrackList::Channels(track.get());
|
||||
auto last = *channels.rbegin();
|
||||
mInitialTrackHeight = last->GetHeight();
|
||||
mInitialActualHeight = last->GetActualHeight();
|
||||
mInitialMinimized = last->GetMinimized();
|
||||
|
||||
if (channels.size() > 1) {
|
||||
auto first = *channels.begin();
|
||||
|
||||
mInitialUpperTrackHeight = first->GetHeight();
|
||||
mInitialUpperActualHeight = first->GetActualHeight();
|
||||
|
||||
if (track.get() == *channels.rbegin())
|
||||
// capturedTrack is the lowest track
|
||||
mMode = IsResizingBelowLinkedTracks;
|
||||
}
|
||||
else if (next && track->GetLink() == next) {
|
||||
// mpTrack is the upper track
|
||||
mInitialTrackHeight = next->GetHeight();
|
||||
mInitialActualHeight = next->GetActualHeight();
|
||||
mInitialMinimized = next->GetMinimized();
|
||||
mInitialUpperTrackHeight = track->GetHeight();
|
||||
mInitialUpperActualHeight = track->GetActualHeight();
|
||||
else
|
||||
// capturedTrack is not the lowest track
|
||||
mMode = IsResizingBetweenLinkedTracks;
|
||||
}
|
||||
else {
|
||||
// DM: Save the initial mouse location and the initial height
|
||||
mInitialTrackHeight = track->GetHeight();
|
||||
mInitialActualHeight = track->GetActualHeight();
|
||||
mInitialMinimized = track->GetMinimized();
|
||||
else
|
||||
mMode = IsResizing;
|
||||
}
|
||||
}
|
||||
|
||||
UIHandle::Result TrackPanelResizeHandle::Drag
|
||||
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
|
||||
@ -113,23 +109,24 @@ UIHandle::Result TrackPanelResizeHandle::Drag
|
||||
// This used to be in HandleResizeClick(), but simply clicking
|
||||
// on a resize border would switch the minimized state.
|
||||
if (pTrack->GetMinimized()) {
|
||||
Track *link = pTrack->GetLink();
|
||||
auto channels = TrackList::Channels( pTrack.get() );
|
||||
for (auto channel : channels) {
|
||||
channel->SetHeight(channel->GetHeight());
|
||||
channel->SetMinimized(false);
|
||||
}
|
||||
|
||||
pTrack->SetHeight(pTrack->GetHeight());
|
||||
pTrack->SetMinimized(false);
|
||||
|
||||
if (link) {
|
||||
link->SetHeight(link->GetHeight());
|
||||
link->SetMinimized(false);
|
||||
if (channels.size() > 1) {
|
||||
// Initial values must be reset since they weren't based on the
|
||||
// minimized heights.
|
||||
mInitialUpperTrackHeight = link->GetHeight();
|
||||
mInitialTrackHeight = pTrack->GetHeight();
|
||||
mInitialUpperTrackHeight = (*channels.begin())->GetHeight();
|
||||
mInitialTrackHeight = (*channels.rbegin())->GetHeight();
|
||||
}
|
||||
}
|
||||
|
||||
// Common pieces of code for MONO_WAVE_PAN and otherwise.
|
||||
auto doResizeBelow = [&] (Track *prev, bool WXUNUSED(vStereo)) {
|
||||
// TODO: more-than-two-channels
|
||||
|
||||
double proportion = static_cast < double >(mInitialTrackHeight)
|
||||
/ (mInitialTrackHeight + mInitialUpperTrackHeight);
|
||||
|
||||
@ -150,6 +147,8 @@ UIHandle::Result TrackPanelResizeHandle::Drag
|
||||
};
|
||||
|
||||
auto doResizeBetween = [&] (Track *next, bool WXUNUSED(vStereo)) {
|
||||
// TODO: more-than-two-channels
|
||||
|
||||
int newUpperTrackHeight = mInitialUpperTrackHeight + delta;
|
||||
int newTrackHeight = mInitialTrackHeight - delta;
|
||||
|
||||
|
@ -22,9 +22,7 @@ class TrackPanelResizeHandle final : public UIHandle
|
||||
TrackPanelResizeHandle(const TrackPanelResizeHandle&) = delete;
|
||||
|
||||
public:
|
||||
explicit TrackPanelResizeHandle
|
||||
( const std::shared_ptr<Track> &pTrack, int y,
|
||||
const AudacityProject *pProject );
|
||||
explicit TrackPanelResizeHandle( const std::shared_ptr<Track> &pTrack, int y );
|
||||
|
||||
TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = default;
|
||||
|
||||
|
@ -28,7 +28,7 @@ std::vector<UIHandlePtr> TrackPanelResizerCell::HitTest
|
||||
auto pTrack = mpTrack.lock();
|
||||
if (pTrack) {
|
||||
auto result = std::make_shared<TrackPanelResizeHandle>(
|
||||
pTrack, st.state.m_y, pProject );
|
||||
pTrack, st.state.m_y );
|
||||
result = AssignUIHandlePtr(mResizeHandle, result);
|
||||
results.push_back(result);
|
||||
}
|
||||
|
@ -1359,10 +1359,9 @@ bool Effect::ProcessPass()
|
||||
if (!left->GetSelected())
|
||||
return fallthrough();
|
||||
|
||||
WaveTrack *right;
|
||||
sampleCount len;
|
||||
sampleCount leftStart;
|
||||
sampleCount rightStart;
|
||||
sampleCount rightStart = 0;
|
||||
|
||||
if (!isGenerator)
|
||||
{
|
||||
@ -1376,48 +1375,37 @@ bool Effect::ProcessPass()
|
||||
mSampleCnt = left->TimeToLongSamples(mDuration);
|
||||
}
|
||||
|
||||
mNumChannels = 1;
|
||||
mNumChannels = 0;
|
||||
WaveTrack *right{};
|
||||
|
||||
if (left->GetChannel() == Track::LeftChannel)
|
||||
{
|
||||
map[0] = ChannelNameFrontLeft;
|
||||
}
|
||||
else if (left->GetChannel() == Track::RightChannel)
|
||||
{
|
||||
map[0] = ChannelNameFrontRight;
|
||||
}
|
||||
// Iterate either over one track which could be any channel,
|
||||
// or if multichannel, then over all channels of left,
|
||||
// which is a leader.
|
||||
for (auto channel :
|
||||
TrackList::Channels(left).StartingWith(left)) {
|
||||
if (channel->GetChannel() == Track::LeftChannel)
|
||||
map[mNumChannels] = ChannelNameFrontLeft;
|
||||
else if (channel->GetChannel() == Track::RightChannel)
|
||||
map[mNumChannels] = ChannelNameFrontRight;
|
||||
else
|
||||
{
|
||||
map[0] = ChannelNameMono;
|
||||
}
|
||||
map[1] = ChannelNameEOL;
|
||||
map[mNumChannels] = ChannelNameMono;
|
||||
|
||||
right = NULL;
|
||||
rightStart = 0;
|
||||
if (left->GetLinked() && multichannel)
|
||||
{
|
||||
// Assume linked track is wave
|
||||
right = static_cast<WaveTrack *>(left->GetLink());
|
||||
if (!isGenerator)
|
||||
{
|
||||
GetSamples(right, &rightStart, &len);
|
||||
}
|
||||
++ mNumChannels;
|
||||
map[mNumChannels] = ChannelNameEOL;
|
||||
|
||||
if (! multichannel)
|
||||
break;
|
||||
|
||||
if (mNumChannels == 2) {
|
||||
// TODO: more-than-two-channels
|
||||
right = channel;
|
||||
clear = false;
|
||||
mNumChannels = 2;
|
||||
if (!isGenerator)
|
||||
GetSamples(right, &rightStart, &len);
|
||||
|
||||
if (right->GetChannel() == Track::LeftChannel)
|
||||
{
|
||||
map[1] = ChannelNameFrontLeft;
|
||||
// Ignore other channels
|
||||
break;
|
||||
}
|
||||
else if (right->GetChannel() == Track::RightChannel)
|
||||
{
|
||||
map[1] = ChannelNameFrontRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
map[1] = ChannelNameMono;
|
||||
}
|
||||
map[2] = ChannelNameEOL;
|
||||
}
|
||||
|
||||
// Let the client know the sample rate
|
||||
|
@ -75,24 +75,20 @@ bool EffectStereoToMono::Process()
|
||||
this->CopyInputTracks(); // Set up mOutputTracks.
|
||||
bool bGoodResult = true;
|
||||
|
||||
auto trackRange = mOutputTracks->Selected< WaveTrack >();
|
||||
mLeftTrack = *trackRange.first;
|
||||
auto trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
|
||||
bool refreshIter = false;
|
||||
|
||||
if(mLeftTrack)
|
||||
{
|
||||
// create a NEW WaveTrack to hold all of the output
|
||||
AudacityProject *p = GetActiveProject();
|
||||
mOutTrack = p->GetTrackFactory()->NewWaveTrack(floatSample, mLeftTrack->GetRate());
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while ( trackRange.first != trackRange.second ) {
|
||||
mLeftTrack = *trackRange.first;
|
||||
if (mLeftTrack->GetLinked()) {
|
||||
auto channels = TrackList::Channels( mLeftTrack );
|
||||
if (channels.size() != 2) {
|
||||
// TODO: more-than-two-channels
|
||||
++ trackRange.first;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Assume linked track is wave
|
||||
mRightTrack = * ++ trackRange.first;
|
||||
mRightTrack = * channels.rbegin();
|
||||
|
||||
if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) {
|
||||
auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime());
|
||||
@ -107,15 +103,12 @@ bool EffectStereoToMono::Process()
|
||||
if (!bGoodResult)
|
||||
break;
|
||||
|
||||
mOutTrack->Clear(mOutTrack->GetStartTime(), mOutTrack->GetEndTime());
|
||||
|
||||
// The right channel has been deleted, so we must restart from the beginning
|
||||
refreshIter = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshIter) {
|
||||
trackRange = mOutputTracks->Selected< WaveTrack >();
|
||||
trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
|
||||
refreshIter = false;
|
||||
}
|
||||
else
|
||||
@ -128,11 +121,6 @@ bool EffectStereoToMono::Process()
|
||||
return bGoodResult;
|
||||
}
|
||||
|
||||
void EffectStereoToMono::End()
|
||||
{
|
||||
mOutTrack.reset();
|
||||
}
|
||||
|
||||
bool EffectStereoToMono::ProcessOne(int count)
|
||||
{
|
||||
float curLeftFrame;
|
||||
@ -145,6 +133,10 @@ bool EffectStereoToMono::ProcessOne(int count)
|
||||
Floats rightBuffer{ idealBlockLen };
|
||||
bool bResult = true;
|
||||
|
||||
AudacityProject *p = GetActiveProject();
|
||||
auto outTrack =
|
||||
p->GetTrackFactory()->NewWaveTrack(floatSample, mLeftTrack->GetRate());
|
||||
|
||||
while (index < mEnd) {
|
||||
bResult &= mLeftTrack->Get((samplePtr)leftBuffer.get(), floatSample, index, idealBlockLen);
|
||||
bResult &= mRightTrack->Get((samplePtr)rightBuffer.get(), floatSample, index, idealBlockLen);
|
||||
@ -156,15 +148,15 @@ bool EffectStereoToMono::ProcessOne(int count)
|
||||
curMonoFrame = (curLeftFrame + curRightFrame) / 2.0;
|
||||
leftBuffer[i] = curMonoFrame;
|
||||
}
|
||||
mOutTrack->Append((samplePtr)leftBuffer.get(), floatSample, limit);
|
||||
outTrack->Append((samplePtr)leftBuffer.get(), floatSample, limit);
|
||||
if (TrackProgress(count, 2.*(index.as_double() / (mEnd - mStart).as_double())))
|
||||
return false;
|
||||
}
|
||||
|
||||
double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime());
|
||||
mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime());
|
||||
mOutTrack->Flush();
|
||||
mLeftTrack->Paste(minStart, mOutTrack.get());
|
||||
outTrack->Flush();
|
||||
mLeftTrack->Paste(minStart, outTrack.get());
|
||||
mLeftTrack->SetLinked(false);
|
||||
mRightTrack->SetLinked(false);
|
||||
mLeftTrack->SetChannel(Track::MonoChannel);
|
||||
|
@ -41,7 +41,6 @@ public:
|
||||
// Effect implementation
|
||||
|
||||
bool Process() override;
|
||||
void End() override;
|
||||
bool IsHidden() override;
|
||||
|
||||
private:
|
||||
@ -54,7 +53,6 @@ private:
|
||||
sampleCount mEnd;
|
||||
WaveTrack *mLeftTrack;
|
||||
WaveTrack *mRightTrack;
|
||||
std::unique_ptr<WaveTrack> mOutTrack;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -769,7 +769,7 @@ bool NyquistEffect::Process()
|
||||
Effect::MessageBox(message, wxOK | wxCENTRE | wxICON_EXCLAMATION, _("Nyquist Error"));
|
||||
}
|
||||
|
||||
auto trackRange = mOutputTracks->Selected< WaveTrack >();
|
||||
auto trackRange = mOutputTracks->Selected< WaveTrack >() + &Track::IsLeader;
|
||||
|
||||
// Keep track of whether the current track is first selected in its sync-lock group
|
||||
// (we have no idea what the length of the returned audio will be, so we have
|
||||
@ -785,10 +785,14 @@ bool NyquistEffect::Process()
|
||||
if (bOnePassTool) {
|
||||
}
|
||||
else {
|
||||
if (mCurTrack[0]->GetLinked()) {
|
||||
auto channels = TrackList::Channels(mCurTrack[0]);
|
||||
if (channels.size() > 1) {
|
||||
// TODO: more-than-two-channels
|
||||
// Pay attention to consistency of mNumSelectedChannels
|
||||
// with the running tally made by this loop!
|
||||
mCurNumChannels = 2;
|
||||
|
||||
mCurTrack[1] = * ++ iter;
|
||||
mCurTrack[1] = * ++ channels.first;
|
||||
if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) {
|
||||
Effect::MessageBox(_("Sorry, cannot apply effect on stereo tracks where the tracks don't match."),
|
||||
wxOK | wxCENTRE);
|
||||
|
@ -562,6 +562,8 @@ protected:
|
||||
void OnChannelChange(wxCommandEvent & event);
|
||||
void OnMergeStereo(wxCommandEvent & event);
|
||||
|
||||
// TODO: more-than-two-channels
|
||||
// How should we define generalized channel manipulation operations?
|
||||
void SplitStereo(bool stereo);
|
||||
|
||||
void OnSwapChannels(wxCommandEvent & event);
|
||||
@ -603,23 +605,28 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
|
||||
(display == WaveTrack::Spectrum) && !bAudioBusy);
|
||||
|
||||
AudacityProject *const project = ::GetActiveProject();
|
||||
TrackList *const tracks = project->GetTracks();
|
||||
bool unsafe = EffectManager::Get().RealtimeIsActive() &&
|
||||
project->IsAudioActive();
|
||||
|
||||
const bool isMono = !pTrack->GetLink();
|
||||
auto nChannels = TrackList::Channels(pTrack).size();
|
||||
const bool isMono = ( nChannels == 1 );
|
||||
const bool isStereo = ( nChannels == 2 );
|
||||
// Maybe more than stereo tracks some time?
|
||||
|
||||
if ( isMono )
|
||||
{
|
||||
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
|
||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||
|
||||
TrackList *const tracks = project->GetTracks();
|
||||
auto next = * ++ tracks->Find(pTrack);
|
||||
|
||||
if (isMono) {
|
||||
const bool canMakeStereo =
|
||||
(next && !next->GetLinked()
|
||||
&& pTrack->GetKind() == Track::Wave
|
||||
&& next->GetKind() == Track::Wave);
|
||||
(next &&
|
||||
TrackList::Channels(next).size() == 1 &&
|
||||
track_cast<WaveTrack*>(next));
|
||||
|
||||
pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
|
||||
|
||||
int itemId;
|
||||
@ -647,8 +654,8 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
|
||||
return end != std::find(checkedIds.begin(), end, id);
|
||||
});
|
||||
|
||||
|
||||
pMenu->Enable(OnSwapChannelsID, !isMono && !unsafe);
|
||||
// Enable this only for properly stereo tracks:
|
||||
pMenu->Enable(OnSwapChannelsID, isStereo && !unsafe);
|
||||
pMenu->Enable(OnSplitStereoID, !isMono && !unsafe);
|
||||
|
||||
#ifndef EXPERIMENTAL_DA
|
||||
@ -836,11 +843,16 @@ void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
|
||||
/// Merge two tracks into one stereo track ??
|
||||
void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
||||
{
|
||||
AudacityProject *const project = ::GetActiveProject();
|
||||
const auto tracks = project->GetTracks();
|
||||
|
||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||
wxASSERT(pTrack);
|
||||
|
||||
auto partner = static_cast< WaveTrack * >
|
||||
( *tracks->Find( pTrack ).advance( 1 ) );
|
||||
|
||||
pTrack->SetLinked(true);
|
||||
// Assume partner is wave or null
|
||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
||||
|
||||
if (partner) {
|
||||
// Set partner's parameters to match target.
|
||||
@ -884,51 +896,40 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
||||
mpData->result = RefreshCode::RefreshAll;
|
||||
}
|
||||
|
||||
/// Split a stereo track into two tracks...
|
||||
/// Split a stereo track (or more-than-stereo?) into two (or more) tracks...
|
||||
void WaveTrackMenuTable::SplitStereo(bool stereo)
|
||||
{
|
||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||
wxASSERT(pTrack);
|
||||
AudacityProject *const project = ::GetActiveProject();
|
||||
auto channels = TrackList::Channels( pTrack );
|
||||
|
||||
if (stereo)
|
||||
pTrack->SetPanFromChannelType();
|
||||
pTrack->SetChannel(Track::MonoChannel);
|
||||
|
||||
// Assume partner is present, and is wave
|
||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
||||
wxASSERT(partner);
|
||||
if (!partner)
|
||||
return;
|
||||
|
||||
if (partner)
|
||||
{
|
||||
int totalHeight = 0;
|
||||
int nChannels = 0;
|
||||
for (auto channel : channels) {
|
||||
// Keep original stereo track name.
|
||||
partner->SetName(pTrack->GetName());
|
||||
channel->SetName(pTrack->GetName());
|
||||
if (stereo)
|
||||
partner->SetPanFromChannelType();
|
||||
partner->SetChannel(Track::MonoChannel);
|
||||
channel->SetPanFromChannelType();
|
||||
channel->SetChannel(Track::MonoChannel);
|
||||
|
||||
//On Demand - have each channel add its own.
|
||||
if (ODManager::IsInstanceCreated() && partner->GetKind() == Track::Wave)
|
||||
ODManager::Instance()->MakeWaveTrackIndependent(partner);
|
||||
if (ODManager::IsInstanceCreated())
|
||||
ODManager::Instance()->MakeWaveTrackIndependent(channel);
|
||||
//make sure no channel is smaller than its minimum height
|
||||
if (channel->GetHeight() < channel->GetMinimizedHeight())
|
||||
channel->SetHeight(channel->GetMinimizedHeight());
|
||||
totalHeight += channel->GetHeight();
|
||||
++nChannels;
|
||||
}
|
||||
|
||||
pTrack->SetLinked(false);
|
||||
//make sure neither track is smaller than its minimum height
|
||||
if (pTrack->GetHeight() < pTrack->GetMinimizedHeight())
|
||||
pTrack->SetHeight(pTrack->GetMinimizedHeight());
|
||||
if (partner)
|
||||
{
|
||||
if (partner->GetHeight() < partner->GetMinimizedHeight())
|
||||
partner->SetHeight(partner->GetMinimizedHeight());
|
||||
int averageHeight = totalHeight / nChannels;
|
||||
|
||||
for (auto channel : channels)
|
||||
// Make tracks the same height
|
||||
if (pTrack->GetHeight() != partner->GetHeight())
|
||||
{
|
||||
pTrack->SetHeight((pTrack->GetHeight() + partner->GetHeight()) / 2.0);
|
||||
partner->SetHeight(pTrack->GetHeight());
|
||||
}
|
||||
}
|
||||
channel->SetHeight( averageHeight );
|
||||
|
||||
mpData->result = RefreshCode::RefreshAll;
|
||||
}
|
||||
@ -939,14 +940,19 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
||||
AudacityProject *const project = ::GetActiveProject();
|
||||
|
||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||
// Assume partner is wave or null
|
||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
||||
auto channels = TrackList::Channels( pTrack );
|
||||
if (channels.size() != 2)
|
||||
return;
|
||||
|
||||
Track *const focused = project->GetTrackPanel()->GetFocusedTrack();
|
||||
const bool hasFocus =
|
||||
(focused == pTrack || focused == partner);
|
||||
const bool hasFocus = channels.contains( focused );
|
||||
|
||||
auto first = *channels.begin();
|
||||
auto partner = *channels.rbegin();
|
||||
|
||||
SplitStereo(false);
|
||||
pTrack->SetChannel(Track::RightChannel);
|
||||
|
||||
first->SetChannel(Track::RightChannel);
|
||||
partner->SetChannel(Track::LeftChannel);
|
||||
|
||||
TrackList *const tracks = project->GetTracks();
|
||||
|
Loading…
x
Reference in New Issue
Block a user