1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 00:19:27 +02:00

Bug 2128 fix

Fixes incorrect recording speed when Track rate not matched to Project Rate.
Fix by binarywisdom. Pull request 423.
This commit is contained in:
SteveDaulton 2020-04-20 18:54:25 +01:00
parent 80f95d407a
commit 70b7487820
3 changed files with 136 additions and 13 deletions

View File

@ -39,6 +39,7 @@ Paul Licameli split from ProjectManager.cpp
#include "widgets/ErrorDialog.h"
#include "widgets/MeterPanelBase.h"
#include "widgets/Warning.h"
#include "widgets/AudacityMessageBox.h"
static AudacityProject::AttachedObjects::RegisteredFactory
@ -383,7 +384,7 @@ void ProjectAudioManager::Pause()
}
WaveTrackArray ProjectAudioManager::ChooseExistingRecordingTracks(
AudacityProject &proj, bool selectedOnly)
AudacityProject &proj, bool selectedOnly, double targetRate)
{
auto p = &proj;
size_t recordingChannels =
@ -391,6 +392,7 @@ WaveTrackArray ProjectAudioManager::ChooseExistingRecordingTracks(
bool strictRules = (recordingChannels <= 2);
// Iterate over all wave tracks, or over selected wave tracks only.
// If target rate was specified, ignore all tracks with other rates.
//
// In the usual cases of one or two recording channels, seek a first-fit
// unbroken sub-sequence for which the total number of channels matches the
@ -414,6 +416,9 @@ WaveTrackArray ProjectAudioManager::ChooseExistingRecordingTracks(
WaveTrackArray candidates;
const auto range = trackList.Leaders<WaveTrack>();
for ( auto candidate : selectedOnly ? range + &Track::IsSelected : range ) {
if (targetRate != RATE_NOT_SELECTED && candidate->GetRate() != targetRate)
continue;
// count channels in this track
const auto channels = TrackList::Channels( candidate );
unsigned nChannels = channels.size();
@ -472,24 +477,53 @@ void ProjectAudioManager::OnRecord(bool altAppearance)
if (t1 == t0)
t1 = DBL_MAX;
auto options = DefaultPlayOptions(*p);
WaveTrackArray existingTracks;
// Checking the selected tracks: counting them and
// making sure they all have the same rate
const auto selectedTracks{ GetPropertiesOfSelected(*p) };
const int rateOfSelected{ selectedTracks.rateOfSelected };
const int numberOfSelected{ selectedTracks.numberOfSelected };
const bool allSameRate{ selectedTracks.allSameRate };
if (!allSameRate) {
AudacityMessageBox(XO("TRACK SELECTION PROBLEM:\nthe tracks selected "
"for recording must all have the same sampling rate"),
XO("Unfitting track selection"),
wxICON_ERROR | wxCENTRE);
return;
}
if (appendRecord) {
const auto trackRange = TrackList::Get( *p ).Any< const WaveTrack >();
// Try to find wave tracks to record into. (If any are selected,
// try to choose only from them; else if wave tracks exist, may record into any.)
existingTracks = ChooseExistingRecordingTracks(*p, true);
if ( !existingTracks.empty() )
t0 = std::max( t0,
( trackRange + &Track::IsSelected ).max( &Track::GetEndTime ) );
existingTracks = ChooseExistingRecordingTracks(*p, true, rateOfSelected);
if (!existingTracks.empty()) {
t0 = std::max(t0,
(trackRange + &Track::IsSelected).max(&Track::GetEndTime));
}
else {
existingTracks = ChooseExistingRecordingTracks(*p, false);
if (numberOfSelected > 0 && rateOfSelected != options.rate) {
AudacityMessageBox(XO("TRACK SELECTION PROBLEM:\n"
"Not enough tracks are selected for recording on non-project rate.\n"
"(keep in mind that Audacity doesn\'t allow "
"using only one channel of a stereo track)"),
XO("Insufficient track selection"),
wxICON_ERROR | wxCENTRE);
return;
}
existingTracks = ChooseExistingRecordingTracks(*p, false, options.rate);
t0 = std::max( t0, trackRange.max( &Track::GetEndTime ) );
// If suitable tracks still not found, will record into NEW ones,
// but the choice of t0 does not depend on that.
}
// Whether we decided on NEW tracks or not:
if (t1 <= selectedRegion.t0() && selectedRegion.t1() > selectedRegion.t0()) {
t1 = selectedRegion.t1(); // record within the selection
@ -515,7 +549,10 @@ void ProjectAudioManager::OnRecord(bool altAppearance)
}
transportTracks.captureTracks = existingTracks;
auto options = DefaultPlayOptions( *p );
if (rateOfSelected != RATE_NOT_SELECTED)
options.rate = rateOfSelected;
DoRecord(*p, transportTracks, t0, t1, altAppearance, options);
}
}
@ -1025,8 +1062,6 @@ void ProjectAudioManager::StopIfPaused()
Stop();
}
#include "widgets/AudacityMessageBox.h"
bool ProjectAudioManager::DoPlayStopSelect( bool click, bool shift )
{
auto &project = mProject;
@ -1108,3 +1143,31 @@ static RegisteredMenuItemEnabler stopIfPaused{{
ProjectAudioManager::Get( project ).StopIfPaused();
}
}};
// GetSelectedProperties collects information about
// currently selected audio tracks
PropertiesOfSelected
GetPropertiesOfSelected(const AudacityProject &proj)
{
double rateOfSelection{ RATE_NOT_SELECTED };
PropertiesOfSelected result;
result.allSameRate = true;
const auto selectedTracks{
TrackList::Get(proj).Selected< const WaveTrack >() };
for (const auto & track : selectedTracks)
{
if (rateOfSelection != RATE_NOT_SELECTED &&
track->GetRate() != rateOfSelection)
result.allSameRate = false;
else if (rateOfSelection == RATE_NOT_SELECTED)
rateOfSelection = track->GetRate();
}
result.numberOfSelected = selectedTracks.size();
result.rateOfSelected = rateOfSelection;
return result;
}

View File

@ -17,6 +17,8 @@ Paul Licameli split from ProjectManager.h
#include "AudioIOListener.h" // to inherit
#include "ClientData.h" // to inherit
constexpr int RATE_NOT_SELECTED{ -1 };
class AudacityProject;
struct AudioIOStartStreamOptions;
class TrackList;
@ -47,7 +49,8 @@ public:
// Find suitable tracks to record into, or return an empty array.
static WaveTrackArray ChooseExistingRecordingTracks(
AudacityProject &proj, bool selectedOnly);
AudacityProject &proj, bool selectedOnly,
double targetRate = RATE_NOT_SELECTED);
static bool UseDuplex();
@ -161,6 +164,15 @@ private:
AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project );
AudioIOStartStreamOptions DefaultSpeedPlayOptions( AudacityProject &project );
struct PropertiesOfSelected
{
bool allSameRate{ false };
int rateOfSelected{ RATE_NOT_SELECTED };
int numberOfSelected{ 0 };
};
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj);
#include "commands/CommandFlag.h"
extern const ReservedCommandFlag

View File

@ -282,6 +282,39 @@ void OnTimerRecord(const CommandContext &context)
wxICON_INFORMATION | wxOK);
return;
}
// We check the selected tracks to see if there is enough of them to accomodate
// all input channels and all of them have the same sampling rate.
// Those checks will be later performed by recording function anyway,
// but we want to warn the user about potential problems from the very start.
const auto selectedTracks{ GetPropertiesOfSelected(project) };
const int rateOfSelected{ selectedTracks.rateOfSelected };
const int numberOfSelected{ selectedTracks.numberOfSelected };
const bool allSameRate{ selectedTracks.allSameRate };
if (!allSameRate) {
AudacityMessageBox(XO("TRACK SELECTION PROBLEM:\nthe tracks selected "
"for recording must all have the same sampling rate"),
XO("Unfitting track selection"),
wxICON_ERROR | wxCENTRE);
return;
}
const auto existingTracks{ ProjectAudioManager::ChooseExistingRecordingTracks(project, true, rateOfSelected) };
if (existingTracks.empty()) {
if (numberOfSelected > 0 && rateOfSelected != settings.GetRate()) {
AudacityMessageBox(XO("TRACK SELECTION PROBLEM:\n"
"Not enough tracks are selected for recording on non-project rate.\n"
"(keep in mind that Audacity doesn\'t allow "
"using only one channel of a stereo track)"),
XO("Insufficient track selection"),
wxICON_ERROR | wxCENTRE);
return;
}
}
// We use this variable to display "Current Project" in the Timer Recording
// save project field
bool bProjectSaved = ProjectFileIO::Get( project ).IsProjectSaved();
@ -374,9 +407,23 @@ void OnPunchAndRoll(const CommandContext &context)
viewInfo.selectedRegion.collapseToT0();
double t1 = std::max(0.0, viewInfo.selectedRegion.t1());
// Checking the selected tracks: making sure they all have the same rate
const auto selectedTracks{ GetPropertiesOfSelected(project) };
const int rateOfSelected{ selectedTracks.rateOfSelected };
const bool allSameRate{ selectedTracks.allSameRate };
if (!allSameRate) {
AudacityMessageBox(XO("TRACK SELECTION PROBLEM:\nthe tracks selected "
"for recording must all have the same sampling rate"),
XO("Unfitting track selection"),
wxICON_ERROR | wxCENTRE);
return;
}
// Decide which tracks to record in.
auto tracks =
ProjectAudioManager::ChooseExistingRecordingTracks(project, true);
ProjectAudioManager::ChooseExistingRecordingTracks(project, true, rateOfSelected);
if (tracks.empty()) {
int recordingChannels =
std::max(0L, gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2));
@ -384,7 +431,7 @@ void OnPunchAndRoll(const CommandContext &context)
(recordingChannels == 1)
? XO("Please select in a mono track.")
: (recordingChannels == 2)
? XO("Please select in a stereo track.")
? XO("Please select in a stereo track or two mono tracks.")
: XO("Please select at least %d channels.").Format( recordingChannels );
ShowErrorDialog(&window, XO("Error"), message, url);
return;
@ -471,6 +518,7 @@ void OnPunchAndRoll(const CommandContext &context)
// Try to start recording
auto options = DefaultPlayOptions( project );
options.rate = rateOfSelected;
options.preRoll = std::max(0L,
gPrefs->Read(AUDIO_PRE_ROLL_KEY, DEFAULT_PRE_ROLL_SECONDS));
options.pCrossfadeData = &crossfadeData;