Hi JUCErs,
A while back I created a Tracktion Engine Hello World project intended as a least-possible-code TE app that actually produces audio. This worked great with TE v2, but when I upgraded it to TE v3 it stopped making any sound (for me at least, I had a report from another user of it working for them). After being advised by the TE devs that “this use case isn’t really supported” I slightly refactored the app from it’s original console-only flavor to incorporate a super-simple GUI, however this has failed to make any difference whatsoever. The app compiles and runs, but it hits TE assertions that mean nothing to me (note: I would describe myself as an “intermediate” C++ developer, and a JUCE n00b), then crashes.
Here’s the current code, will be cleaned up further once it’s working:
#include <juce_gui_extra/juce_gui_extra.h>
// ---- INTERESTING STUFF STARTS HERE ----
#include <tracktion_engine/tracktion_engine.h>
#include <memory>
using namespace std;
namespace te = tracktion;
using namespace std::literals;
using namespace te::literals;
static void addNoteToClip(
te::MidiClip *midiClip,
int noteNumber,
int velocity,
te::BeatPosition start,
te::BeatDuration duration)
{
midiClip->getSequence().addNote(
noteNumber,
start,
duration,
velocity,
0,
nullptr);
}
//==============================================================================
class MainComponent final : public juce::Component
{
public:
MainComponent(te::Engine &e) : engine(e)
{
DBG("* Initialize MainComponent");
setSize(400, 200);
DBG("* Create an edit");
edit = std::make_unique<te::Edit>(
engine,
te::Edit::forEditing);
DBG("* Create a track");
edit->ensureNumberOfAudioTracks(1);
auto track = te::getAudioTracks(*edit)[0];
DBG("* Get length of 1 bar");
const tracktion::TimeRange oneBarTimeRange(
0s,
edit->tempoSequence.toTime({1, tracktion::BeatDuration()}));
DBG("* Insert a 1 bar long Midi clip");
auto clip = track->insertNewClip(
te::TrackItem::Type::midi,
"Midi Clip",
oneBarTimeRange,
nullptr);
auto midiClip = static_cast<te::MidiClip *>(clip);
DBG("* Add a 4-note C-E-G-C sequence to the clip");
// Note the use of Tracktion's beat position/duration literals
addNoteToClip(midiClip, 60, 100, 0_bp, 0.5_bd);
addNoteToClip(midiClip, 64, 100, 1_bp, 0.5_bd);
addNoteToClip(midiClip, 67, 100, 2_bp, 0.5_bd);
addNoteToClip(midiClip, 72, 100, 3_bp, 0.5_bd);
DBG("* Create a built-in synth plugin instance to play the sequence on");
auto plugin = edit->getPluginCache()
.createNewPlugin(te::FourOscPlugin::xmlTypeName, {})
.get();
auto fourOscPlugin = static_cast<te::FourOscPlugin *>(plugin);
DBG("* Insert the plugin to the track");
track->pluginList.insertPlugin(*fourOscPlugin, 0, nullptr);
DBG("* Get the transport & set it to the start of the edit");
auto &transport = edit->getTransport();
transport.setPosition(0s);
DBG("* Set the transport to loop our clip");
transport.setLoopRange(clip->getEditTimeRange());
transport.looping = true;
DBG("* Begin playback");
transport.play(false);
}
void paint(juce::Graphics &g) override
{
g.fillAll(juce::Colours::darkgrey);
}
private:
te::Engine &engine;
std::unique_ptr<te::Edit> edit;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
};
// ---- BORING JUCE BOILERPLATE FOLLOWS ----
//==============================================================================
class TEHelloWorldApplication : public juce::JUCEApplication
{
public:
TEHelloWorldApplication() {}
const juce::String getApplicationName() override { return JUCE_APPLICATION_NAME_STRING; }
const juce::String getApplicationVersion() override { return JUCE_APPLICATION_VERSION_STRING; }
void initialise(const juce::String &commandLine) override
{
mainWindow.reset(new MainWindow(getApplicationName()));
}
void shutdown() override
{
mainWindow = nullptr;
}
class MainWindow : public juce::DocumentWindow
{
public:
MainWindow(juce::String name)
: DocumentWindow(name,
juce::Desktop::getInstance().getDefaultLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId),
DocumentWindow::allButtons)
{
setUsingNativeTitleBar(true);
te::Engine engine{JUCE_APPLICATION_NAME_STRING};
setContentOwned(new MainComponent(engine), true);
setVisible(true);
}
void closeButtonPressed() override
{
JUCEApplication::getInstance()->systemRequestedQuit();
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
private:
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
START_JUCE_APPLICATION(TEHelloWorldApplication)
And for completeness, here’s CMakeLists.txt
:
cmake_minimum_required(VERSION 3.22)
project(TE_HELLO_WORLD VERSION 0.0.2)
add_subdirectory(tracktion_engine/modules/juce)
add_subdirectory(tracktion_engine/modules)
juce_add_gui_app(TEHelloWorld
PRODUCT_NAME "TEHelloWorld")
target_sources(TEHelloWorld
PRIVATE
Main.cpp)
target_compile_definitions(TEHelloWorld
PRIVATE
JUCE_WEB_BROWSER=0
JUCE_USE_CURL=0
JUCE_APPLICATION_NAME_STRING="$<TARGET_PROPERTY:TEHelloWorld,JUCE_PRODUCT_NAME>"
JUCE_APPLICATION_VERSION_STRING="$<TARGET_PROPERTY:TEHelloWorld,JUCE_VERSION>")
target_link_libraries(TEHelloWorld
PRIVATE
tracktion::tracktion_engine
juce::juce_gui_extra
#juce::juce_audio_utils
PUBLIC
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags)
set_property(TARGET TEHelloWorld PROPERTY CXX_STANDARD 20)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(TEHelloWorld PRIVATE "-latomic")
endif()
Running this results in the following console output:
[boring build log omitted]
JUCE v8.0.6
Settings file: /Users/ms/Library/TEHelloWorld/Settings.xml
Audio block size: 512 Rate: 44100
Creating Default Controllers...
* Initialize MainComponent
* Create an edit
Edit loaded in: 2 ms
* Create a track
* Get length of 1 bar
* Insert a 1 bar long Midi clip
* Add a 4-note C-E-G-C sequence to the clip
* Create a built-in synth plugin instance to play the sequence on
* Insert the plugin to the track
* Get the transport & set it to the start of the edit
* Set the transport to loop our clip
* Begin playback
JUCE Assertion failure in tracktion_DeviceManager.cpp:432
Cleaning up temp files..
JUCE Assertion failure in tracktion_Engine.cpp:152
./build-and-run.sh: line 3: 41355 Segmentation fault: 11 ./cmake-build/TEHelloWorld_artefacts/Debug/TEHelloWorld.app/Contents/MacOS/TEHelloWorld
As you can see there are two assertion failures in TE here. To save you looking them up the first one is jassert (activeContexts.isEmpty());
and the second one is jassert (deviceManager != nullptr);
. So that’s where I’ve got to.
I suspect that this problem is something to do with me not understanding object lifetimes properly, but beyond that I have no clue. And tbh I feel like this is just an example of a larger problem I have, which is that I have no way of figuring out TE-related bugs except spamming you lovely people. With plain JUCE, I can google my problems and almost always find some pretty good advice, but as soon as TE enters the picture the process becomes incredibly painful. Anyway, maybe that’s a me problem…