Skip to content

Commit

Permalink
Recording & Latency: Fixed some issues with round-trip latency and ad…
Browse files Browse the repository at this point in the history
…ded tests around them (#222)

* WaveNode:   Removed the 2-sample latency introduced with the LagrangeResampler when using AudioClipBase::setUsesProxy (false)

* WaveNode:   Fixed a conversion warning

* Recording Tests:   Added a track-to-track recording test

* Tests:   Added a TestPropertyStorage to avoid saving state between test runs

* Tests:   Added a test for WaveInputDevice recording alignment

* DeviceManager:   Fixed a problem keeping default wave devices if the PropertyStorage don't store anything (as in the case of tests)

* Hosted audio device:   Added the ability to apply input and output latency for testing purposes

* Tests:    More WaveInputDevice recording tests

* Recording:   Reset the playhead position before starting recording to avoid missing the first block

* Recording:   Renamed a test

* Recording:   Added a test to do a track-to-track recording via a looped-back InsertPlugin

* Recording:   Started to add some round-trip latency detection functions

* Recording:   Added a test to detect latency, apply it and do a loop back recording

* Recording:   Removed a now unnecessary recording position adjustment

* Recording:   Fixed an issue with large numbers of tracks recording where the same block could be recorded twice

* Recording:   Added some extra checks

* Recording:   Removed an unnecessary playHeadWrapper->setPosition call

* Aux Send:   Ensured the "ownerTrack" member is actually updated when the track is changed
  • Loading branch information
drowaudio authored Jul 29, 2024
1 parent a2c7a3e commit fdab8cf
Show file tree
Hide file tree
Showing 32 changed files with 1,131 additions and 60 deletions.
40 changes: 39 additions & 1 deletion examples/TestRunner/TestRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,40 @@ class TestEngineBehaviour : public EngineBehaviour
};


//==============================================================================
//==============================================================================
class TestPropertyStorage : public PropertyStorage
{
public:
TestPropertyStorage (juce::StringRef appName_)
: PropertyStorage (appName_)
{
getAppCacheFolder().deleteRecursively (false);
getAppPrefsFolder().deleteRecursively (false);
}

~TestPropertyStorage() override
{
getAppCacheFolder().deleteRecursively (false);
getAppPrefsFolder().deleteRecursively (false);
}

//==============================================================================
void removeProperty (SettingID) override {}
juce::var getProperty (SettingID, const juce::var& defaultValue) override { return defaultValue; }
void setProperty (SettingID, const juce::var&) override {}
std::unique_ptr<juce::XmlElement> getXmlProperty (SettingID) override { return {}; }
void setXmlProperty (SettingID, const juce::XmlElement&) override {}

//==============================================================================
void removePropertyItem (SettingID, juce::StringRef) override {}
juce::var getPropertyItem (SettingID, juce::StringRef, const juce::var& defaultValue) override { return defaultValue; }
void setPropertyItem (SettingID, juce::StringRef, const juce::var&) override {}
std::unique_ptr<juce::XmlElement> getXmlPropertyItem (SettingID, juce::StringRef) override { return {}; }
void setXmlPropertyItem (SettingID, juce::StringRef, const juce::XmlElement&) override {}
};


//==============================================================================
//==============================================================================
struct CoutLogger : public Logger
Expand Down Expand Up @@ -173,7 +207,10 @@ namespace TestRunner
CoutLogger logger;
Logger::setCurrentLogger (&logger);

tracktion_engine::Engine engine { ProjectInfo::projectName, std::make_unique<TestUIBehaviour>(), std::make_unique<TestEngineBehaviour>() };
tracktion_engine::Engine engine { std::make_unique<TestPropertyStorage> (ProjectInfo::projectName),
std::make_unique<TestUIBehaviour>(),
std::make_unique<TestEngineBehaviour>() };
engine.getTemporaryFileManager().getTempDirectory().deleteRecursively (false);

UnitTestRunner testRunner;
testRunner.setAssertOnFailure (true);
Expand Down Expand Up @@ -215,6 +252,7 @@ namespace TestRunner
const auto doctestFailed = doctestContext.run();

Logger::setCurrentLogger (nullptr);
engine.getTemporaryFileManager().getTempDirectory().deleteRecursively (false);

return (numFailues > 0 || doctestFailed) ? 1 : 0;
}
Expand Down
3 changes: 3 additions & 0 deletions modules/tracktion_core/tracktion_TestConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define GRAPH_UNIT_TESTS_EDITNODE 1

#define ENGINE_UNIT_TESTS_AUTOMATION 1
#define ENGINE_UNIT_TESTS_AUX_SEND 1
#define ENGINE_UNIT_TESTS_CLIPBOARD 1
#define ENGINE_UNIT_TESTS_CLIPSLOT 1
#define ENGINE_UNIT_TESTS_CONSTRAINED_CACHED_VALUE 1
Expand All @@ -27,6 +28,7 @@
#define ENGINE_UNIT_TESTS_EDIT_TIME 1
#define ENGINE_UNIT_TESTS_FREEZE 1
#define ENGINE_UNIT_TESTS_FOLLOW_ACTIONS 1
#define ENGINE_UNIT_TESTS_LATENCY 1
#define ENGINE_UNIT_TESTS_LAUNCH_HANDLE 1
#define ENGINE_UNIT_TESTS_LAUNCHER_CLIP_PLAYBACK_HANDLE 1
#define ENGINE_UNIT_TESTS_LAUNCH_QUANTISATION 1
Expand All @@ -47,6 +49,7 @@
#define ENGINE_UNIT_TESTS_VOLPANPLUGIN 1
#define ENGINE_UNIT_TESTS_TEMPO_SEQUENCE 1
#define ENGINE_UNIT_TESTS_QUANTISATION_TYPE 1
#define ENGINE_UNIT_TESTS_WAVE_INPUT_DEVICE 1

// Defined in tracktion_graph
#define GRAPH_UNIT_TESTS_PLAYHEAD 1
Expand Down
16 changes: 16 additions & 0 deletions modules/tracktion_engine/model/edit/tracktion_EditUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1324,5 +1324,21 @@ bool isRecording (EditPlaybackContext& epc)
return false;
}

InputDeviceInstance::Destination* assignTrackAsInput (AudioTrack& destTrack, const AudioTrack& sourceTrack, InputDevice::DeviceType type)
{
assert (&sourceTrack.edit == &destTrack.edit);
assert (type == InputDevice::trackMidiDevice || type == InputDevice::trackWaveDevice);

auto& edit = destTrack.edit;
auto sourceDevice = type == InputDevice::trackMidiDevice
? static_cast<InputDevice*> (&sourceTrack.getMidiInputDevice())
: static_cast<InputDevice*> (&sourceTrack.getWaveInputDevice());
edit.getTransport().ensureContextAllocated();
edit.getEditInputDevices().getInstanceStateForInputDevice (*sourceDevice);
auto res = edit.getCurrentPlaybackContext()->getInputFor (sourceDevice)->setTarget (destTrack.itemID, false, nullptr);

return res ? *res : nullptr;
}


}} // namespace tracktion { inline namespace engine
3 changes: 3 additions & 0 deletions modules/tracktion_engine/model/edit/tracktion_EditUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ tl::expected<Clip::Array, juce::String> punchOutRecording (InputDeviceInstance&)
/** Returns true if any inputs are currently recording. */
bool isRecording (EditPlaybackContext&);

/** Creates an InputDeviceInstance::Destination on the destinationTrack from the sourceTrack if possible. */
InputDeviceInstance::Destination* assignTrackAsInput (AudioTrack& destinationTrack, const AudioTrack& sourceTrack, InputDevice::DeviceType);


//==============================================================================
/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,11 @@ class WaveInputDeviceInstance : public InputDeviceInstance
(choc::buffer::FrameCount) numSamples));
}

// If we haven't actually started playing yet, don't record the block as we
// might get more than one block for the same position
if (! context.isPlaying())
return;

{
const auto blockStart = context.globalStreamTimeToEditTimeUnlooped (streamTime);
const std::shared_lock sl (contextLock);
Expand Down Expand Up @@ -1500,6 +1505,11 @@ void WaveInputDevice::setStereoPair (bool stereo)
dm.setDeviceInChannelStereo (deviceChannels[0].indexInDevice, stereo);
}

void WaveInputDevice::setRecordAdjustment (TimeDuration d)
{
setRecordAdjustmentMs (d.inSeconds() * 1000.0);
}

void WaveInputDevice::setRecordAdjustmentMs (double ms)
{
recordAdjustMs = juce::jlimit (-500.0, 500.0, ms);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class WaveInputDevice : public InputDevice
InputDeviceInstance* createInstance (EditPlaybackContext&) override;

//==============================================================================
void setRecordAdjustment (TimeDuration);
TimeDuration getRecordAdjustment() const { return TimeDuration::fromSeconds (recordAdjustMs / 1000.0); }
void setRecordAdjustmentMs (double ms);
double getRecordAdjustmentMs() const { return recordAdjustMs; }
bool isStereoPair() const;
Expand Down
Loading

0 comments on commit fdab8cf

Please sign in to comment.