Unresolved external symbols

I’m trying to make Python bindings for Tracktion Engine. I start the code off with

Engine engine {juce::String("Soundshop")};
Edit edit{ engine, Edit::EditRole::forEditing };
TransportControl& transport{ edit.getTransport() };

which I got from Render Example? - #3 by bwall and tracktion_engine/tutorials/03 - StepSequencerDemo.md at develop · Tracktion/tracktion_engine · GitHub

And I get the errors when trying to build in Visual Studio:

unresolved external symbol "public: __cdecl tracktion::engine::Engine::~Engine(void)" (??1Engine@engine@tracktion@@QEAA@XZ)
unresolved external symbol "public: __cdecl tracktion::engine::Engine::Engine(class juce::String)" (??0Engine@engine@tracktion@@QEAA@VString@juce@@@Z)
unresolved external symbol "public: virtual __cdecl tracktion::engine::Edit::~Edit(void)" (??1Edit@engine@tracktion@@UEAA@XZ)
unresolved external symbol "public: __cdecl tracktion::engine::Edit::Edit(class tracktion::engine::Engine &,enum tracktion::engine::Edit::EditRole)" (??0Edit@engine@tracktion@@QEAA@AEAVEngine@12@W4EditRole@012@@Z)
4 unresolved externals

Here’s the complete code:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "tracktion_engine/tracktion_engine.h"

namespace py = pybind11;
using namespace tracktion::engine;

Engine engine {juce::String("Soundshop")};
Edit edit{ engine, Edit::EditRole::forEditing };
TransportControl& transport{ edit.getTransport() };

PYBIND11_MODULE(tracktion_engine, m) {
  m.doc() = "Python bindings for Tracktion Engine, a framework for building DAW-like audio applications.";

  // Track class binding
  py::class_<Track, std::unique_ptr<Track, py::nodelete>>(m, "Track")
    .def("get_name", &Track::getName, "Returns the name of the track.");

  // AudioTrack class binding (inherits from Track)
  py::class_<AudioTrack, Track, std::unique_ptr<AudioTrack, py::nodelete>>(m, "AudioTrack")
    ;

  // Clip class binding
  py::class_<Clip, std::unique_ptr<Clip, py::nodelete>>(m, "Clip")
    .def("get_name", &Clip::getName, "Returns the name of the clip.")
    .def("get_position", &Clip::getPosition, "Returns the position of the clip in seconds.");
      
  // ValueTree class binding (used for Edit state)
  py::class_<juce::ValueTree>(m, "ValueTree")
    .def(py::init<juce::Identifier>(), py::arg("type"), "Creates a ValueTree with the specified type.")
    .def("set_property", &juce::ValueTree::setProperty, py::arg("name"), py::arg("value"), py::arg("undo_manager") = nullptr,
      "Sets a property on the ValueTree.");

}

I tried ‘rebuild solution’ and still get the errors. Halp! Thanks.

What does your CMakeLists.txt look like? That is to say, how are you linking tracktion_engine as a library with your project?

I don’t use CMakeLists.txt, just a visual studio project. I made the project using projucer and selecting dynamic link library, then i just point vs to the tracktion engine source and the pybind11 source in the project configuration in VS and try to code bindings and compile the code. I get these errors while trying to compile that dll. I haven’t even gotten a chance to try to use the dll in an actual Python program yet. To do that I would just rename the dll to *.pyd or whatever it is and “import TracktionPythonBindings”.

If you put your project into a public repo, I’ll have a bash at converting it to CMake for you.

For such things as working out bindings for scripting languages, CMake really is a lot more productive. Many of the tools and libraries and binding scaffolding of script languages are CMake-friendly .. if you have to poke things into a GUI such as VS et al., its a bit frustrating and not quite future proof.

I understand the convenience of the GUI IDE world, really I do, but I cannot stress enough how much more productive you will be once you learn to bend CMake to your will. Its really not that hard - certainly not more complex than learning some of JUCE’ own inner workings.

At some point, the hand-holding fades away and the power to be had in having a good grasp of tooling and methodology takes over.

Yeah, Tracktion Engine isn’t designed to be built as a dll, it’s certainly not provided as a pre-built one. It’s a juce module so should be added in a similar way to the juce ones but we only really support cmake these days.

1 Like

@dave96 Oh, I had just been creating a project with projucer and then adding the tracktion engine modules directory to my include paths. I don’t know how to add it as a juce module. I clicked + in the projucer modules section and tried the main tracktion directory and a few different subdirectories, and none of them worked. Does it make a difference if I add it as a module, and how do I do that?

In PJ you should be able to use “Add module from folder”.

I downloaded the tracktion release from the release pages on tracktion’s github, then I extracted it, then i tried adding a module from folder in juce and entering the directory i extracted to, and it says, 'this wasn’t a valid module folder!"

I think you need to add the three modules, not the root repo tracktion_engine, tracktion_core & tracktion_graph, they’re all in /modules.

downloaded the tracktion release from the release pages on tracktion’s github, then I extracted it, then i tried adding a module from folder in juce and entering the directory i extracted to

Seriously, this approach is somewhat error prone. If you use CMake for this project, you can then later integrate it easier with future projects.

Python bindings have a lot of inherent value to us all - if you want to get this working properly, we can work through a repo together and get it sorted out smoothly. Its far, far better to use FetchContent to get all of this module setup done properly, than poking at the Projucer. This is because getting it working this way, results in reproducible build results ..

(BTW, by hand-holding, I meant that Projucer is doing that for you. Also, one thing that has to be noted: you can convert almost any .jucer to CMakeLists.txt file with one small and simple trick: use Grok! Seriously, it works very well these days .. thanks, in no small part I think, to all the times many of have been doing it manually and then fixing it, over the years..)

Ok, thanks.

I deleted the message in which I said I got it to build because I was mistaken.
But I took @dave96’s advice and added the tracktion modules in projucer, and I didn’t even include my own code this time, and it wouldn’t build. Maybe it doesn’t matter if we’re going to do this with a cmakelists.txt file anyway, but here’s the build output I got:

Build started at 11:00 AM...
1>------ Build started: Project: te_py_bind_DynamicLibrary, Configuration: Debug x64 ------
1>include_juce_analytics.cpp
1>include_juce_animation.cpp
1>include_juce_audio_basics.cpp
1>include_juce_audio_devices.cpp
1>include_juce_audio_formats.cpp
1>include_juce_audio_processors_ara.cpp
1>include_juce_audio_processors_lv2_libs.cpp
1>include_juce_audio_utils.cpp
1>include_juce_box2d.cpp
1>include_juce_core_CompilationTime.cpp
1>include_juce_cryptography.cpp
1>include_juce_data_structures.cpp
1>include_juce_dsp.cpp
1>include_juce_events.cpp
1>include_juce_graphics_Harfbuzz.cpp
1>include_juce_gui_extra.cpp
1>include_juce_javascript.cpp
1>include_juce_midi_ci.cpp
1>include_juce_opengl.cpp
1>include_juce_osc.cpp
1>include_juce_product_unlocking.cpp
1>include_juce_video.cpp
1>include_tracktion_core.cpp
1>include_tracktion_engine_airwindows_1.cpp
1>include_tracktion_engine_airwindows_2.cpp
1>include_tracktion_engine_airwindows_3.cpp
1>include_tracktion_engine_audio_files.cpp
1>include_tracktion_engine_model_1.cpp
1>include_tracktion_engine_model_2.cpp
1>include_tracktion_engine_playback.cpp
1>include_tracktion_engine_plugins.cpp
1>include_tracktion_engine_timestretch.cpp
1>include_tracktion_engine_utils.cpp
1>include_tracktion_graph.cpp
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(548,20): warning C4127: conditional expression is constant
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_1.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(548,20):
1>    consider using 'if constexpr' statement instead
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(552,25): warning C4127: conditional expression is constant
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_1.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(552,25):
1>    consider using 'if constexpr' statement instead
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(556,25): warning C4127: conditional expression is constant
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_1.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(556,25):
1>    consider using 'if constexpr' statement instead
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(560,16): warning C4127: conditional expression is constant
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_1.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\model\automation\tracktion_ParameterControlMappings.cpp(560,16):
1>    consider using 'if constexpr' statement instead
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C2039: 'runDispatchLoopUntilTrue': is not a member of 'tracktion::engine::test_utilities'
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_2.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(17,11):
1>    see declaration of 'tracktion::engine::test_utilities'
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C3861: 'runDispatchLoopUntilTrue': identifier not found
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_model_2.cpp')
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C2039: 'runDispatchLoopUntilTrue': is not a member of 'tracktion::engine::test_utilities'
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_plugins.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(17,11):
1>    see declaration of 'tracktion::engine::test_utilities'
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C3861: 'runDispatchLoopUntilTrue': identifier not found
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_plugins.cpp')
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C2039: 'runDispatchLoopUntilTrue': is not a member of 'tracktion::engine::test_utilities'
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_utils.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(17,11):
1>    see declaration of 'tracktion::engine::test_utilities'
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C3861: 'runDispatchLoopUntilTrue': identifier not found
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_utils.cpp')
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C2039: 'runDispatchLoopUntilTrue': is not a member of 'tracktion::engine::test_utilities'
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_playback.cpp')
1>    C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(17,11):
1>    see declaration of 'tracktion::engine::test_utilities'
1>C:\tracktion_engine-3.1.0\modules\tracktion_engine\utilities\tracktion_TestUtilities.h(170,25): error C3861: 'runDispatchLoopUntilTrue': identifier not found
1>(compiling source file '../../JuceLibraryCode/include_tracktion_engine_playback.cpp')
1>C:\visual studio projects\SoundShop\te_py_bind\NewProject\JuceLibraryCode\include_tracktion_engine_model_1.cpp(1,1): error C1128: number of sections exceeded object file format limit: compile with /bigobj
1>cl : command line  error D8040: error creating or communicating with child process
1>Done building project "te_py_bind_DynamicLibrary.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 11:01 AM and took 49.307 seconds ==========

Btw, I don’t even know what FetchContent is. =/

But I guess if you insist I should be using a makefile, I’ll try to go down that route. What do I need to do/know?

Note that make/makefiles and Cmake/Cmakelists are 2 different things, make being the older one and not supported with Juce/Tracktion Engine. Just mentioning this so that you don’t get sidetracked trying to use make/makefiles for this.

1 Like

Ok, looks like you’re compiling the Tracktion modules now which is good.
Looks like we have some testing code that’s not disabled though. Can you add this to your global defines in PJ (if you’re still using that) JUCE_MODAL_LOOPS_PERMITTED=1. I think it will build then.

Thanks, I finally got it to compile!
I did run into a hiccup, it said an object file had too many sections and I had to build with /bigobj, so I added that to the command line setting, and it worked. I mention this because if there weren’t some drawback or potential problems with using /bigobj, then you’d think it wouldn’t even be an option, it would just be a part of the compiler’s default behavior..

Argh, actually trying to import my compiled dll in Python failed. I renamed the dll to a pyd, and when I tried to import it in Python, it said, DLL load failed while importing NewProject: A dynamic link library (DLL) initialization routine failed.

Nevermind, I fixed it with the help of AI. I didn’t realize that the name of the module you pass to PYBIND11_MODULE() must be the same as the dll’s filename. Oh, er.. that was a different problem I fixed. After I fixed this problem by moving all the init code to within the PYBIND11_MODULE part.