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:
parent
80f95d407a
commit
70b7487820
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user