mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-19 22:27:43 +02:00
Rewrite channel iterations that don't generalize to more than two...
... requiring at least some message rewrites. All these places have a comment: // TODO: more-than-two-channels or in simpler cases just // TODO: more-than-two-channels-message
This commit is contained in:
commit
7d55defde6
@ -1891,20 +1891,17 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
// group determination should mimic what is done in audacityAudioCallback()
|
// group determination should mimic what is done in audacityAudioCallback()
|
||||||
// when calling RealtimeProcess().
|
// when calling RealtimeProcess().
|
||||||
int group = 0;
|
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();
|
const WaveTrack *vt = mPlaybackTracks[i].get();
|
||||||
|
|
||||||
unsigned chanCnt = 1;
|
// TODO: more-than-two-channels
|
||||||
if (vt->GetLinked())
|
unsigned chanCnt = TrackList::Channels(vt).size();
|
||||||
{
|
i += chanCnt;
|
||||||
i++;
|
|
||||||
chanCnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup for realtime playback at the rate of the realtime
|
// Setup for realtime playback at the rate of the realtime
|
||||||
// stream, not the rate of the track.
|
// 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 drop = false;
|
||||||
bool dropQuickly = false;
|
bool dropQuickly = false;
|
||||||
bool linkFlag = false;
|
|
||||||
for (unsigned t = 0; t < numPlaybackTracks; t++)
|
for (unsigned t = 0; t < numPlaybackTracks; t++)
|
||||||
{
|
{
|
||||||
WaveTrack *vt = mPlaybackTracks[t].get();
|
WaveTrack *vt = mPlaybackTracks[t].get();
|
||||||
|
|
||||||
chans[chanCnt] = vt;
|
chans[chanCnt] = vt;
|
||||||
|
|
||||||
if ( linkFlag ) {
|
// TODO: more-than-two-channels
|
||||||
linkFlag = false;
|
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 );
|
dropQuickly = dropQuickly && doneMicrofading( *vt );
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
drop = dropTrack( *vt );
|
drop = dropTrack( *vt );
|
||||||
|
|
||||||
linkFlag = vt->GetLinked();
|
|
||||||
selected = vt->GetSelected();
|
selected = vt->GetSelected();
|
||||||
|
|
||||||
// If we have a mono track, clear the right channel
|
if ( lastChannel ) {
|
||||||
if (!linkFlag)
|
// TODO: more-than-two-channels
|
||||||
|
#if 1
|
||||||
|
// If we have a mono track, clear the right channel
|
||||||
memset(tempBufs[1], 0, framesPerBuffer * sizeof(float));
|
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 );
|
dropQuickly = drop && doneMicrofading( *vt );
|
||||||
}
|
}
|
||||||
@ -5055,7 +5064,7 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
maxLen = std::max(maxLen, len);
|
maxLen = std::max(maxLen, len);
|
||||||
|
|
||||||
|
|
||||||
if (linkFlag)
|
if ( ! lastChannel )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
179
src/Menus.cpp
179
src/Menus.cpp
@ -2318,13 +2318,12 @@ CommandFlag MenuCommandHandler::GetUpdateFlags
|
|||||||
flags |= PlayableTracksExistFlag;
|
flags |= PlayableTracksExistFlag;
|
||||||
if (t->GetSelected()) {
|
if (t->GetSelected()) {
|
||||||
flags |= TracksSelectedFlag;
|
flags |= TracksSelectedFlag;
|
||||||
if (t->GetLinked()) {
|
// TODO: more-than-two-channels
|
||||||
|
if (TrackList::Channels(t).size() > 1) {
|
||||||
flags |= StereoRequiredFlag;
|
flags |= StereoRequiredFlag;
|
||||||
}
|
}
|
||||||
else {
|
flags |= WaveTracksSelectedFlag;
|
||||||
flags |= WaveTracksSelectedFlag;
|
flags |= AudioTracksSelectedFlag;
|
||||||
flags |= AudioTracksSelectedFlag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if( t->GetEndTime() > t->GetStartTime() )
|
if( t->GetEndTime() > t->GetStartTime() )
|
||||||
flags |= HasWaveDataFlag;
|
flags |= HasWaveDataFlag;
|
||||||
@ -3856,13 +3855,14 @@ double MenuCommandHandler::OnClipMove
|
|||||||
|
|
||||||
auto t0 = selectedRegion.t0();
|
auto t0 = selectedRegion.t0();
|
||||||
|
|
||||||
state.capturedClip = wt->GetClipAtTime( t0 );
|
// Find the first channel that has a clip at time t0
|
||||||
if (state.capturedClip == nullptr && track->GetLinked() && track->GetLink()) {
|
for (auto channel : TrackList::Channels(wt) ) {
|
||||||
// the clips in the right channel may be different from the left
|
if( nullptr != (state.capturedClip = channel->GetClipAtTime( t0 )) ) {
|
||||||
track = track->GetLink();
|
wt = channel;
|
||||||
wt = static_cast<WaveTrack*>(track);
|
break;
|
||||||
state.capturedClip = wt->GetClipAtTime(t0);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.capturedClip == nullptr)
|
if (state.capturedClip == nullptr)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
@ -5709,6 +5709,7 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
|||||||
bool bPastedSomething = false;
|
bool bPastedSomething = false;
|
||||||
|
|
||||||
auto pC = clipTrackRange.begin();
|
auto pC = clipTrackRange.begin();
|
||||||
|
size_t nnChannels, ncChannels;
|
||||||
while (*pN && *pC) {
|
while (*pN && *pC) {
|
||||||
auto n = *pN;
|
auto n = *pN;
|
||||||
auto c = *pC;
|
auto c = *pC;
|
||||||
@ -5763,15 +5764,34 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
|||||||
_("Pasting one type of track into another is not allowed.")
|
_("Pasting one type of track into another is not allowed.")
|
||||||
};
|
};
|
||||||
|
|
||||||
// When trying to copy from stereo to mono track, show error and exit
|
// We should need this check only each time we visit the leading
|
||||||
// TODO: Automatically offer user to mix down to mono (unfortunately
|
// channel
|
||||||
// this is not easy to implement
|
if ( n->IsLeader() ) {
|
||||||
if (c->GetLinked() && !n->GetLinked())
|
wxASSERT( c->IsLeader() ); // the iteration logic should ensure this
|
||||||
// Throw, so that any previous changes to the project in this loop
|
|
||||||
// are discarded.
|
auto cChannels = TrackList::Channels(c);
|
||||||
throw SimpleMessageBoxException{
|
ncChannels = cChannels.size();
|
||||||
_("Copying stereo audio into a mono track is not allowed.")
|
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 (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)
|
if (!ff)
|
||||||
ff = n;
|
ff = n;
|
||||||
@ -5806,11 +5826,17 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
--nnChannels;
|
||||||
|
--ncChannels;
|
||||||
|
|
||||||
// When copying from mono to stereo track, paste the wave form
|
// When copying from mono to stereo track, paste the wave form
|
||||||
// to both channels
|
// 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;
|
n = * ++ pN;
|
||||||
|
--nnChannels;
|
||||||
|
|
||||||
n->TypeSwitch(
|
n->TypeSwitch(
|
||||||
[&](WaveTrack *wn){
|
[&](WaveTrack *wn){
|
||||||
@ -5826,7 +5852,7 @@ void MenuCommandHandler::OnPaste(const CommandContext &context)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bAdvanceClipboard){
|
if (bAdvanceClipboard) {
|
||||||
prevClip = c;
|
prevClip = c;
|
||||||
c = * ++ pC;
|
c = * ++ pC;
|
||||||
}
|
}
|
||||||
@ -6906,34 +6932,29 @@ int MenuCommandHandler::FindClips
|
|||||||
std::vector<FoundClip> results;
|
std::vector<FoundClip> results;
|
||||||
|
|
||||||
int nTracksSearched = 0;
|
int nTracksSearched = 0;
|
||||||
int trackNum = 1;
|
auto leaders = tracks->Leaders();
|
||||||
for (auto track : tracks->Leaders()) {
|
auto range = leaders.Filter<const WaveTrack>();
|
||||||
if (track->GetKind() == Track::Wave && (!anyWaveTracksSelected || track->GetSelected())) {
|
if (anyWaveTracksSelected)
|
||||||
auto waveTrack = static_cast<const WaveTrack*>(track);
|
range = range + &Track::GetSelected;
|
||||||
bool stereoAndDiff = waveTrack->GetLinked() && !ChannelsHaveSameClipBoundaries(waveTrack);
|
for (auto waveTrack : range) {
|
||||||
|
bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack);
|
||||||
|
|
||||||
auto result = next ? FindNextClip(project, waveTrack, t0, t1) :
|
auto range = stereoAndDiff
|
||||||
FindPrevClip(project, waveTrack, t0, t1);
|
? TrackList::Channels( waveTrack )
|
||||||
|
: TrackList::SingletonRange( waveTrack );
|
||||||
|
|
||||||
|
for ( auto wt : range ) {
|
||||||
|
auto result = next ? FindNextClip(project, wt, t0, t1) :
|
||||||
|
FindPrevClip(project, wt, t0, t1);
|
||||||
if (result.found) {
|
if (result.found) {
|
||||||
result.trackNum = trackNum;
|
result.trackNum =
|
||||||
|
1 + std::distance( leaders.begin(), leaders.find( waveTrack ) );
|
||||||
result.channel = stereoAndDiff;
|
result.channel = stereoAndDiff;
|
||||||
results.push_back(result);
|
results.push_back(result);
|
||||||
}
|
}
|
||||||
if (stereoAndDiff) {
|
|
||||||
auto waveTrack2 = static_cast<const WaveTrack*>(track->GetLink());
|
|
||||||
auto result = next ? FindNextClip(project, waveTrack2, t0, t1) :
|
|
||||||
FindPrevClip(project, waveTrack2, t0, t1);
|
|
||||||
if (result.found) {
|
|
||||||
result.trackNum = trackNum;
|
|
||||||
result.channel = stereoAndDiff;
|
|
||||||
results.push_back(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nTracksSearched++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trackNum++;
|
nTracksSearched++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6974,14 +6995,13 @@ int MenuCommandHandler::FindClips
|
|||||||
return nTracksSearched; // can be used for screen reader messages if required
|
return nTracksSearched; // can be used for screen reader messages if required
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether the two channels of a stereo track have the same clips
|
namespace {
|
||||||
bool MenuCommandHandler::ChannelsHaveSameClipBoundaries(const WaveTrack* wt)
|
bool TwoChannelsHaveSameBoundaries
|
||||||
{
|
( const WaveTrack *first, const WaveTrack *second )
|
||||||
bool sameClips = false;
|
{
|
||||||
|
bool sameClips = false;
|
||||||
if (wt->GetLinked() && wt->GetLink()) {
|
auto& left = first->GetClips();
|
||||||
auto& left = wt->GetClips();
|
auto& right = second->GetClips();
|
||||||
auto& right = static_cast<const WaveTrack*>(wt->GetLink())->GetClips();
|
|
||||||
if (left.size() == right.size()) {
|
if (left.size() == right.size()) {
|
||||||
sameClips = true;
|
sameClips = true;
|
||||||
for (unsigned int i = 0; i < left.size(); i++) {
|
for (unsigned int i = 0; i < left.size(); i++) {
|
||||||
@ -6992,9 +7012,24 @@ bool MenuCommandHandler::ChannelsHaveSameClipBoundaries(const WaveTrack* wt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return sameClips;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuCommandHandler::ChannelsHaveDifferentClipBoundaries(
|
||||||
|
const WaveTrack* wt)
|
||||||
|
{
|
||||||
|
// This is quadratic in the number of channels
|
||||||
|
auto channels = TrackList::Channels(wt);
|
||||||
|
while (!channels.empty()) {
|
||||||
|
auto channel = *channels.first++;
|
||||||
|
for (auto other : channels) {
|
||||||
|
if (!TwoChannelsHaveSameBoundaries(channel, other))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sameClips;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuCommandHandler::OnSelectPrevClip(const CommandContext &context)
|
void MenuCommandHandler::OnSelectPrevClip(const CommandContext &context)
|
||||||
@ -8120,34 +8155,29 @@ int MenuCommandHandler::FindClipBoundaries
|
|||||||
std::vector<FoundClipBoundary> results;
|
std::vector<FoundClipBoundary> results;
|
||||||
|
|
||||||
int nTracksSearched = 0;
|
int nTracksSearched = 0;
|
||||||
int trackNum = 1;
|
auto leaders = tracks->Leaders();
|
||||||
for (auto track : tracks->Leaders()) {
|
auto range = leaders.Filter<const WaveTrack>();
|
||||||
if (track->GetKind() == Track::Wave && (!anyWaveTracksSelected || track->GetSelected())) {
|
if (anyWaveTracksSelected)
|
||||||
auto waveTrack = static_cast<const WaveTrack*>(track);
|
range = range + &Track::GetSelected;
|
||||||
bool stereoAndDiff = waveTrack->GetLinked() && !ChannelsHaveSameClipBoundaries(waveTrack);
|
for (auto waveTrack : range) {
|
||||||
|
bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack);
|
||||||
|
|
||||||
auto result = next ? FindNextClipBoundary(waveTrack, time) :
|
auto range = stereoAndDiff
|
||||||
FindPrevClipBoundary(waveTrack, time);
|
? TrackList::Channels( waveTrack )
|
||||||
|
: TrackList::SingletonRange(waveTrack);
|
||||||
|
|
||||||
|
for (auto wt : range) {
|
||||||
|
auto result = next ? FindNextClipBoundary(wt, time) :
|
||||||
|
FindPrevClipBoundary(wt, time);
|
||||||
if (result.nFound > 0) {
|
if (result.nFound > 0) {
|
||||||
result.trackNum = trackNum;
|
result.trackNum =
|
||||||
|
1 + std::distance( leaders.begin(), leaders.find( waveTrack ) );
|
||||||
result.channel = stereoAndDiff;
|
result.channel = stereoAndDiff;
|
||||||
results.push_back(result);
|
results.push_back(result);
|
||||||
}
|
}
|
||||||
if (stereoAndDiff) {
|
|
||||||
waveTrack = static_cast<const WaveTrack*>(track->GetLink());
|
|
||||||
result = next ? FindNextClipBoundary(waveTrack, time) :
|
|
||||||
FindPrevClipBoundary(waveTrack, time);
|
|
||||||
if (result.nFound > 0) {
|
|
||||||
result.trackNum = trackNum;
|
|
||||||
result.channel = stereoAndDiff;
|
|
||||||
results.push_back(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nTracksSearched++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trackNum++;
|
nTracksSearched++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8215,7 +8245,8 @@ wxString MenuCommandHandler::FoundTrack::ComposeTrackName() const
|
|||||||
: name;
|
: name;
|
||||||
auto longName = shortName;
|
auto longName = shortName;
|
||||||
if (channel) {
|
if (channel) {
|
||||||
if ( waveTrack->GetLinked() )
|
// TODO: more-than-two-channels-message
|
||||||
|
if ( waveTrack->IsLeader() )
|
||||||
/* i18n-hint: given the name of a track, specify its left channel */
|
/* i18n-hint: given the name of a track, specify its left channel */
|
||||||
longName = wxString::Format(_("%s left"), shortName);
|
longName = wxString::Format(_("%s left"), shortName);
|
||||||
else
|
else
|
||||||
|
@ -357,7 +357,7 @@ FoundClip FindPrevClip
|
|||||||
int FindClips
|
int FindClips
|
||||||
(AudacityProject &project,
|
(AudacityProject &project,
|
||||||
double t0, double t1, bool next, std::vector<FoundClip>& results);
|
double t0, double t1, bool next, std::vector<FoundClip>& results);
|
||||||
bool ChannelsHaveSameClipBoundaries(const WaveTrack* wt);
|
bool ChannelsHaveDifferentClipBoundaries(const WaveTrack* wt);
|
||||||
void OnSelectPrevClip(const CommandContext &context );
|
void OnSelectPrevClip(const CommandContext &context );
|
||||||
void OnSelectNextClip(const CommandContext &context );
|
void OnSelectNextClip(const CommandContext &context );
|
||||||
void OnSelectClip(AudacityProject &project, bool next);
|
void OnSelectClip(AudacityProject &project, bool next);
|
||||||
|
75
src/Mix.cpp
75
src/Mix.cpp
@ -49,28 +49,23 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
|||||||
uLeft.reset(), uRight.reset();
|
uLeft.reset(), uRight.reset();
|
||||||
|
|
||||||
// This function was formerly known as "Quick Mix".
|
// This function was formerly known as "Quick Mix".
|
||||||
const Track *t;
|
|
||||||
bool mono = false; /* flag if output can be mono without loosing anything*/
|
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
|
bool oneinput = false; /* flag set to true if there is only one input track
|
||||||
(mono or stereo) */
|
(mono or stereo) */
|
||||||
|
|
||||||
TrackListIterator iter(tracks);
|
const auto trackRange = tracks->Selected< const WaveTrack >();
|
||||||
SelectedTrackListOfKindIterator usefulIter(Track::Wave, tracks);
|
auto first = *trackRange.begin();
|
||||||
// this only iterates tracks which are relevant to this function, i.e.
|
// this only iterates tracks which are relevant to this function, i.e.
|
||||||
// selected WaveTracks. The tracklist is (confusingly) the list of all
|
// selected WaveTracks. The tracklist is (confusingly) the list of all
|
||||||
// tracks in the project
|
// tracks in the project
|
||||||
|
|
||||||
int numWaves = 0; /* number of wave tracks in the selection */
|
int numWaves = 0; /* number of wave tracks in the selection */
|
||||||
int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
|
int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/
|
||||||
t = iter.First();
|
for(auto wt : trackRange) {
|
||||||
while (t) {
|
numWaves++;
|
||||||
if (t->GetSelected() && t->GetKind() == Track::Wave) {
|
float pan = wt->GetPan();
|
||||||
numWaves++;
|
if (wt->GetChannel() == Track::MonoChannel && pan == 0)
|
||||||
float pan = ((WaveTrack*)t)->GetPan();
|
numMono++;
|
||||||
if (t->GetChannel() == Track::MonoChannel && pan == 0)
|
|
||||||
numMono++;
|
|
||||||
}
|
|
||||||
t = iter.Next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numMono == numWaves)
|
if (numMono == numWaves)
|
||||||
@ -88,45 +83,42 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
|||||||
double tstart, tend; // start and end times for one track.
|
double tstart, tend; // start and end times for one track.
|
||||||
|
|
||||||
WaveTrackConstArray waveArray;
|
WaveTrackConstArray waveArray;
|
||||||
t = iter.First();
|
|
||||||
|
|
||||||
while (t) {
|
for(auto wt : trackRange) {
|
||||||
if (t->GetSelected() && t->GetKind() == Track::Wave) {
|
waveArray.push_back( Track::Pointer< const WaveTrack >( wt ) );
|
||||||
waveArray.push_back(Track::Pointer<const WaveTrack>(t));
|
tstart = wt->GetStartTime();
|
||||||
tstart = t->GetStartTime();
|
tend = wt->GetEndTime();
|
||||||
tend = t->GetEndTime();
|
if (tend > mixEndTime)
|
||||||
if (tend > mixEndTime)
|
mixEndTime = tend;
|
||||||
mixEndTime = tend;
|
// try and get the start time. If the track is empty we will get 0,
|
||||||
// try and get the start time. If the track is empty we will get 0,
|
// which is ambiguous because it could just mean the track starts at
|
||||||
// which is ambiguous because it could just mean the track starts at
|
// the beginning of the project, as well as empty track. The give-away
|
||||||
// the beginning of the project, as well as empty track. The give-away
|
// is that an empty track also ends at zero.
|
||||||
// is that an empty track also ends at zero.
|
|
||||||
|
|
||||||
if (tstart != tend) {
|
if (tstart != tend) {
|
||||||
// we don't get empty tracks here
|
// we don't get empty tracks here
|
||||||
if (!gotstart) {
|
if (!gotstart) {
|
||||||
// no previous start, use this one unconditionally
|
// no previous start, use this one unconditionally
|
||||||
mixStartTime = tstart;
|
mixStartTime = tstart;
|
||||||
gotstart = true;
|
gotstart = true;
|
||||||
} else if (tstart < mixStartTime)
|
} else if (tstart < mixStartTime)
|
||||||
mixStartTime = tstart; // have a start, only make it smaller
|
mixStartTime = tstart; // have a start, only make it smaller
|
||||||
} // end if start and end are different
|
} // 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) */
|
/* create the destination track (NEW track) */
|
||||||
if ((numWaves == 1) || ((numWaves == 2) && (usefulIter.First()->GetLink() != NULL)))
|
if (numWaves == TrackList::Channels(first).size())
|
||||||
oneinput = true;
|
oneinput = true;
|
||||||
// only one input track (either 1 mono or one linked stereo pair)
|
// only one input track (either 1 mono or one linked stereo pair)
|
||||||
|
|
||||||
auto mixLeft = trackFactory->NewWaveTrack(format, rate);
|
auto mixLeft = trackFactory->NewWaveTrack(format, rate);
|
||||||
if (oneinput)
|
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
|
else
|
||||||
mixLeft->SetName(_("Mix"));
|
mixLeft->SetName(_("Mix"));
|
||||||
mixLeft->SetOffset(mixStartTime);
|
mixLeft->SetOffset(mixStartTime);
|
||||||
|
|
||||||
|
// TODO: more-than-two-channels
|
||||||
decltype(mixLeft) mixRight{};
|
decltype(mixLeft) mixRight{};
|
||||||
if (mono) {
|
if (mono) {
|
||||||
mixLeft->SetChannel(Track::MonoChannel);
|
mixLeft->SetChannel(Track::MonoChannel);
|
||||||
@ -134,10 +126,11 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory,
|
|||||||
else {
|
else {
|
||||||
mixRight = trackFactory->NewWaveTrack(format, rate);
|
mixRight = trackFactory->NewWaveTrack(format, rate);
|
||||||
if (oneinput) {
|
if (oneinput) {
|
||||||
if (usefulIter.First()->GetLink() != NULL) // we have linked track
|
auto channels = TrackList::Channels(first);
|
||||||
mixRight->SetName(usefulIter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/
|
if (channels.size() > 1)
|
||||||
|
mixRight->SetName((*channels.begin().advance(1))->GetName()); /* set name to match input track's right channel!*/
|
||||||
else
|
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
|
else
|
||||||
mixRight->SetName(_("Mix"));
|
mixRight->SetName(_("Mix"));
|
||||||
|
@ -334,9 +334,10 @@ WaveTrack *MixerTrackCluster::GetWave() const
|
|||||||
|
|
||||||
WaveTrack *MixerTrackCluster::GetRight() const
|
WaveTrack *MixerTrackCluster::GetRight() const
|
||||||
{
|
{
|
||||||
|
// TODO: more-than-two-channels
|
||||||
auto left = GetWave();
|
auto left = GetWave();
|
||||||
if (left)
|
if (left)
|
||||||
return static_cast<WaveTrack*>(left->GetLink());
|
return * ++ TrackList::Channels(left).begin();
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -981,9 +982,9 @@ void MixerBoard::UpdateTrackClusters()
|
|||||||
size_t nClusterCount = mMixerTrackClusters.size();
|
size_t nClusterCount = mMixerTrackClusters.size();
|
||||||
unsigned int nClusterIndex = 0;
|
unsigned int nClusterIndex = 0;
|
||||||
MixerTrackCluster* pMixerTrackCluster = NULL;
|
MixerTrackCluster* pMixerTrackCluster = NULL;
|
||||||
Track* pTrack;
|
|
||||||
|
|
||||||
for (auto pPlayableTrack: mTracks->Leaders<PlayableTrack>()) {
|
for (auto pPlayableTrack: mTracks->Leaders<PlayableTrack>()) {
|
||||||
|
// TODO: more-than-two-channels
|
||||||
auto spTrack = Track::Pointer<PlayableTrack>( pPlayableTrack );
|
auto spTrack = Track::Pointer<PlayableTrack>( pPlayableTrack );
|
||||||
if (nClusterIndex < nClusterCount)
|
if (nClusterIndex < nClusterCount)
|
||||||
{
|
{
|
||||||
|
@ -703,16 +703,20 @@ void ScreenFrame::OnOneHour(wxCommandEvent & WXUNUSED(event))
|
|||||||
|
|
||||||
void ScreenFrame::SizeTracks(int h)
|
void ScreenFrame::SizeTracks(int h)
|
||||||
{
|
{
|
||||||
TrackListIterator iter(mContext.GetProject()->GetTracks());
|
// h is the height for a channel
|
||||||
for (Track * t = iter.First(); t; t = iter.Next()) {
|
// Set the height of a mono track twice as high
|
||||||
if (t->GetKind() == Track::Wave) {
|
|
||||||
if (t->GetLink()) {
|
// TODO: more-than-two-channels
|
||||||
t->SetHeight(h);
|
// If there should be more-than-stereo tracks, this makes
|
||||||
}
|
// each channel as high as for a stereo channel
|
||||||
else {
|
|
||||||
t->SetHeight(h*2);
|
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();
|
mContext.GetProject()->RedrawProject();
|
||||||
}
|
}
|
||||||
|
@ -1078,23 +1078,17 @@ void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
|
|||||||
if (!trk)
|
if (!trk)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Track *link = trk->GetLink();
|
trk = *GetTracks()->FindLeader(trk);
|
||||||
|
auto height =
|
||||||
if (link && !trk->GetLinked()) {
|
TrackList::Channels(trk).sum( &Track::GetHeight )
|
||||||
trk = link;
|
- kTopInset - kShadowThickness;
|
||||||
link = trk->GetLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtract insets and shadows from the rectangle, but not border
|
// subtract insets and shadows from the rectangle, but not border
|
||||||
// This matters because some separators do paint over the border
|
// This matters because some separators do paint over the border
|
||||||
wxRect rect(kLeftInset,
|
wxRect rect(kLeftInset,
|
||||||
-mViewInfo->vpos + trk->GetY() + kTopInset,
|
-mViewInfo->vpos + trk->GetY() + kTopInset,
|
||||||
GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness,
|
GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness,
|
||||||
trk->GetHeight() - kTopInset - kShadowThickness);
|
height);
|
||||||
|
|
||||||
if (link) {
|
|
||||||
rect.height += link->GetHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
if( refreshbacking )
|
if( refreshbacking )
|
||||||
{
|
{
|
||||||
@ -1184,41 +1178,24 @@ void TrackPanel::DrawEverythingElse(TrackPanelDrawingContext &context,
|
|||||||
trackRect.y = t->GetY() - mViewInfo->vpos;
|
trackRect.y = t->GetY() - mViewInfo->vpos;
|
||||||
trackRect.height = t->GetHeight();
|
trackRect.height = t->GetHeight();
|
||||||
|
|
||||||
// If this track is linked to the next one, display a common
|
auto leaderTrack = *GetTracks()->FindLeader( t );
|
||||||
// border for both, otherwise draw a normal border
|
|
||||||
wxRect rect = trackRect;
|
|
||||||
bool skipBorder = false;
|
|
||||||
Track *l = t->GetLink();
|
|
||||||
|
|
||||||
if (l && t->GetLinked()) {
|
|
||||||
rect.height += l->GetHeight();
|
|
||||||
}
|
|
||||||
else if (l && trackRect.y >= 0) {
|
|
||||||
skipBorder = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the previous track is linked to this one but isn't on the screen
|
// If the previous track is linked to this one but isn't on the screen
|
||||||
// (and thus would have been skipped) we need to draw that track's border
|
// (and thus would have been skipped) we need to draw that track's border
|
||||||
// instead.
|
// instead.
|
||||||
const Track *borderTrack = t;
|
bool drawBorder = (t == leaderTrack || trackRect.y < 0);
|
||||||
wxRect borderRect = rect;
|
|
||||||
|
|
||||||
if (l && !t->GetLinked() && trackRect.y < 0)
|
if (drawBorder) {
|
||||||
{
|
wxRect teamRect = trackRect;
|
||||||
borderTrack = l;
|
teamRect.y = leaderTrack->GetY() - mViewInfo->vpos;
|
||||||
|
// danger with pending tracks?
|
||||||
|
teamRect.height =
|
||||||
|
TrackList::Channels(leaderTrack)
|
||||||
|
.sum( &Track::GetHeight );
|
||||||
|
|
||||||
borderRect = trackRect;
|
|
||||||
borderRect.y = l->GetY() - mViewInfo->vpos;
|
|
||||||
borderRect.height = l->GetHeight();
|
|
||||||
|
|
||||||
borderRect.height += t->GetHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skipBorder) {
|
|
||||||
if (mAx->IsFocused(t)) {
|
if (mAx->IsFocused(t)) {
|
||||||
focusRect = borderRect;
|
focusRect = teamRect;
|
||||||
}
|
}
|
||||||
DrawOutside(context, borderTrack, borderRect);
|
DrawOutside(context, leaderTrack, teamRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Believe it or not, we can speed up redrawing if we don't
|
// Believe it or not, we can speed up redrawing if we don't
|
||||||
@ -1675,7 +1652,9 @@ void TrackInfo::Status1DrawFunction
|
|||||||
/// stereo and what sample rate it's using.
|
/// stereo and what sample rate it's using.
|
||||||
auto rate = wt ? wt->GetRate() : 44100.0;
|
auto rate = wt ? wt->GetRate() : 44100.0;
|
||||||
wxString s;
|
wxString s;
|
||||||
if (!wt || (wt->GetLinked()))
|
if (!pTrack || TrackList::Channels(pTrack).size() > 1)
|
||||||
|
// TODO: more-than-two-channels-message
|
||||||
|
// more appropriate strings
|
||||||
s = _("Stereo, %dHz");
|
s = _("Stereo, %dHz");
|
||||||
else {
|
else {
|
||||||
if (wt->GetChannel() == Track::MonoChannel)
|
if (wt->GetChannel() == Track::MonoChannel)
|
||||||
@ -1714,6 +1693,15 @@ void TrackPanel::DrawOutside
|
|||||||
wxRect rect = rec;
|
wxRect rect = rec;
|
||||||
DrawOutsideOfTrack(context, t, rect);
|
DrawOutsideOfTrack(context, t, rect);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto channels = TrackList::Channels(t);
|
||||||
|
// omit last (perhaps, only) channel
|
||||||
|
--channels.second;
|
||||||
|
for (auto channel : channels)
|
||||||
|
// draw the sash below this channel
|
||||||
|
DrawSash(channel, dc, rect);
|
||||||
|
}
|
||||||
|
|
||||||
// Now exclude left, right, and top insets
|
// Now exclude left, right, and top insets
|
||||||
rect.x += kLeftInset;
|
rect.x += kLeftInset;
|
||||||
rect.y += kTopInset;
|
rect.y += kTopInset;
|
||||||
@ -1733,7 +1721,16 @@ void TrackPanel::DrawOutside
|
|||||||
// TrackArtist::DrawSyncLockTiles(dc, tileFill);
|
// TrackArtist::DrawSyncLockTiles(dc, tileFill);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
DrawBordersAroundTrack(t, dc, rect, labelw, vrul);
|
DrawBordersAroundTrack(dc, rect, vrul);
|
||||||
|
{
|
||||||
|
auto channels = TrackList::Channels(t);
|
||||||
|
// omit last (perhaps, only) channel
|
||||||
|
--channels.second;
|
||||||
|
for (auto channel : channels)
|
||||||
|
// draw the sash below this channel
|
||||||
|
DrawBordersAroundSash(channel, dc, rect, labelw);
|
||||||
|
}
|
||||||
|
|
||||||
DrawShadow(t, dc, rect);
|
DrawShadow(t, dc, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1777,16 +1774,17 @@ void TrackPanel::DrawOutsideOfTrack
|
|||||||
side.x += side.width - kRightInset;
|
side.x += side.width - kRightInset;
|
||||||
side.width = kRightInset;
|
side.width = kRightInset;
|
||||||
dc->DrawRectangle(side);
|
dc->DrawRectangle(side);
|
||||||
|
}
|
||||||
|
|
||||||
// Area between tracks of stereo group
|
void TrackPanel::DrawSash(const Track * t, wxDC * dc, const wxRect & rect)
|
||||||
if (t->GetLinked()) {
|
{
|
||||||
// Paint the channel separator over (what would be) the shadow of the top
|
// Area between channels of a group
|
||||||
// channel, and the top inset of the bottom channel
|
// Paint the channel separator over (what would be) the shadow of this
|
||||||
side = rect;
|
// channel, and the top inset of the following channel
|
||||||
side.y += t->GetHeight() - kShadowThickness;
|
wxRect side = rect;
|
||||||
side.height = kTopInset + kShadowThickness;
|
side.y = t->GetY() - mViewInfo->vpos + t->GetHeight() - kShadowThickness;
|
||||||
dc->DrawRectangle(side);
|
side.height = kTopInset + kShadowThickness;
|
||||||
}
|
dc->DrawRectangle(side);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackPanel::SetBackgroundCell
|
void TrackPanel::SetBackgroundCell
|
||||||
@ -1979,8 +1977,8 @@ void TrackPanel::VerticalScroll( float fracPosition){
|
|||||||
// Given rectangle excludes the insets left, right, and top
|
// Given rectangle excludes the insets left, right, and top
|
||||||
// Draw a rectangular border and also a vertical separator of track controls
|
// Draw a rectangular border and also a vertical separator of track controls
|
||||||
// from the rest (ruler and proper track area)
|
// from the rest (ruler and proper track area)
|
||||||
void TrackPanel::DrawBordersAroundTrack(const Track * t, wxDC * dc,
|
void TrackPanel::DrawBordersAroundTrack(wxDC * dc,
|
||||||
const wxRect & rect, const int labelw,
|
const wxRect & rect,
|
||||||
const int vrul)
|
const int vrul)
|
||||||
{
|
{
|
||||||
// Border around track and label area
|
// Border around track and label area
|
||||||
@ -1991,21 +1989,21 @@ void TrackPanel::DrawBordersAroundTrack(const Track * t, wxDC * dc,
|
|||||||
rect.width - kShadowThickness,
|
rect.width - kShadowThickness,
|
||||||
rect.height - kShadowThickness);
|
rect.height - kShadowThickness);
|
||||||
|
|
||||||
|
|
||||||
// between vruler and TrackInfo
|
// between vruler and TrackInfo
|
||||||
AColor::Line(*dc, vrul, rect.y, vrul, rect.y + rect.height - 1);
|
AColor::Line(*dc, vrul, rect.y, vrul, rect.y + rect.height - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// The lines at bottom of 1st track and top of second track of stereo group
|
void TrackPanel::DrawBordersAroundSash(const Track * t, wxDC * dc,
|
||||||
// Possibly replace with DrawRectangle to add left border.
|
const wxRect & rect, const int labelw)
|
||||||
if (t->GetLinked()) {
|
{
|
||||||
// The given rect has had the top inset subtracted
|
int h1 = t->GetY() - mViewInfo->vpos + t->GetHeight();
|
||||||
int h1 = rect.y + t->GetHeight() - kTopInset;
|
// h1 is the top coordinate of the following channel's rectangle
|
||||||
// h1 is the top coordinate of the second tracks' rectangle
|
// Draw (part of) the bottom border of the top channel and top border of the bottom
|
||||||
// Draw (part of) the bottom border of the top channel and top border of the bottom
|
// At left it extends between the vertical rulers too
|
||||||
// At left it extends between the vertical rulers too
|
// These lines stroke over what is otherwise "border" of each channel
|
||||||
// These lines stroke over what is otherwise "border" of each channel
|
AColor::Line(*dc, labelw, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin);
|
||||||
AColor::Line(*dc, labelw, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin);
|
AColor::Line(*dc, labelw, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset);
|
||||||
AColor::Line(*dc, labelw, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given rectangle has insets subtracted left, right, and top
|
// Given rectangle has insets subtracted left, right, and top
|
||||||
|
@ -373,16 +373,20 @@ protected:
|
|||||||
void DrawEverythingElse(TrackPanelDrawingContext &context,
|
void DrawEverythingElse(TrackPanelDrawingContext &context,
|
||||||
const wxRegion & region,
|
const wxRegion & region,
|
||||||
const wxRect & clip);
|
const wxRect & clip);
|
||||||
void DrawOutside
|
void DrawOutside(
|
||||||
(TrackPanelDrawingContext &context,
|
TrackPanelDrawingContext &context,
|
||||||
const Track *t, const wxRect & rec);
|
const Track *leaderTrack, const wxRect & teamRect);
|
||||||
|
|
||||||
void HighlightFocusedTrack (wxDC* dc, const wxRect &rect);
|
void HighlightFocusedTrack (wxDC* dc, const wxRect &rect);
|
||||||
void DrawShadow (const Track *t, wxDC* dc, const wxRect & rect);
|
void DrawShadow (const Track *t, wxDC* dc, const wxRect & rect);
|
||||||
void DrawBordersAroundTrack(const Track *t, wxDC* dc, const wxRect & rect, const int labelw, const int vrul);
|
void DrawBordersAroundTrack(wxDC* dc, const wxRect & rect,
|
||||||
void DrawOutsideOfTrack
|
const int vrul);
|
||||||
(TrackPanelDrawingContext &context,
|
void DrawBordersAroundSash (const Track *t, wxDC* dc, const wxRect & rect,
|
||||||
const Track *t, const wxRect & rect);
|
const int labelw);
|
||||||
|
void DrawOutsideOfTrack (
|
||||||
|
TrackPanelDrawingContext &context,
|
||||||
|
const Track *t, const wxRect & rect);
|
||||||
|
void DrawSash (const Track *t, wxDC* dc, const wxRect & rect);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Set the object that performs catch-all event handling when the pointer
|
// Set the object that performs catch-all event handling when the pointer
|
||||||
|
@ -26,6 +26,8 @@ Paul Licameli split from TrackPanel.cpp
|
|||||||
|
|
||||||
HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked)
|
HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked)
|
||||||
{
|
{
|
||||||
|
// TODO: more-than-two-channels-message
|
||||||
|
|
||||||
static wxCursor resizeCursor{ wxCURSOR_SIZENS };
|
static wxCursor resizeCursor{ wxCURSOR_SIZENS };
|
||||||
|
|
||||||
/// When in the resize area we can adjust size or relative size.
|
/// When in the resize area we can adjust size or relative size.
|
||||||
@ -59,40 +61,34 @@ UIHandle::Result TrackPanelResizeHandle::Click
|
|||||||
}
|
}
|
||||||
|
|
||||||
TrackPanelResizeHandle::TrackPanelResizeHandle
|
TrackPanelResizeHandle::TrackPanelResizeHandle
|
||||||
( const std::shared_ptr<Track> &track, int y, const AudacityProject *pProject )
|
( const std::shared_ptr<Track> &track, int y )
|
||||||
: mpTrack{ track }
|
: mpTrack{ track }
|
||||||
, mMouseClickY( y )
|
, mMouseClickY( y )
|
||||||
{
|
{
|
||||||
auto tracks = pProject->GetTracks();
|
// TODO: more-than-two-channels
|
||||||
auto prev = * -- tracks->Find(track.get());
|
|
||||||
auto next = * ++ tracks->Find(track.get());
|
|
||||||
|
|
||||||
//STM: Determine whether we should rescale one or two tracks
|
//STM: Determine whether we should rescale one or two tracks
|
||||||
if (prev && prev->GetLink() == track.get()) {
|
auto channels = TrackList::Channels(track.get());
|
||||||
// mpTrack is the lower track
|
auto last = *channels.rbegin();
|
||||||
mInitialTrackHeight = track->GetHeight();
|
mInitialTrackHeight = last->GetHeight();
|
||||||
mInitialActualHeight = track->GetActualHeight();
|
mInitialActualHeight = last->GetActualHeight();
|
||||||
mInitialMinimized = track->GetMinimized();
|
mInitialMinimized = last->GetMinimized();
|
||||||
mInitialUpperTrackHeight = prev->GetHeight();
|
|
||||||
mInitialUpperActualHeight = prev->GetActualHeight();
|
if (channels.size() > 1) {
|
||||||
mMode = IsResizingBelowLinkedTracks;
|
auto first = *channels.begin();
|
||||||
|
|
||||||
|
mInitialUpperTrackHeight = first->GetHeight();
|
||||||
|
mInitialUpperActualHeight = first->GetActualHeight();
|
||||||
|
|
||||||
|
if (track.get() == *channels.rbegin())
|
||||||
|
// capturedTrack is the lowest track
|
||||||
|
mMode = IsResizingBelowLinkedTracks;
|
||||||
|
else
|
||||||
|
// capturedTrack is not the lowest track
|
||||||
|
mMode = IsResizingBetweenLinkedTracks;
|
||||||
}
|
}
|
||||||
else if (next && track->GetLink() == next) {
|
else
|
||||||
// mpTrack is the upper track
|
|
||||||
mInitialTrackHeight = next->GetHeight();
|
|
||||||
mInitialActualHeight = next->GetActualHeight();
|
|
||||||
mInitialMinimized = next->GetMinimized();
|
|
||||||
mInitialUpperTrackHeight = track->GetHeight();
|
|
||||||
mInitialUpperActualHeight = track->GetActualHeight();
|
|
||||||
mMode = IsResizingBetweenLinkedTracks;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// DM: Save the initial mouse location and the initial height
|
|
||||||
mInitialTrackHeight = track->GetHeight();
|
|
||||||
mInitialActualHeight = track->GetActualHeight();
|
|
||||||
mInitialMinimized = track->GetMinimized();
|
|
||||||
mMode = IsResizing;
|
mMode = IsResizing;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIHandle::Result TrackPanelResizeHandle::Drag
|
UIHandle::Result TrackPanelResizeHandle::Drag
|
||||||
@ -113,23 +109,24 @@ UIHandle::Result TrackPanelResizeHandle::Drag
|
|||||||
// This used to be in HandleResizeClick(), but simply clicking
|
// This used to be in HandleResizeClick(), but simply clicking
|
||||||
// on a resize border would switch the minimized state.
|
// on a resize border would switch the minimized state.
|
||||||
if (pTrack->GetMinimized()) {
|
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());
|
if (channels.size() > 1) {
|
||||||
pTrack->SetMinimized(false);
|
|
||||||
|
|
||||||
if (link) {
|
|
||||||
link->SetHeight(link->GetHeight());
|
|
||||||
link->SetMinimized(false);
|
|
||||||
// Initial values must be reset since they weren't based on the
|
// Initial values must be reset since they weren't based on the
|
||||||
// minimized heights.
|
// minimized heights.
|
||||||
mInitialUpperTrackHeight = link->GetHeight();
|
mInitialUpperTrackHeight = (*channels.begin())->GetHeight();
|
||||||
mInitialTrackHeight = pTrack->GetHeight();
|
mInitialTrackHeight = (*channels.rbegin())->GetHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common pieces of code for MONO_WAVE_PAN and otherwise.
|
// Common pieces of code for MONO_WAVE_PAN and otherwise.
|
||||||
auto doResizeBelow = [&] (Track *prev, bool WXUNUSED(vStereo)) {
|
auto doResizeBelow = [&] (Track *prev, bool WXUNUSED(vStereo)) {
|
||||||
|
// TODO: more-than-two-channels
|
||||||
|
|
||||||
double proportion = static_cast < double >(mInitialTrackHeight)
|
double proportion = static_cast < double >(mInitialTrackHeight)
|
||||||
/ (mInitialTrackHeight + mInitialUpperTrackHeight);
|
/ (mInitialTrackHeight + mInitialUpperTrackHeight);
|
||||||
|
|
||||||
@ -150,6 +147,8 @@ UIHandle::Result TrackPanelResizeHandle::Drag
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto doResizeBetween = [&] (Track *next, bool WXUNUSED(vStereo)) {
|
auto doResizeBetween = [&] (Track *next, bool WXUNUSED(vStereo)) {
|
||||||
|
// TODO: more-than-two-channels
|
||||||
|
|
||||||
int newUpperTrackHeight = mInitialUpperTrackHeight + delta;
|
int newUpperTrackHeight = mInitialUpperTrackHeight + delta;
|
||||||
int newTrackHeight = mInitialTrackHeight - delta;
|
int newTrackHeight = mInitialTrackHeight - delta;
|
||||||
|
|
||||||
|
@ -22,9 +22,7 @@ class TrackPanelResizeHandle final : public UIHandle
|
|||||||
TrackPanelResizeHandle(const TrackPanelResizeHandle&) = delete;
|
TrackPanelResizeHandle(const TrackPanelResizeHandle&) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TrackPanelResizeHandle
|
explicit TrackPanelResizeHandle( const std::shared_ptr<Track> &pTrack, int y );
|
||||||
( const std::shared_ptr<Track> &pTrack, int y,
|
|
||||||
const AudacityProject *pProject );
|
|
||||||
|
|
||||||
TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = default;
|
TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = default;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ std::vector<UIHandlePtr> TrackPanelResizerCell::HitTest
|
|||||||
auto pTrack = mpTrack.lock();
|
auto pTrack = mpTrack.lock();
|
||||||
if (pTrack) {
|
if (pTrack) {
|
||||||
auto result = std::make_shared<TrackPanelResizeHandle>(
|
auto result = std::make_shared<TrackPanelResizeHandle>(
|
||||||
pTrack, st.state.m_y, pProject );
|
pTrack, st.state.m_y );
|
||||||
result = AssignUIHandlePtr(mResizeHandle, result);
|
result = AssignUIHandlePtr(mResizeHandle, result);
|
||||||
results.push_back(result);
|
results.push_back(result);
|
||||||
}
|
}
|
||||||
|
@ -1359,10 +1359,9 @@ bool Effect::ProcessPass()
|
|||||||
if (!left->GetSelected())
|
if (!left->GetSelected())
|
||||||
return fallthrough();
|
return fallthrough();
|
||||||
|
|
||||||
WaveTrack *right;
|
|
||||||
sampleCount len;
|
sampleCount len;
|
||||||
sampleCount leftStart;
|
sampleCount leftStart;
|
||||||
sampleCount rightStart;
|
sampleCount rightStart = 0;
|
||||||
|
|
||||||
if (!isGenerator)
|
if (!isGenerator)
|
||||||
{
|
{
|
||||||
@ -1376,48 +1375,37 @@ bool Effect::ProcessPass()
|
|||||||
mSampleCnt = left->TimeToLongSamples(mDuration);
|
mSampleCnt = left->TimeToLongSamples(mDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
mNumChannels = 1;
|
mNumChannels = 0;
|
||||||
|
WaveTrack *right{};
|
||||||
|
|
||||||
if (left->GetChannel() == Track::LeftChannel)
|
// Iterate either over one track which could be any channel,
|
||||||
{
|
// or if multichannel, then over all channels of left,
|
||||||
map[0] = ChannelNameFrontLeft;
|
// which is a leader.
|
||||||
}
|
for (auto channel :
|
||||||
else if (left->GetChannel() == Track::RightChannel)
|
TrackList::Channels(left).StartingWith(left)) {
|
||||||
{
|
if (channel->GetChannel() == Track::LeftChannel)
|
||||||
map[0] = ChannelNameFrontRight;
|
map[mNumChannels] = ChannelNameFrontLeft;
|
||||||
}
|
else if (channel->GetChannel() == Track::RightChannel)
|
||||||
else
|
map[mNumChannels] = ChannelNameFrontRight;
|
||||||
{
|
|
||||||
map[0] = ChannelNameMono;
|
|
||||||
}
|
|
||||||
map[1] = ChannelNameEOL;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
clear = false;
|
|
||||||
mNumChannels = 2;
|
|
||||||
|
|
||||||
if (right->GetChannel() == Track::LeftChannel)
|
|
||||||
{
|
|
||||||
map[1] = ChannelNameFrontLeft;
|
|
||||||
}
|
|
||||||
else if (right->GetChannel() == Track::RightChannel)
|
|
||||||
{
|
|
||||||
map[1] = ChannelNameFrontRight;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
map[mNumChannels] = ChannelNameMono;
|
||||||
map[1] = ChannelNameMono;
|
|
||||||
|
++ mNumChannels;
|
||||||
|
map[mNumChannels] = ChannelNameEOL;
|
||||||
|
|
||||||
|
if (! multichannel)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (mNumChannels == 2) {
|
||||||
|
// TODO: more-than-two-channels
|
||||||
|
right = channel;
|
||||||
|
clear = false;
|
||||||
|
if (!isGenerator)
|
||||||
|
GetSamples(right, &rightStart, &len);
|
||||||
|
|
||||||
|
// Ignore other channels
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
map[2] = ChannelNameEOL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let the client know the sample rate
|
// Let the client know the sample rate
|
||||||
|
@ -219,10 +219,6 @@ bool EffectNormalize::Process()
|
|||||||
//Iterate over each track
|
//Iterate over each track
|
||||||
this->CopyInputTracks(); // Set up mOutputTracks.
|
this->CopyInputTracks(); // Set up mOutputTracks.
|
||||||
bool bGoodResult = true;
|
bool bGoodResult = true;
|
||||||
SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks.get());
|
|
||||||
WaveTrack *track = (WaveTrack *) iter.First();
|
|
||||||
WaveTrack *prevTrack;
|
|
||||||
prevTrack = track;
|
|
||||||
double progress = 0;
|
double progress = 0;
|
||||||
wxString topMsg;
|
wxString topMsg;
|
||||||
if(mDC && mGain)
|
if(mDC && mGain)
|
||||||
@ -234,8 +230,10 @@ bool EffectNormalize::Process()
|
|||||||
else if(!mDC && !mGain)
|
else if(!mDC && !mGain)
|
||||||
topMsg = _("Not doing anything...\n"); // shouldn't get here
|
topMsg = _("Not doing anything...\n"); // shouldn't get here
|
||||||
|
|
||||||
while (track) {
|
for ( auto track : mOutputTracks->Selected< WaveTrack >()
|
||||||
|
+ ( mStereoInd ? &Track::Any : &Track::IsLeader ) ) {
|
||||||
//Get start and end times from track
|
//Get start and end times from track
|
||||||
|
// PRL: No accounting for multiple channels?
|
||||||
double trackStart = track->GetStartTime();
|
double trackStart = track->GetStartTime();
|
||||||
double trackEnd = track->GetEndTime();
|
double trackEnd = track->GetEndTime();
|
||||||
|
|
||||||
@ -244,121 +242,106 @@ bool EffectNormalize::Process()
|
|||||||
mCurT0 = mT0 < trackStart? trackStart: mT0;
|
mCurT0 = mT0 < trackStart? trackStart: mT0;
|
||||||
mCurT1 = mT1 > trackEnd? trackEnd: mT1;
|
mCurT1 = mT1 > trackEnd? trackEnd: mT1;
|
||||||
|
|
||||||
|
auto range = mStereoInd
|
||||||
|
? TrackList::SingletonRange(track)
|
||||||
|
: TrackList::Channels(track);
|
||||||
|
|
||||||
// Process only if the right marker is to the right of the left marker
|
// Process only if the right marker is to the right of the left marker
|
||||||
if (mCurT1 > mCurT0) {
|
if (mCurT1 > mCurT0) {
|
||||||
wxString msg;
|
wxString trackName = track->GetName();
|
||||||
auto trackName = track->GetName();
|
|
||||||
|
|
||||||
if(!track->GetLinked() || mStereoInd)
|
float extent;
|
||||||
msg =
|
#ifdef EXPERIMENTAL_R128_NORM
|
||||||
topMsg + wxString::Format( _("Analyzing: %s"), trackName );
|
if (mUseLoudness)
|
||||||
|
// Loudness: use sum of both tracks.
|
||||||
|
// As a result, stereo tracks appear about 3 LUFS louder,
|
||||||
|
// as specified.
|
||||||
|
extent = 0;
|
||||||
else
|
else
|
||||||
msg =
|
#endif
|
||||||
topMsg + wxString::Format( _("Analyzing first track of stereo pair: %s"), trackName );
|
// Will compute a maximum
|
||||||
float offset, extent;
|
extent = std::numeric_limits<float>::lowest();
|
||||||
bGoodResult = AnalyseTrack(track, msg, progress, offset, extent);
|
std::vector<float> offsets;
|
||||||
if (!bGoodResult )
|
|
||||||
break;
|
wxString msg;
|
||||||
if(!track->GetLinked() || mStereoInd) {
|
if (range.size() == 1)
|
||||||
// mono or 'stereo tracks independently'
|
// mono or 'stereo tracks independently'
|
||||||
if( (extent > 0) && mGain )
|
msg = topMsg +
|
||||||
{
|
wxString::Format( _("Analyzing: %s"), trackName );
|
||||||
mMult = ratio / extent;
|
else
|
||||||
|
msg = topMsg +
|
||||||
|
// TODO: more-than-two-channels-message
|
||||||
|
wxString::Format( _("Analyzing first track of stereo pair: %s"), trackName);
|
||||||
|
|
||||||
|
// Analysis loop over channels collects offsets and extent
|
||||||
|
for (auto channel : range) {
|
||||||
|
float offset, extent2;
|
||||||
|
bGoodResult =
|
||||||
|
AnalyseTrack( channel, msg, progress, offset, extent2 );
|
||||||
|
if ( ! bGoodResult )
|
||||||
|
goto break2;
|
||||||
#ifdef EXPERIMENTAL_R128_NORM
|
#ifdef EXPERIMENTAL_R128_NORM
|
||||||
if(mUseLoudness) {
|
if (mUseLoudness)
|
||||||
// LUFS is defined as -0.691 dB + 10*log10(sum(channels))
|
extent += extent2;
|
||||||
mMult /= 0.8529037031;
|
|
||||||
// LUFS are related to square values so the multiplier must be the root.
|
|
||||||
mMult = sqrt(ratio / extent);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
mMult = 1.0;
|
#endif
|
||||||
|
extent = std::max( extent, extent2 );
|
||||||
msg =
|
offsets.push_back(offset);
|
||||||
topMsg + wxString::Format( _("Processing: %s"), trackName );
|
// TODO: more-than-two-channels-message
|
||||||
|
msg = topMsg +
|
||||||
if(track->GetLinked() || prevTrack->GetLinked()) // only get here if there is a linked track but we are processing independently
|
wxString::Format( _("Analyzing second track of stereo pair: %s"), trackName );
|
||||||
msg =
|
|
||||||
topMsg + wxString::Format( _("Processing stereo channels independently: %s"), trackName );
|
|
||||||
|
|
||||||
if (!ProcessOne(track, msg, progress, offset))
|
|
||||||
{
|
|
||||||
bGoodResult = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// we have a linked stereo track
|
|
||||||
// so we need to find it's min, max and offset
|
|
||||||
// as they are needed to calc the multiplier for both tracks
|
|
||||||
|
|
||||||
track = (WaveTrack *) iter.Next(); // get the next one
|
|
||||||
msg =
|
|
||||||
topMsg + wxString::Format( _("Analyzing second track of stereo pair: %s"), trackName );
|
|
||||||
|
|
||||||
float offset2, extent2;
|
|
||||||
bGoodResult = AnalyseTrack(track, msg, progress, offset2, extent2);
|
|
||||||
if ( !bGoodResult )
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
// Compute the multiplier using extent
|
||||||
|
if( (extent > 0) && mGain ) {
|
||||||
|
mMult = ratio / extent;
|
||||||
#ifdef EXPERIMENTAL_R128_NORM
|
#ifdef EXPERIMENTAL_R128_NORM
|
||||||
if (mUseLoudness) {
|
if(mUseLoudness) {
|
||||||
// Loudness: use sum of both tracks.
|
// PRL: See commit 9cbb67a for the origin of the next line,
|
||||||
// As a result, stereo tracks appear about 3 LUFS louder, as specified.
|
// which has no effect because mMult is again overwritten. What
|
||||||
extent = extent + extent2;
|
// was the intent?
|
||||||
|
|
||||||
// LUFS is defined as -0.691 dB + 10*log10(sum(channels))
|
// LUFS is defined as -0.691 dB + 10*log10(sum(channels))
|
||||||
extent *= 0.8529037031;
|
mMult /= 0.8529037031;
|
||||||
|
// LUFS are related to square values so the multiplier must be the root.
|
||||||
|
mMult = sqrt(ratio / extent);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Peak: use maximum of both tracks.
|
|
||||||
extent = fmax(extent, extent2);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Peak: use maximum of both tracks.
|
|
||||||
extent = fmax(extent, extent2);
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mMult = 1.0;
|
||||||
|
|
||||||
if( (extent > 0) && mGain )
|
if (range.size() == 1) {
|
||||||
{
|
if (TrackList::Channels(track).size() == 1)
|
||||||
mMult = ratio / extent; // we need to use this for both linked tracks
|
// really mono
|
||||||
#ifdef EXPERIMENTAL_R128_NORM
|
msg = topMsg +
|
||||||
if(mUseLoudness) {
|
wxString::Format( _("Processing: %s"), trackName );
|
||||||
// LUFS are related to square values so the multiplier must be the root.
|
|
||||||
mMult = sqrt(mMult);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
mMult = 1.0;
|
//'stereo tracks independently'
|
||||||
|
// TODO: more-than-two-channels-message
|
||||||
|
msg = topMsg +
|
||||||
|
wxString::Format( _("Processing stereo channels independently: %s"), trackName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
msg = topMsg +
|
||||||
|
// TODO: more-than-two-channels-message
|
||||||
|
wxString::Format( _("Processing first track of stereo pair: %s"), trackName);
|
||||||
|
|
||||||
track = (WaveTrack *) iter.Prev(); // go back to the first linked one
|
// Use multiplier in the second, processing loop over channels
|
||||||
msg =
|
auto pOffset = offsets.begin();
|
||||||
topMsg + wxString::Format( _("Processing first track of stereo pair: %s"), trackName );
|
for (auto channel : range) {
|
||||||
|
if (false ==
|
||||||
if (!ProcessOne(track, msg, progress, offset))
|
(bGoodResult = ProcessOne(channel, msg, progress, *pOffset++)) )
|
||||||
{
|
goto break2;
|
||||||
bGoodResult = false;
|
// TODO: more-than-two-channels-message
|
||||||
break;
|
msg = topMsg +
|
||||||
}
|
wxString::Format( _("Processing second track of stereo pair: %s"), trackName);
|
||||||
track = (WaveTrack *) iter.Next(); // go to the second linked one
|
|
||||||
msg =
|
|
||||||
topMsg + wxString::Format( _("Processing second track of stereo pair: %s"), trackName );
|
|
||||||
|
|
||||||
if (!ProcessOne(track, msg, progress, offset2))
|
|
||||||
{
|
|
||||||
bGoodResult = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Iterate to the next track
|
|
||||||
prevTrack = track;
|
|
||||||
track = (WaveTrack *) iter.Next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break2:
|
||||||
|
|
||||||
this->ReplaceProcessedTracks(bGoodResult);
|
this->ReplaceProcessedTracks(bGoodResult);
|
||||||
return bGoodResult;
|
return bGoodResult;
|
||||||
}
|
}
|
||||||
|
@ -252,11 +252,11 @@ bool EffectSBSMS::Process()
|
|||||||
auto start = leftTrack->TimeToLongSamples(mCurT0);
|
auto start = leftTrack->TimeToLongSamples(mCurT0);
|
||||||
auto end = leftTrack->TimeToLongSamples(mCurT1);
|
auto end = leftTrack->TimeToLongSamples(mCurT1);
|
||||||
|
|
||||||
WaveTrack* rightTrack = NULL;
|
// TODO: more-than-two-channels
|
||||||
if (leftTrack->GetLinked()) {
|
WaveTrack *rightTrack =
|
||||||
|
* ++ TrackList::Channels(leftTrack).begin();
|
||||||
|
if (rightTrack) {
|
||||||
double t;
|
double t;
|
||||||
// Assume linked track is wave or null
|
|
||||||
rightTrack = static_cast<WaveTrack*>(leftTrack->GetLink());
|
|
||||||
|
|
||||||
//Adjust bounds by the right tracks markers
|
//Adjust bounds by the right tracks markers
|
||||||
t = rightTrack->GetStartTime();
|
t = rightTrack->GetStartTime();
|
||||||
|
@ -120,10 +120,10 @@ bool EffectSoundTouch::ProcessWithTimeWarper(const TimeWarper &warper)
|
|||||||
// Process only if the right marker is to the right of the left marker
|
// Process only if the right marker is to the right of the left marker
|
||||||
if (mCurT1 > mCurT0) {
|
if (mCurT1 > mCurT0) {
|
||||||
|
|
||||||
if (leftTrack->GetLinked()) {
|
// TODO: more-than-two-channels
|
||||||
|
auto channels = TrackList::Channels(leftTrack);
|
||||||
|
if (auto rightTrack = * ++ channels.begin()) {
|
||||||
double t;
|
double t;
|
||||||
// Assume linked track is wave
|
|
||||||
WaveTrack* rightTrack = static_cast<WaveTrack*>(leftTrack->GetLink());
|
|
||||||
|
|
||||||
//Adjust bounds by the right tracks markers
|
//Adjust bounds by the right tracks markers
|
||||||
t = rightTrack->GetStartTime();
|
t = rightTrack->GetStartTime();
|
||||||
|
@ -75,47 +75,40 @@ bool EffectStereoToMono::Process()
|
|||||||
this->CopyInputTracks(); // Set up mOutputTracks.
|
this->CopyInputTracks(); // Set up mOutputTracks.
|
||||||
bool bGoodResult = true;
|
bool bGoodResult = true;
|
||||||
|
|
||||||
auto trackRange = mOutputTracks->Selected< WaveTrack >();
|
auto trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
|
||||||
mLeftTrack = *trackRange.first;
|
|
||||||
bool refreshIter = false;
|
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;
|
int count = 0;
|
||||||
while ( trackRange.first != trackRange.second ) {
|
while ( trackRange.first != trackRange.second ) {
|
||||||
mLeftTrack = *trackRange.first;
|
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 = * channels.rbegin();
|
||||||
mRightTrack = * ++ trackRange.first;
|
|
||||||
|
|
||||||
if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) {
|
if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) {
|
||||||
auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime());
|
auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime());
|
||||||
auto rightTrackStart = mRightTrack->TimeToLongSamples(mRightTrack->GetStartTime());
|
auto rightTrackStart = mRightTrack->TimeToLongSamples(mRightTrack->GetStartTime());
|
||||||
mStart = wxMin(leftTrackStart, rightTrackStart);
|
mStart = wxMin(leftTrackStart, rightTrackStart);
|
||||||
|
|
||||||
auto leftTrackEnd = mLeftTrack->TimeToLongSamples(mLeftTrack->GetEndTime());
|
auto leftTrackEnd = mLeftTrack->TimeToLongSamples(mLeftTrack->GetEndTime());
|
||||||
auto rightTrackEnd = mRightTrack->TimeToLongSamples(mRightTrack->GetEndTime());
|
auto rightTrackEnd = mRightTrack->TimeToLongSamples(mRightTrack->GetEndTime());
|
||||||
mEnd = wxMax(leftTrackEnd, rightTrackEnd);
|
mEnd = wxMax(leftTrackEnd, rightTrackEnd);
|
||||||
|
|
||||||
bGoodResult = ProcessOne(count);
|
bGoodResult = ProcessOne(count);
|
||||||
if (!bGoodResult)
|
if (!bGoodResult)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
mOutTrack->Clear(mOutTrack->GetStartTime(), mOutTrack->GetEndTime());
|
// The right channel has been deleted, so we must restart from the beginning
|
||||||
|
refreshIter = true;
|
||||||
// The right channel has been deleted, so we must restart from the beginning
|
|
||||||
refreshIter = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refreshIter) {
|
if (refreshIter) {
|
||||||
trackRange = mOutputTracks->Selected< WaveTrack >();
|
trackRange = mOutputTracks->SelectedLeaders< WaveTrack >();
|
||||||
refreshIter = false;
|
refreshIter = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -128,11 +121,6 @@ bool EffectStereoToMono::Process()
|
|||||||
return bGoodResult;
|
return bGoodResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectStereoToMono::End()
|
|
||||||
{
|
|
||||||
mOutTrack.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EffectStereoToMono::ProcessOne(int count)
|
bool EffectStereoToMono::ProcessOne(int count)
|
||||||
{
|
{
|
||||||
float curLeftFrame;
|
float curLeftFrame;
|
||||||
@ -145,6 +133,10 @@ bool EffectStereoToMono::ProcessOne(int count)
|
|||||||
Floats rightBuffer{ idealBlockLen };
|
Floats rightBuffer{ idealBlockLen };
|
||||||
bool bResult = true;
|
bool bResult = true;
|
||||||
|
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
auto outTrack =
|
||||||
|
p->GetTrackFactory()->NewWaveTrack(floatSample, mLeftTrack->GetRate());
|
||||||
|
|
||||||
while (index < mEnd) {
|
while (index < mEnd) {
|
||||||
bResult &= mLeftTrack->Get((samplePtr)leftBuffer.get(), floatSample, index, idealBlockLen);
|
bResult &= mLeftTrack->Get((samplePtr)leftBuffer.get(), floatSample, index, idealBlockLen);
|
||||||
bResult &= mRightTrack->Get((samplePtr)rightBuffer.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;
|
curMonoFrame = (curLeftFrame + curRightFrame) / 2.0;
|
||||||
leftBuffer[i] = curMonoFrame;
|
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())))
|
if (TrackProgress(count, 2.*(index.as_double() / (mEnd - mStart).as_double())))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime());
|
double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime());
|
||||||
mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime());
|
mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime());
|
||||||
mOutTrack->Flush();
|
outTrack->Flush();
|
||||||
mLeftTrack->Paste(minStart, mOutTrack.get());
|
mLeftTrack->Paste(minStart, outTrack.get());
|
||||||
mLeftTrack->SetLinked(false);
|
mLeftTrack->SetLinked(false);
|
||||||
mRightTrack->SetLinked(false);
|
mRightTrack->SetLinked(false);
|
||||||
mLeftTrack->SetChannel(Track::MonoChannel);
|
mLeftTrack->SetChannel(Track::MonoChannel);
|
||||||
|
@ -41,7 +41,6 @@ public:
|
|||||||
// Effect implementation
|
// Effect implementation
|
||||||
|
|
||||||
bool Process() override;
|
bool Process() override;
|
||||||
void End() override;
|
|
||||||
bool IsHidden() override;
|
bool IsHidden() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -54,7 +53,6 @@ private:
|
|||||||
sampleCount mEnd;
|
sampleCount mEnd;
|
||||||
WaveTrack *mLeftTrack;
|
WaveTrack *mLeftTrack;
|
||||||
WaveTrack *mRightTrack;
|
WaveTrack *mRightTrack;
|
||||||
std::unique_ptr<WaveTrack> mOutTrack;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -769,7 +769,7 @@ bool NyquistEffect::Process()
|
|||||||
Effect::MessageBox(message, wxOK | wxCENTRE | wxICON_EXCLAMATION, _("Nyquist Error"));
|
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
|
// 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
|
// (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) {
|
if (bOnePassTool) {
|
||||||
}
|
}
|
||||||
else {
|
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;
|
mCurNumChannels = 2;
|
||||||
|
|
||||||
mCurTrack[1] = * ++ iter;
|
mCurTrack[1] = * ++ channels.first;
|
||||||
if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) {
|
if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) {
|
||||||
Effect::MessageBox(_("Sorry, cannot apply effect on stereo tracks where the tracks don't match."),
|
Effect::MessageBox(_("Sorry, cannot apply effect on stereo tracks where the tracks don't match."),
|
||||||
wxOK | wxCENTRE);
|
wxOK | wxCENTRE);
|
||||||
|
@ -299,25 +299,25 @@ bool VampEffect::Init()
|
|||||||
{
|
{
|
||||||
mRate = 0.0;
|
mRate = 0.0;
|
||||||
|
|
||||||
for (auto left : inputTracks()->Leaders< const WaveTrack >() )
|
// PRL: this loop checked that channels of a track have the same rate,
|
||||||
{
|
// but there was no check that all tracks have one rate, and only the first
|
||||||
if (mRate == 0.0)
|
// is remembered in mRate. Is that correct?
|
||||||
{
|
|
||||||
mRate = left->GetRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (left->GetLinked())
|
|
||||||
{
|
|
||||||
auto right = static_cast<const WaveTrack*>( left->GetLink() );
|
|
||||||
|
|
||||||
|
for (auto leader : inputTracks()->Leaders<const WaveTrack>()) {
|
||||||
|
auto channelGroup = TrackList::Channels( leader );
|
||||||
|
auto rate = (*channelGroup.first++) -> GetRate();
|
||||||
|
for(auto channel : channelGroup) {
|
||||||
|
if (rate != channel->GetRate())
|
||||||
// PRL: Track rate might not match individual clip rates.
|
// PRL: Track rate might not match individual clip rates.
|
||||||
// So is this check not adequate?
|
// So is this check not adequate?
|
||||||
if (left->GetRate() != right->GetRate())
|
{
|
||||||
{
|
// TODO: more-than-two-channels-message
|
||||||
Effect::MessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match."));
|
Effect::MessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mRate == 0.0)
|
||||||
|
mRate = rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mRate <= 0.0)
|
if (mRate <= 0.0)
|
||||||
@ -361,22 +361,26 @@ bool VampEffect::Process()
|
|||||||
|
|
||||||
std::vector<std::shared_ptr<Effect::AddedAnalysisTrack>> addedTracks;
|
std::vector<std::shared_ptr<Effect::AddedAnalysisTrack>> addedTracks;
|
||||||
|
|
||||||
for (auto left : inputTracks()->Leaders< const WaveTrack >() )
|
for (auto leader : inputTracks()->Leaders<const WaveTrack>())
|
||||||
{
|
{
|
||||||
|
auto channelGroup = TrackList::Channels(leader);
|
||||||
|
auto left = *channelGroup.first++;
|
||||||
|
|
||||||
sampleCount lstart, rstart = 0;
|
sampleCount lstart, rstart = 0;
|
||||||
sampleCount len;
|
sampleCount len;
|
||||||
GetSamples(left, &lstart, &len);
|
GetSamples(left, &lstart, &len);
|
||||||
|
|
||||||
unsigned channels = 1;
|
unsigned channels = 1;
|
||||||
|
|
||||||
const WaveTrack *right{};
|
const WaveTrack *right = *channelGroup.first++;
|
||||||
if (left->GetLinked())
|
if (right)
|
||||||
{
|
{
|
||||||
right = static_cast< const WaveTrack* >( left->GetLink() );
|
|
||||||
channels = 2;
|
channels = 2;
|
||||||
GetSamples(right, &rstart, &len);
|
GetSamples(right, &rstart, &len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: more-than-two-channels
|
||||||
|
|
||||||
size_t step = mPlugin->getPreferredStepSize();
|
size_t step = mPlugin->getPreferredStepSize();
|
||||||
size_t block = mPlugin->getPreferredBlockSize();
|
size_t block = mPlugin->getPreferredBlockSize();
|
||||||
|
|
||||||
|
@ -562,6 +562,8 @@ protected:
|
|||||||
void OnChannelChange(wxCommandEvent & event);
|
void OnChannelChange(wxCommandEvent & event);
|
||||||
void OnMergeStereo(wxCommandEvent & event);
|
void OnMergeStereo(wxCommandEvent & event);
|
||||||
|
|
||||||
|
// TODO: more-than-two-channels
|
||||||
|
// How should we define generalized channel manipulation operations?
|
||||||
void SplitStereo(bool stereo);
|
void SplitStereo(bool stereo);
|
||||||
|
|
||||||
void OnSwapChannels(wxCommandEvent & event);
|
void OnSwapChannels(wxCommandEvent & event);
|
||||||
@ -603,23 +605,28 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
|
|||||||
(display == WaveTrack::Spectrum) && !bAudioBusy);
|
(display == WaveTrack::Spectrum) && !bAudioBusy);
|
||||||
|
|
||||||
AudacityProject *const project = ::GetActiveProject();
|
AudacityProject *const project = ::GetActiveProject();
|
||||||
|
TrackList *const tracks = project->GetTracks();
|
||||||
bool unsafe = EffectManager::Get().RealtimeIsActive() &&
|
bool unsafe = EffectManager::Get().RealtimeIsActive() &&
|
||||||
project->IsAudioActive();
|
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 )
|
if ( isMono )
|
||||||
{
|
{
|
||||||
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
|
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
|
||||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||||
|
|
||||||
TrackList *const tracks = project->GetTracks();
|
|
||||||
auto next = * ++ tracks->Find(pTrack);
|
auto next = * ++ tracks->Find(pTrack);
|
||||||
|
|
||||||
if (isMono) {
|
if (isMono) {
|
||||||
const bool canMakeStereo =
|
const bool canMakeStereo =
|
||||||
(next && !next->GetLinked()
|
(next &&
|
||||||
&& pTrack->GetKind() == Track::Wave
|
TrackList::Channels(next).size() == 1 &&
|
||||||
&& next->GetKind() == Track::Wave);
|
track_cast<WaveTrack*>(next));
|
||||||
|
|
||||||
pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
|
pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
|
||||||
|
|
||||||
int itemId;
|
int itemId;
|
||||||
@ -647,8 +654,8 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
|
|||||||
return end != std::find(checkedIds.begin(), end, id);
|
return end != std::find(checkedIds.begin(), end, id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable this only for properly stereo tracks:
|
||||||
pMenu->Enable(OnSwapChannelsID, !isMono && !unsafe);
|
pMenu->Enable(OnSwapChannelsID, isStereo && !unsafe);
|
||||||
pMenu->Enable(OnSplitStereoID, !isMono && !unsafe);
|
pMenu->Enable(OnSplitStereoID, !isMono && !unsafe);
|
||||||
|
|
||||||
#ifndef EXPERIMENTAL_DA
|
#ifndef EXPERIMENTAL_DA
|
||||||
@ -836,11 +843,16 @@ void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
|
|||||||
/// Merge two tracks into one stereo track ??
|
/// Merge two tracks into one stereo track ??
|
||||||
void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
||||||
{
|
{
|
||||||
|
AudacityProject *const project = ::GetActiveProject();
|
||||||
|
const auto tracks = project->GetTracks();
|
||||||
|
|
||||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||||
wxASSERT(pTrack);
|
wxASSERT(pTrack);
|
||||||
|
|
||||||
|
auto partner = static_cast< WaveTrack * >
|
||||||
|
( *tracks->Find( pTrack ).advance( 1 ) );
|
||||||
|
|
||||||
pTrack->SetLinked(true);
|
pTrack->SetLinked(true);
|
||||||
// Assume partner is wave or null
|
|
||||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
|
||||||
|
|
||||||
if (partner) {
|
if (partner) {
|
||||||
// Set partner's parameters to match target.
|
// Set partner's parameters to match target.
|
||||||
@ -884,51 +896,40 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
|
|||||||
mpData->result = RefreshCode::RefreshAll;
|
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)
|
void WaveTrackMenuTable::SplitStereo(bool stereo)
|
||||||
{
|
{
|
||||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||||
wxASSERT(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
|
int totalHeight = 0;
|
||||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
int nChannels = 0;
|
||||||
wxASSERT(partner);
|
for (auto channel : channels) {
|
||||||
if (!partner)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (partner)
|
|
||||||
{
|
|
||||||
// Keep original stereo track name.
|
// Keep original stereo track name.
|
||||||
partner->SetName(pTrack->GetName());
|
channel->SetName(pTrack->GetName());
|
||||||
if (stereo)
|
if (stereo)
|
||||||
partner->SetPanFromChannelType();
|
channel->SetPanFromChannelType();
|
||||||
partner->SetChannel(Track::MonoChannel);
|
channel->SetChannel(Track::MonoChannel);
|
||||||
|
|
||||||
//On Demand - have each channel add its own.
|
//On Demand - have each channel add its own.
|
||||||
if (ODManager::IsInstanceCreated() && partner->GetKind() == Track::Wave)
|
if (ODManager::IsInstanceCreated())
|
||||||
ODManager::Instance()->MakeWaveTrackIndependent(partner);
|
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);
|
pTrack->SetLinked(false);
|
||||||
//make sure neither track is smaller than its minimum height
|
int averageHeight = totalHeight / nChannels;
|
||||||
if (pTrack->GetHeight() < pTrack->GetMinimizedHeight())
|
|
||||||
pTrack->SetHeight(pTrack->GetMinimizedHeight());
|
|
||||||
if (partner)
|
|
||||||
{
|
|
||||||
if (partner->GetHeight() < partner->GetMinimizedHeight())
|
|
||||||
partner->SetHeight(partner->GetMinimizedHeight());
|
|
||||||
|
|
||||||
|
for (auto channel : channels)
|
||||||
// Make tracks the same height
|
// Make tracks the same height
|
||||||
if (pTrack->GetHeight() != partner->GetHeight())
|
channel->SetHeight( averageHeight );
|
||||||
{
|
|
||||||
pTrack->SetHeight((pTrack->GetHeight() + partner->GetHeight()) / 2.0);
|
|
||||||
partner->SetHeight(pTrack->GetHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mpData->result = RefreshCode::RefreshAll;
|
mpData->result = RefreshCode::RefreshAll;
|
||||||
}
|
}
|
||||||
@ -939,14 +940,19 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
|
|||||||
AudacityProject *const project = ::GetActiveProject();
|
AudacityProject *const project = ::GetActiveProject();
|
||||||
|
|
||||||
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
|
||||||
// Assume partner is wave or null
|
auto channels = TrackList::Channels( pTrack );
|
||||||
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
|
if (channels.size() != 2)
|
||||||
|
return;
|
||||||
|
|
||||||
Track *const focused = project->GetTrackPanel()->GetFocusedTrack();
|
Track *const focused = project->GetTrackPanel()->GetFocusedTrack();
|
||||||
const bool hasFocus =
|
const bool hasFocus = channels.contains( focused );
|
||||||
(focused == pTrack || focused == partner);
|
|
||||||
|
auto first = *channels.begin();
|
||||||
|
auto partner = *channels.rbegin();
|
||||||
|
|
||||||
SplitStereo(false);
|
SplitStereo(false);
|
||||||
pTrack->SetChannel(Track::RightChannel);
|
|
||||||
|
first->SetChannel(Track::RightChannel);
|
||||||
partner->SetChannel(Track::LeftChannel);
|
partner->SetChannel(Track::LeftChannel);
|
||||||
|
|
||||||
TrackList *const tracks = project->GetTracks();
|
TrackList *const tracks = project->GetTracks();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user