kunitoki / popsicle

Popsicle aims to bridge the JUCE c++ framework to python.
https://pypi.org/project/popsicle
Other
168 stars 8 forks source link

popsicle + dawdreamer #11

Open shakfu opened 2 years ago

shakfu commented 2 years ago

Hi, very cool project, thanks for sharing!! I just mentioned it in the discussions sections of another cool juce + python project. I think these two are complementary and I they work nice together.

I made a somewhat crude demo which is attached.

You will need to pip install dawdreamer and, of course, pip install popsicle

demo.py.zip

shakfu commented 2 years ago

Here's another iteration to address a segfaulting bug with some command line options

demo.2.zip

kunitoki commented 2 years ago

Sounds great! Thanks for mentioning it. What is the benefit of using dawdreamer over pythonized juce and the c++ capabilities of cppyy?

shakfu commented 2 years ago

Thanks for your reply @kunitoki

What is the benefit of using dawdreamer over pythonized juce and the c++ capabilities of cppyy

I would also add spotify's pedalboard to the same category as dawdreamer: both are pip-installable python packages and both use pybind11 to integrate some subset of JUCE's audio capabilities to enable the creation of audio processing graphs.

I'm sure you can achieve the same with cppyy, but the benefit is that they already exist and already provide novel and differentiated features: midi playback and faust-processing with dawdreamer, and 'strong thread-safety, memory usage, and speed guarantees' with pedalboard.

kunitoki commented 2 years ago

Yeah i understand, they are python packages afterall so they could integrate nicely

turian commented 8 months ago

@shakfu so what does your demo actually do?

shakfu commented 8 months ago

@turian It was some time since I posted it, and I tried to run popsicle examples just now but was getting "uncaught exception in JIT" warnings and execution issues on a Mac m1 machine.

But I think the point I was trying to make is evident from a function calling dawdreamer where a sine wave file is rendered from a daydreamer audio traph which contains a node containing faust code and then displayed on the popsicle sample player.

import dawdreamer as daw
...
def render_sine(path='sine.wav'):
    engine = daw.RenderEngine(SAMPLE_RATE, 512)  # 512 block size
    faust_processor = engine.make_faust_processor("faust")
    faust_processor.set_dsp_string(
        """
        declare name "MySine";
        freq = hslider("freq", 440, 0, 20000, 0);
        gain = hslider("vol[unit:dB]", 0, -120, 20, 0) : ba.db2linear;
        process = freq : os.osc : _*gain <: si.bus(2);
        """
        )
    print(faust_processor.get_parameters_description())
    engine.load_graph([
                       (faust_processor, [])
    ])
    faust_processor.set_parameter("/MySine/freq", 440.)  # 440 Hz
    faust_processor.set_parameter("/MySine/vol", -6.)  # -6 dB volume

    engine.set_bpm(120.)
    engine.render(4., beats=True)  # render 4 beats.
    audio = engine.get_audio()  # shaped (2, N samples)
    wavfile.write(path, SAMPLE_RATE, audio.transpose())
    return path
kunitoki commented 6 months ago

I've started a complete rewrite of popsicle, and i'm currently being addressing audio. The new popsicle should be more stable and break less often as it doesn't rely on cppyy and overall it is manually finetuned.

shakfu commented 6 months ago

Hi @kunitoki that sounds great!

kunitoki commented 6 months ago

I've released the current progress (not really 1.0 but we are approaching it). Feel free to try and report back any feedback !

shakfu commented 6 months ago

@kunitoki I just downloaded it, build it from source and ran the examples (I'm on a M1 MacBook Air).

On the build side: it was very easy and non-problematic to build, your CMakeExtension class is pretty cool, I liked the default universal2 architecture choice and the extension size is a nice 20Mb).

Examples ran great except for drawables.py which crashed on me... I'm not sure was the demo folder contained since it was in cpp.

But overall congrats.. it's looking good. I definitely prefer this pybind11 version of popsicle to the prior one.

kunitoki commented 6 months ago

Nice, glad to hear that!

kunitoki commented 6 months ago

Btw, i saw that you correctly embedded the standard library as a zip file, it would be great to be able to have a step in the demo app that bundles the python std lib so it's standalone. The tricky part is doing it cross platform.

shakfu commented 6 months ago

@kunitoki
Back in the day, I posted about this in stack-overflow.

I have evolved somewhat since then, and now I routinely embed the python standard lib as a zip file in my py-js project and this is one step of of a whole custom build system in python in the builder module.

The specific function in builder looks like the following:

def ziplib(self):
        """zip python package in site-packages to .zip archive"""
        temp_lib_dynload = self.prefix_lib / "lib-dynload"
        temp_os_py = self.prefix_lib / "os.py"

        self.cmd.move(self.site_packages, "/tmp/site-packages")
        self.lib_dynload.rename(temp_lib_dynload)
        self.cmd.copy(self.python_lib / "os.py", temp_os_py)

        zip_path = self.prefix_lib / f"python{self.product.ver_nodot}"
        shutil.make_archive(str(zip_path), "zip", str(self.python_lib))

        self.cmd.remove(self.python_lib)
        self.python_lib.mkdir()
        temp_lib_dynload.rename(self.lib_dynload)
        temp_os_py.rename(self.python_lib / "os.py")
        self.cmd.move("/tmp/site-packages", self.site_packages)

I've only tested this on macOS, but I don't see why this wouldn't work on other platforms.

kunitoki commented 6 months ago

Thanks for the headsup, really appreciated ! Btw i started hacking around a bit:


import os
import sys
import shutil
from pathlib import Path

if __name__ == "__main__":
    base_folder = Path(sys.argv[1])
    output_folder = Path(sys.argv[2])
    version_major = sys.argv[3]
    version_minor = sys.argv[4]

    version = f"{version_major}.{version_minor}"
    version_nodot = f"{version_major}{version_minor}"

    ignored_files = shutil.ignore_patterns(
        "*.pyc",
        "__pycache__",
        "__phello__",
        "*config-3*",
        "*tcl*",
        "*tdbc*",
        "*tk*",
        "Tk*",
        "_tk*",
        "_test*",
        "libpython*",
        "pkgconfig",
        "idlelib",
        "site-packages",
        "test",
        "turtledemo",
    )

    print("cleaning up...")
    shutil.rmtree(str(output_folder / "python"))

    print("copying library...")
    shutil.copytree(
        str(base_folder / "lib" / f"python{version}"),
        str(output_folder / "python"),
        ignore=ignored_files,
        dirs_exist_ok=True)

    print("creating site-packages...")
    os.makedirs(str(output_folder / "python" / "site-packages"), exist_ok=True)

    print("making archive...")
    shutil.make_archive(str(output_folder / f"python{version_nodot}"), "zip", str(output_folder / "python"))

This is executed from cmake and the resulting zip file is embedded in the app, but i've tried bootstrapping python and i cannot find the right way to do it (assuming i setup isolated mode to not look into the system):

libc++abi: terminating due to uncaught exception of type std::runtime_error: failed to get the Python codec of the filesystem encoding
kunitoki commented 6 months ago

This is the full output of python when it fails initialisation:

Python path configuration:
  PYTHONHOME = '/Users/kunitoki/Library/Caches/Popsicle Demo'
  PYTHONPATH = '/Users/kunitoki/Library/Caches/Popsicle Demo/python311.zip'
  program name = 'PopsicleDemo'
  isolated = 1
  environment = 1
  user site = 1
  safe_path = 0
  import site = 1
  is in build tree = 0
  stdlib dir = '/Users/kunitoki/Library/Caches/Popsicle Demo/lib/python3.11'
  sys._base_executable = '/Users/kunitoki/popsicle/demo/build/popsicle_demo_artefacts/Debug/Popsicle Demo.app/Contents/MacOS/Popsicle Demo'
  sys.base_prefix = '/Users/kunitoki/Library/Caches/Popsicle Demo'
  sys.base_exec_prefix = '/Users/kunitoki/Library/Caches/Popsicle Demo'
  sys.platlibdir = 'lib'
  sys.executable = '/Users/kunitoki/popsicle/demo/build/popsicle_demo_artefacts/Debug/Popsicle Demo.app/Contents/MacOS/Popsicle Demo'
  sys.prefix = '/Users/kunitoki/Library/Caches/Popsicle Demo'
  sys.exec_prefix = '/Users/kunitoki/Library/Caches/Popsicle Demo'
  sys.path = [
    '/Users/kunitoki/Library/Caches/Popsicle Demo/python311.zip',
    '/Users/kunitoki/Library/Caches/Popsicle Demo/lib/python311.zip',
    '/Users/kunitoki/Library/Caches/Popsicle Demo/lib/python3.11',
    '/Users/kunitoki/Library/Caches/Popsicle Demo/lib/python3.11/lib-dynload',
  ]
shakfu commented 6 months ago

the stack overflow has a very important note: # NOTE: need os.py to remain in site-packages or it will fail

kunitoki commented 6 months ago

mmmh interesting, but i have no os.py in site-packages in my default python installation ? it is in the standard library default location.

i think the issue is stdlib dir = '/Users/kunitoki/Library/Caches/Popsicle Demo/lib/python3.11' i tried to set the variable to point to the .zip file but it always revert to search in the lib/python3.11 subfolder

shakfu commented 6 months ago

Don't ask me why but, but if you zip the whole of site-packages you need to keep os.py in the root of python311. Look at the screenshot below (note that site-packages is empty because everything is zipped but you still need it there...lib-dynload is empty but doesn't haven't to be (in this case, it is because all extensions are statically linked with libpython + my-code). I've also attached my zipped plugin for your information below the screenshot

Screenshot 2024-02-13 at 9 55 44 AM

py.mxo.zip

shakfu commented 6 months ago

One has to put a single file, os.py, in python3.11 or site-packages ... the key point is that an uncompressed os.py is required for bootstrapping python.

kunitoki commented 6 months ago

yes but i have this like a standard python installation

folder
  \ os.py
  \ ...
  \ site-packages

so should work

kunitoki commented 6 months ago

I couldn't make it work with the zip. The only way i could make it work is by unpacking the zip file in the lib/pythonX.Y folder and pointing the python home the base folder, that works fine.

shakfu commented 6 months ago

Here's an updated bash script which demonstrates it from a-z (also attached):

#!/usr/bin/env bash

# build_zpython.sh
# builds minimized python with zipped `site-packages`

# ----------------------------------------------------------------------------
# VARS

NAME=python
VERSION=3.11.7

# ----------------------------------------------------------------------------

PWD=$(pwd)
PREFIX=${PWD}/${NAME}
VER=${VERSION%.*}
VERN="${VER//./}"
LIB=${PREFIX}/lib/python${VER}
MAC_DEP_TARGET=10.13
URL=https://www.python.org/ftp/python/${VERSION}/Python-${VERSION}.tar.xz

get_python() {
    wget $URL
}

remove() {
    echo "removing $1"
    rm -rf $1
}

rm_lib() {
    echo "removing $1"
    rm -rf ${LIB}/$1
}

clean() {
    echo "removing __pycache__ .pyc/o from $1"
    find $1 | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
}

clean_tests() {
    echo "removing 'test' dirs from $1"
    find $1 | grep -E "(tests|test)" | xargs rm -rf
}

clean_site_packages() {
    echo "removing everything in $LIB/site-packages"
    rm -rf $LIB/site-packages/*
}

rm_ext() {
    echo "removing $LIB/lib-dynload/$1.cpython-${VER}-darwin.so"
    rm -rf $LIB/lib-dynload/$1.cpython-*.so
}

rm_bin() {
    echo "removing $PREFIX/bin/$1"
    rm -rf $PREFIX/bin/$1
}

# ----------------------------------------------------------------------------
# main

main() {
    if ! test -f Python-${VERSION}.tar.xz; then
        wget $URL
    fi
    if test -d Python-${VERSION}; then
        rm -rf Python-${VERSION}
    fi
    tar xvf Python-${VERSION}.tar.xz
    cd Python-${VERSION}

    ./configure MACOSX_DEPLOYMENT_TARGET=${MAC_DEP_TARGET} \
        --prefix=$PREFIX \
        --enable-shared \
        --disable-test-modules \
        --with-ensurepip=no \
        --without-static-libpython

    make install

    clean $PREFIX
    clean_tests $LIB
    clean_site_packages
    remove ${LIB}/site-packages

    # remove what you want here...
    rm_lib config-${VERSION}-darwin
    rm_lib idlelib
    rm_lib lib2to3
    rm_lib tkinter
    rm_lib turtledemo
    rm_lib turtle.py
    rm_lib ensurepip
    rm_lib venv

    remove $LIB/distutils/command/*.exe
    remove $LIB/lib-dynload/_xx*.so
    remove $LIB/lib-dynload/xx*.so
    remove $PREFIX/lib/pkgconfig
    remove $PREFIX/share

    # remove what you want here...
    rm_ext _tkinter

    rm_bin 2to3-${VER}
    rm_bin idle${VER}
    rm_bin idle3
    rm_bin 2to3-${VER}
    rm_bin 2to3

    mv $LIB/lib-dynload $PREFIX
    cp $LIB/os.py $PREFIX
    clean $PREFIX

    echo "zip site-packages"
    python3 -m zipfile -c $PREFIX/lib/python${VERN}.zip $LIB/*
    remove $LIB
    mkdir -p $LIB
    mv $PREFIX/lib-dynload $LIB
    mv $PREFIX/os.py $LIB
    mkdir $LIB/site-packages

    echo "cleanup"
    rm -rf Python-${VERSION}
}

test_zipped_python() {
    ./python/bin/python3 -c "import string; print(string.__file__)"
    ./python/bin/python3 -c "import string; assert string.digits=='0123456789'"
}

# run it
time main

# test it
test_zipped_python

build_zpython.sh.zip

shakfu commented 6 months ago

@kunitoki hope the above script is helpful. It would be useful to know what you will be using the zipped sodlib for..

kunitoki commented 6 months ago

Thanks for sharing, i'm allowing popsicle to work as a JUCE module as well and act as an embeddable python scripting language (with juce exposed thanks to popsicle itself).

You can have a look at the demo app, which is a standalone JUCE app with a bundled python interpreter + python stdlib + popsicle bindings + (optional) custom app bindings.

For now i'm just bundling the python standard library inside the binary itself (the juce way) so it's cross platform https://github.com/kunitoki/popsicle/blob/dev/small_updates/demo/CMakeLists.txt#L42-L55 using this script together with the JUCE's juce_add_binary_data cmake utility.

The application then bootstrap the python home like this https://github.com/kunitoki/popsicle/blob/dev/small_updates/modules/juce_python/scripting/ScriptEngine.cpp#L69-L117 and initializes the python interpreter in isolated mode (so there is no way to mix with user installed pythons).

shakfu commented 6 months ago

@kunitoki

For now i'm just bundling the python standard library inside the binary itself (the juce way) so it's cross platform https://github.com/kunitoki/popsicle/blob/dev/small_updates/demo/CMakeLists.txt#L42-L55 using this script together with the JUCE's juce_add_binary_data cmake utility.

Cool, I wasn't aware that was possible. After reading this, I was intrigued to see how it worked and I tried to build the demo using:

cd demo
mkdir build
cd build
cmake ..
cmake --build . --config Release

It was building alright but then I got a link error because it was trying and failing to link to my homebrew python

[100%] Linking CXX executable "popsicle_demo_artefacts/Popsicle Demo.app/Contents/MacOS/Popsicle Demo"
ld: warning: ignoring file '/opt/homebrew/Cellar/python@3.11/3.11.7_1/Frameworks/Python.framework/Versions/3.11/Python': found architecture 'arm64', required architecture 'x86_64'
ld: Undefined symbols:
  _PyBaseObject_Type, referenced from:
      pybind11::detail::make_object_base_type(_typeobject*) in Main.cpp.o
  _PyBool_FromLong, referenced from:
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyBool_Type, referenced from:
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyBuffer_Release, referenced from:
      pybind11::buffer_info::~buffer_info() in PopsicleDemo.cpp.o
  _PyByteArray_AsString, referenced from:
      bool pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load_raw<char>(std::__1::enable_if<std::is_same<char, char>::value, pybind11::handle>::type) in Main.cpp.o
      pybind11::detail::type_caster<juce::StringRef, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyByteArray_Size, referenced from:
      bool pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load_raw<char>(std::__1::enable_if<std::is_same<char, char>::value, pybind11::handle>::type) in Main.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyByteArray_Type, referenced from:
      bool pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load_raw<char>(std::__1::enable_if<std::is_same<char, char>::value, pybind11::handle>::type) in Main.cpp.o
      pybind11::detail::type_caster<juce::StringRef, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyBytes_AsString, referenced from:
      bool pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load_raw<char>(std::__1::enable_if<std::is_same<char, char>::value, pybind11::handle>::type) in Main.cpp.o
      pybind11::detail::type_caster<juce::StringRef, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::string_caster<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t>>, false>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyBytes_AsStringAndSize, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::str::operator std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>() const in Main.cpp.o
      std::__1::basic_string_view<char, std::__1::char_traits<char>> pybind11::bytes::string_op<std::__1::basic_string_view<char, std::__1::char_traits<char>>>() const in juce_python_core.cpp.o
  _PyBytes_FromStringAndSize, referenced from:
      pybind11::bytes::bytes<long, 0>(char const*, long const&) in juce_python_core.cpp.o
  _PyBytes_Size, referenced from:
      bool pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load_raw<char>(std::__1::enable_if<std::is_same<char, char>::value, pybind11::handle>::type) in Main.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load_raw(pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::string_caster<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t>>, false>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyCFunction_Type, referenced from:
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
      pybind11::function::cpp_function() const in juce_python_audio_basics.cpp.o
  _PyCMethod_New, referenced from:
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
  _PyCallable_Check, referenced from:
      pybind11::function::check_(pybind11::handle) in juce_python_audio_basics.cpp.o
  _PyCapsule_GetContext, referenced from:
      pybind11::capsule::initialize_with_void_ptr_destructor(void const*, char const*, void (*)(void*))::'lambda'(_object*)::operator()(_object*) const in Main.cpp.o
  _PyCapsule_GetName, referenced from:
      pybind11::capsule::name() const in Main.cpp.o
      pybind11::capsule::get_name_in_error_scope(_object*) in Main.cpp.o
  _PyCapsule_GetPointer, referenced from:
      pybind11::detail::get_internals_pp_from_capsule(pybind11::handle) in Main.cpp.o
      pybind11::detail::function_record* pybind11::capsule::get_pointer<pybind11::detail::function_record>() const in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::capsule::initialize_with_void_ptr_destructor(void const*, char const*, void (*)(void*))::'lambda'(_object*)::operator()(_object*) const in Main.cpp.o
      pybind11::detail::type_info* pybind11::capsule::get_pointer<pybind11::detail::type_info>() const in PopsicleDemo.cpp.o
      void* pybind11::capsule::get_pointer<void>() const in juce_python_core.cpp.o
  _PyCapsule_New, referenced from:
      pybind11::capsule::capsule(void const*, char const*, void (*)(_object*)) in Main.cpp.o
      pybind11::capsule::initialize_with_void_ptr_destructor(void const*, char const*, void (*)(void*)) in Main.cpp.o
  _PyCapsule_SetContext, referenced from:
      pybind11::capsule::initialize_with_void_ptr_destructor(void const*, char const*, void (*)(void*)) in Main.cpp.o
  _PyCapsule_SetPointer, referenced from:
      pybind11::capsule::set_pointer(void const*) in Main.cpp.o
  _PyCapsule_Type, referenced from:
      pybind11::capsule::check_(pybind11::handle) in Main.cpp.o
  _PyConfig_Clear, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _PyConfig_InitPythonConfig, referenced from:
      pybind11::initialize_interpreter(bool, int, char const* const*, bool) in juce_python.cpp.o
  _PyConfig_SetBytesArgv, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _PyDict_Contains, referenced from:
      bool pybind11::dict::contains<pybind11::str&>(pybind11::str&) const in juce_python_audio_basics.cpp.o
      bool pybind11::dict::contains<char const (&) [4]>(char const (&) [4]) const in juce_python_core.cpp.o
      bool pybind11::dict::contains<char const (&) [5]>(char const (&) [5]) const in juce_python_core.cpp.o
      bool pybind11::dict::contains<char const (&) [6]>(char const (&) [6]) const in juce_python_core.cpp.o
  _PyDict_Copy, referenced from:
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
  _PyDict_DelItemString, referenced from:
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
  _PyDict_GetItemWithError, referenced from:
      pybind11::detail::dict_getitemstring(_object*, char const*) in Main.cpp.o
      pybind11::detail::dict_getitem(_object*, _object*) in juce_python_audio_basics.cpp.o
  _PyDict_New, referenced from:
      pybind11::dict::dict() in Main.cpp.o
  _PyDict_Next, referenced from:
      pybind11::detail::iterator_policies::dict_readonly::increment() in Main.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyDict_Size, referenced from:
      pybind11::dict::size() const in Main.cpp.o
  _PyDict_Type, referenced from:
      pybind11::dict::raw_dict(_object*) in Main.cpp.o
  _PyErr_CheckSignals, referenced from:
      popsicle::Bindings::registerJuceGuiEntryPointsBindings(pybind11::module_&)::PyTestableApplication::processEvents(int) in juce_python_gui_basics.cpp.o
  _PyErr_Clear, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load(pybind11::handle, bool) in Main.cpp.o
      pybind11::detail::type_caster<bool, void>::load(pybind11::handle, bool) in PopsicleDemo.cpp.o
      pybind11::getattr(pybind11::handle, char const*, pybind11::handle) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      ...
  _PyErr_Fetch, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::detail::error_fetch_and_normalize::error_fetch_and_normalize(char const*) in Main.cpp.o
      pybind11::error_scope::error_scope() in Main.cpp.o
  _PyErr_Format, referenced from:
      _pybind11_meta_call in Main.cpp.o
  _PyErr_NormalizeException, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::detail::error_fetch_and_normalize::error_fetch_and_normalize(char const*) in Main.cpp.o
  _PyErr_Occurred, referenced from:
      pybind11::detail::dict_getitemstring(_object*, char const*) in Main.cpp.o
      pybind11::str::str(char const*) in Main.cpp.o
      pybind11::detail::raise_err(_object*, char const*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::capsule::name() const in Main.cpp.o
      pybind11::capsule::initialize_with_void_ptr_destructor(void const*, char const*, void (*)(void*))::'lambda'(_object*)::operator()(_object*) const in Main.cpp.o
      ...
  _PyErr_Restore, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::detail::error_fetch_and_normalize::restore() in Main.cpp.o
      pybind11::error_scope::~error_scope() in Main.cpp.o
  _PyErr_SetString, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
      pybind11::cast_error::set_error() const in Main.cpp.o
      pybind11::detail::raise_err(_object*, char const*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      ...
  _PyErr_WarnEx, referenced from:
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
  _PyErr_WriteUnraisable, referenced from:
      pybind11::capsule::get_name_in_error_scope(_object*) in Main.cpp.o
  _PyEval_AcquireThread, referenced from:
      pybind11::gil_scoped_acquire::gil_scoped_acquire() in Main.cpp.o
  _PyEval_GetBuiltins, referenced from:
      pybind11::detail::get_python_state_dict() in Main.cpp.o
  _PyEval_GetGlobals, referenced from:
      pybind11::globals() in PopsicleDemo.cpp.o
  _PyEval_GetLocals, referenced from:
      pybind11::detail::get_type_override(void const*, pybind11::detail::type_info const*, char const*) in juce_python_audio_basics.cpp.o
  _PyEval_RestoreThread, referenced from:
      pybind11::gil_scoped_release::~gil_scoped_release() in juce_python_audio_devices.cpp.o
  _PyEval_SaveThread, referenced from:
      pybind11::gil_scoped_acquire::~gil_scoped_acquire() in Main.cpp.o
      pybind11::gil_scoped_release::gil_scoped_release(bool) in juce_python_audio_devices.cpp.o
  _PyExc_BufferError, referenced from:
      _pybind11_getbuffer in PopsicleDemo.cpp.o
      _pybind11_getbuffer in PopsicleDemo.cpp.o
  _PyExc_FutureWarning, referenced from:
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
  _PyExc_ImportError, referenced from:
      pybind11_init_wrapper_custom() in PopsicleDemo.cpp.o
      pybind11_init_wrapper_custom() in PopsicleDemo.cpp.o
      pybind11_init_wrapper_popsicle() in juce_python_bindings.cpp.o
      pybind11_init_wrapper_popsicle() in juce_python_bindings.cpp.o
      pybind11_init_wrapper___popsicle__() in juce_python_modules.cpp.o
      pybind11_init_wrapper___popsicle__() in juce_python_modules.cpp.o
  _PyExc_IndexError, referenced from:
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
  _PyExc_MemoryError, referenced from:
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
  _PyExc_OverflowError, referenced from:
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
  _PyExc_RuntimeError, referenced from:
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::cast_error::set_error() const in Main.cpp.o
      pybind11::reference_cast_error::set_error() const in PopsicleDemo.cpp.o
  _PyExc_StopIteration, referenced from:
      pybind11::stop_iteration::set_error() const in juce_python_audio_basics.cpp.o
  _PyExc_SystemError, referenced from:
      pybind11::detail::get_python_state_dict() in Main.cpp.o
      pybind11::detail::get_internals_pp_from_capsule(pybind11::handle) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
  _PyExc_TypeError, referenced from:
      _pybind11_meta_call in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
      _pybind11_object_init in Main.cpp.o
      ...
  _PyExc_ValueError, referenced from:
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::detail::translate_exception(std::exception_ptr) in Main.cpp.o
      pybind11::value_error::set_error() const in juce_python_audio_basics.cpp.o
  _PyException_SetCause, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
  _PyException_SetContext, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
  _PyException_SetTraceback, referenced from:
      pybind11::raise_from(_object*, char const*) in Main.cpp.o
  _PyFloat_AsDouble, referenced from:
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<double, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyFloat_FromDouble, referenced from:
      std::__1::enable_if<std::is_floating_point<float>::value, pybind11::handle>::type pybind11::detail::type_caster<float, void>::cast<float>(float, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      std::__1::enable_if<std::is_floating_point<double>::value, pybind11::handle>::type pybind11::detail::type_caster<double, void>::cast<double>(double, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
      pybind11::float_::float_(float) in juce_python_graphics.cpp.o
  _PyFloat_Type, referenced from:
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<double, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<long long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      ...
  _PyFrame_GetBack, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
  _PyFrame_GetCode, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::detail::get_type_override(void const*, pybind11::detail::type_info const*, char const*) in juce_python_audio_basics.cpp.o
  _PyFrame_GetLineNumber, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
  _PyGILState_Ensure, referenced from:
      pybind11::detail::get_internals()::gil_scoped_acquire_local::gil_scoped_acquire_local() in Main.cpp.o
  _PyGILState_GetThisThreadState, referenced from:
      pybind11::gil_scoped_acquire::gil_scoped_acquire() in Main.cpp.o
  _PyGILState_Release, referenced from:
      pybind11::detail::get_internals()::gil_scoped_acquire_local::~gil_scoped_acquire_local() in Main.cpp.o
  _PyImport_AddModule, referenced from:
      pybind11::module_::def_submodule(char const*, char const*) in juce_python_graphics.cpp.o
  _PyImport_AppendInittab, referenced from:
      pybind11::detail::embedded_module::embedded_module(char const*, _object* (*)()) in PopsicleDemo.cpp.o
  _PyImport_ImportModule, referenced from:
      pybind11::module_::import(char const*) in PopsicleDemo.cpp.o
  _PyIndex_Check, referenced from:
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<int, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned int, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<long long, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned long long, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_core.cpp.o
      pybind11::detail::type_caster<unsigned char, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_core.cpp.o
      pybind11::detail::type_caster<short, void>::load(pybind11::handle, bool)::'lambda'(_object*)::operator()(_object*) const in juce_python_core.cpp.o
      ...
  _PyInstanceMethod_New, referenced from:
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
  _PyInstanceMethod_Type, referenced from:
      _pybind11_meta_getattro in Main.cpp.o
      pybind11::cpp_function::initialize_generic(std::__1::unique_ptr<pybind11::detail::function_record, pybind11::cpp_function::InitializingFunctionRecordDeleter>&&, char const*, std::type_info const* const*, unsigned long) in Main.cpp.o
      pybind11::detail::get_function(pybind11::handle) in PopsicleDemo.cpp.o
  _PyIter_Check, referenced from:
      pybind11::iterator::check_(pybind11::handle) in juce_python_audio_basics.cpp.o
  _PyIter_Next, referenced from:
      pybind11::iterator::advance() in juce_python_gui_basics.cpp.o
  _PyList_Append, referenced from:
      void pybind11::list::append<_object*>(_object*&&) in PopsicleDemo.cpp.o
      void pybind11::list::append<popsicle::Bindings::PyArrayView<int const>>(popsicle::Bindings::PyArrayView<int const>&&) in juce_python_audio_formats.cpp.o
      void pybind11::list::append<juce::var const&>(juce::var const&) in juce_python_core.cpp.o
      void pybind11::list::append<juce::UndoableAction const*&>(juce::UndoableAction const*&) in juce_python_data_structures.cpp.o
      void pybind11::list::append<juce::MouseInputSource const&>(juce::MouseInputSource const&) in juce_python_gui_basics.cpp.o
      void pybind11::list::append<pybind11::object>(pybind11::object&&) in juce_python_gui_basics.cpp.o
      void pybind11::list::append<juce::Component* const&>(juce::Component* const&) in juce_python_gui_basics.cpp.o
      ...
  _PyList_GetItem, referenced from:
      pybind11::object pybind11::detail::accessor_policies::list_item::get<unsigned long, 0>(pybind11::handle, unsigned long const&) in PopsicleDemo.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyList_New, referenced from:
      pybind11::list::list<long, 0>(long) in PopsicleDemo.cpp.o
      pybind11::list::list<int, 0>(int) in juce_python_audio_basics.cpp.o
      pybind11::list::list<unsigned long, 0>(unsigned long) in juce_python_audio_devices.cpp.o
  _PyList_SetItem, referenced from:
      void pybind11::detail::accessor_policies::list_item::set<unsigned long, 0>(pybind11::handle, unsigned long const&, pybind11::handle) in juce_python_audio_basics.cpp.o
  _PyList_Size, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::list::size() const in PopsicleDemo.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyLong_AsLong, referenced from:
      pybind11::detail::type_caster<int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<long long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<short, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<long, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyLong_AsUnsignedLong, referenced from:
      unsigned long pybind11::detail::as_unsigned<unsigned long>(_object*) in juce_python_audio_basics.cpp.o
  _PyLong_FromLong, referenced from:
      pybind11::int_::int_<int, 0>(int) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyLong_FromLongLong, referenced from:
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyLong_FromSize_t, referenced from:
      std::__1::enable_if<!std::is_floating_point<unsigned long>::value && std::is_unsigned<unsigned long>::value && sizeof (unsigned long) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<unsigned long, void>::cast<unsigned long>(unsigned long, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      std::__1::enable_if<!std::is_floating_point<unsigned int>::value && std::is_unsigned<unsigned int>::value && sizeof (unsigned int) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<unsigned int, void>::cast<unsigned int>(unsigned int, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      std::__1::enable_if<!std::is_floating_point<unsigned long long>::value && std::is_unsigned<unsigned long long>::value && sizeof (unsigned long long) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<unsigned long long, void>::cast<unsigned long long>(unsigned long long, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_devices.cpp.o
      std::__1::enable_if<!std::is_floating_point<unsigned char>::value && std::is_unsigned<unsigned char>::value && sizeof (unsigned char) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<unsigned char, void>::cast<unsigned char>(unsigned char, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
      std::__1::enable_if<!std::is_floating_point<unsigned short>::value && std::is_unsigned<unsigned short>::value && sizeof (unsigned short) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<unsigned short, void>::cast<unsigned short>(unsigned short, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyLong_FromSsize_t, referenced from:
      std::__1::enable_if<!std::is_floating_point<int>::value && std::is_signed<int>::value && sizeof (int) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<int, void>::cast<int>(int, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      std::__1::enable_if<!std::is_floating_point<long long>::value && std::is_signed<long long>::value && sizeof (long long) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<long long, void>::cast<long long>(long long, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
      std::__1::enable_if<!std::is_floating_point<short>::value && std::is_signed<short>::value && sizeof (short) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<short, void>::cast<short>(short, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
      std::__1::enable_if<!std::is_floating_point<long>::value && std::is_signed<long>::value && sizeof (long) <= 8ul, pybind11::handle>::type pybind11::detail::type_caster<long, void>::cast<long>(long, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyMem_Calloc, referenced from:
      pybind11::detail::instance::allocate_layout() in Main.cpp.o
  _PyMem_Free, referenced from:
      pybind11::detail::instance::deallocate_layout() in Main.cpp.o
  _PyMemoryView_FromMemory, referenced from:
      pybind11::memoryview::from_memory(void*, long, bool) in juce_python_core.cpp.o
  _PyMemoryView_FromObject, referenced from:
      pybind11::memoryview::memoryview(pybind11::object&&) in juce_python_core.cpp.o
  _PyMemoryView_Type, referenced from:
      pybind11::memoryview::check_(pybind11::handle) in juce_python_core.cpp.o
  _PyMethod_Type, referenced from:
      pybind11::detail::get_function(pybind11::handle) in PopsicleDemo.cpp.o
  _PyModule_AddObject, referenced from:
      pybind11::module_::add_object(char const*, pybind11::handle, bool) in juce_python_audio_basics.cpp.o
  _PyModule_Create2, referenced from:
      pybind11::module_::create_extension_module(char const*, char const*, PyModuleDef*) in PopsicleDemo.cpp.o
  _PyModule_GetName, referenced from:
      pybind11::module_::def_submodule(char const*, char const*) in juce_python_graphics.cpp.o
  _PyModule_Type, referenced from:
      pybind11::detail::make_new_python_type(pybind11::detail::type_record const&) in PopsicleDemo.cpp.o
  _PyNumber_And, referenced from:
      pybind11::detail::object_api<pybind11::handle>::operator&(pybind11::detail::object_api<pybind11::handle> const&) const in juce_python_audio_basics.cpp.o
  _PyNumber_Check, referenced from:
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<double, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<long long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned long long, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      ...
  _PyNumber_Float, referenced from:
      pybind11::detail::type_caster<float, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<double, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
  _PyNumber_Invert, referenced from:
      pybind11::detail::object_api<pybind11::handle>::operator~() const in juce_python_audio_basics.cpp.o
  _PyNumber_Long, referenced from:
      pybind11::detail::type_caster<unsigned long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::int_::int_(pybind11::object const&) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned int, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<long long, void>::load(pybind11::handle, bool) in juce_python_audio_basics.cpp.o
      pybind11::detail::type_caster<unsigned long long, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<unsigned char, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      ...
  _PyNumber_Or, referenced from:
      pybind11::detail::object_api<pybind11::handle>::operator|(pybind11::detail::object_api<pybind11::handle> const&) const in juce_python_audio_basics.cpp.o
  _PyNumber_Xor, referenced from:
      pybind11::detail::object_api<pybind11::handle>::operator^(pybind11::detail::object_api<pybind11::handle> const&) const in juce_python_audio_basics.cpp.o
  _PyObject_Call, referenced from:
      void pybind11::implicitly_convertible<juce::RectanglePlacement::Flags, juce::RectanglePlacement>()::'lambda'(_object*, _typeobject*)::operator()(_object*, _typeobject*) const in juce_python_graphics.cpp.o
  _PyObject_CallFunctionObjArgs, referenced from:
      pybind11::dict::raw_dict(_object*) in Main.cpp.o
  _PyObject_CallObject, referenced from:
      pybind11::detail::simple_collector<(pybind11::return_value_policy)1>::call(_object*) const in Main.cpp.o
  _PyObject_CheckBuffer, referenced from:
      pybind11::buffer::check_(pybind11::handle) in juce_python_core.cpp.o
  _PyObject_ClearWeakRefs, referenced from:
      pybind11::detail::clear_instance(_object*) in Main.cpp.o
  _PyObject_GC_UnTrack, referenced from:
      _pybind11_object_dealloc in Main.cpp.o
  _PyObject_GenericGetDict, referenced from:
      pybind11::detail::enable_dynamic_attributes(_heaptypeobject*)::getset in PopsicleDemo.cpp.o
  _PyObject_GenericSetDict, referenced from:
      pybind11::detail::enable_dynamic_attributes(_heaptypeobject*)::getset in PopsicleDemo.cpp.o
  _PyObject_GetAttrString, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::getattr(pybind11::handle, char const*) in Main.cpp.o
      pybind11::getattr(pybind11::handle, char const*, pybind11::handle) in juce_python_audio_basics.cpp.o
      pybind11::detail::get_type_override(void const*, pybind11::detail::type_info const*, char const*) in juce_python_audio_basics.cpp.o
  _PyObject_GetBuffer, referenced from:
      pybind11::buffer::request(bool) const in juce_python_core.cpp.o
  _PyObject_GetItem, referenced from:
      pybind11::detail::accessor_policies::generic_item::get(pybind11::handle, pybind11::handle) in juce_python_audio_basics.cpp.o
  _PyObject_GetIter, referenced from:
      pybind11::iter(pybind11::handle) in juce_python_gui_basics.cpp.o
  _PyObject_HasAttrString, referenced from:
      pybind11::hasattr(pybind11::handle, char const*) in Main.cpp.o
  _PyObject_IsInstance, referenced from:
      _pybind11_meta_setattro in Main.cpp.o
      _pybind11_meta_setattro in Main.cpp.o
      pybind11::isinstance(pybind11::handle, pybind11::handle) in juce_python_core.cpp.o
  _PyObject_IsTrue, referenced from:
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyObject_Malloc, referenced from:
      pybind11::detail::make_new_python_type(pybind11::detail::type_record const&) in PopsicleDemo.cpp.o
  _PyObject_Repr, referenced from:
      pybind11::repr(pybind11::handle) in Main.cpp.o
  _PyObject_RichCompareBool, referenced from:
      pybind11::detail::object_api<pybind11::handle>::rich_compare(pybind11::detail::object_api<pybind11::handle> const&, int) const in juce_python_audio_basics.cpp.o
  _PyObject_SetAttr, referenced from:
      pybind11::setattr(pybind11::handle, pybind11::handle, pybind11::handle) in juce_python_audio_basics.cpp.o
  _PyObject_SetAttrString, referenced from:
      pybind11::setattr(pybind11::handle, char const*, pybind11::handle) in Main.cpp.o
  _PyObject_SetItem, referenced from:
      pybind11::detail::accessor_policies::generic_item::set(pybind11::handle, pybind11::handle, pybind11::handle) in Main.cpp.o
  _PyObject_Str, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::str::raw_str(_object*) in Main.cpp.o
  _PyProperty_Type, referenced from:
      pybind11::detail::make_static_property_type() in Main.cpp.o
      _pybind11_static_get in Main.cpp.o
      _pybind11_static_set in Main.cpp.o
      pybind11::detail::generic_type::def_property_static_impl(char const*, pybind11::handle, pybind11::handle, pybind11::detail::function_record*) in PopsicleDemo.cpp.o
      pybind11::detail::enum_base::init(bool, bool) in juce_python_audio_basics.cpp.o
  _PyRun_SimpleStringFlags, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _PyRun_StringFlags, referenced from:
      pybind11::object pybind11::eval<(pybind11::eval_mode)2>(pybind11::str const&, pybind11::object, pybind11::object) in juce_python.cpp.o
  _PySequence_Check, referenced from:
      pybind11::sequence::check_(pybind11::handle) in juce_python_gui_basics.cpp.o
  _PySequence_GetItem, referenced from:
      pybind11::object pybind11::detail::accessor_policies::sequence_item::get<unsigned long, 0>(pybind11::handle, unsigned long const&) in juce_python_gui_basics.cpp.o
  _PySequence_Size, referenced from:
      pybind11::sequence::end() const in juce_python_gui_basics.cpp.o
      pybind11::sequence::size() const in juce_python_gui_basics.cpp.o
  _PySequence_Tuple, referenced from:
      pybind11::tuple::tuple(pybind11::object const&) in PopsicleDemo.cpp.o
      pybind11::tuple::tuple(pybind11::object&&) in juce_python_audio_formats.cpp.o
  _PyStaticMethod_New, referenced from:
      pybind11::staticmethod::staticmethod(pybind11::object&&) in juce_python_audio_basics.cpp.o
  _PyStaticMethod_Type, referenced from:
      pybind11::detail::PyStaticMethod_Check(_object*) in juce_python_audio_basics.cpp.o
  _PyStatus_Exception, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _PyStatus_IsError, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _PyThreadState_Clear, referenced from:
      pybind11::gil_scoped_acquire::dec_ref() in Main.cpp.o
  _PyThreadState_DeleteCurrent, referenced from:
      pybind11::gil_scoped_acquire::dec_ref() in Main.cpp.o
  _PyThreadState_Get, referenced from:
      pybind11::detail::get_internals() in Main.cpp.o
      pybind11::detail::get_type_override(void const*, pybind11::detail::type_info const*, char const*) in juce_python_audio_basics.cpp.o
  _PyThreadState_GetFrame, referenced from:
      pybind11::detail::get_type_override(void const*, pybind11::detail::type_info const*, char const*) in juce_python_audio_basics.cpp.o
  _PyThreadState_New, referenced from:
      pybind11::gil_scoped_acquire::gil_scoped_acquire() in Main.cpp.o
  _PyThread_tss_alloc, referenced from:
      pybind11::detail::get_internals() in Main.cpp.o
      pybind11::detail::local_internals::shared_loader_life_support_data::shared_loader_life_support_data() in Main.cpp.o
  _PyThread_tss_create, referenced from:
      pybind11::detail::get_internals() in Main.cpp.o
      pybind11::detail::local_internals::shared_loader_life_support_data::shared_loader_life_support_data() in Main.cpp.o
  _PyThread_tss_free, referenced from:
      pybind11::detail::internals::~internals() in juce_python.cpp.o
  _PyThread_tss_get, referenced from:
      pybind11::gil_scoped_acquire::gil_scoped_acquire() in Main.cpp.o
      pybind11::detail::loader_life_support::get_stack_top() in Main.cpp.o
  _PyThread_tss_set, referenced from:
      pybind11::gil_scoped_acquire::gil_scoped_acquire() in Main.cpp.o
      pybind11::detail::get_internals() in Main.cpp.o
      pybind11::detail::loader_life_support::set_stack_top(pybind11::detail::loader_life_support*) in Main.cpp.o
      pybind11::gil_scoped_acquire::dec_ref() in Main.cpp.o
      pybind11::gil_scoped_release::gil_scoped_release(bool) in juce_python_audio_devices.cpp.o
      pybind11::gil_scoped_release::~gil_scoped_release() in juce_python_audio_devices.cpp.o
  _PyTuple_GetItem, referenced from:
      pybind11::object pybind11::detail::accessor_policies::tuple_item::get<unsigned long, 0>(pybind11::handle, unsigned long const&) in Main.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyTuple_New, referenced from:
      pybind11::tuple::tuple<long, 0>(long) in Main.cpp.o
      pybind11::tuple::tuple<int, 0>(int) in Main.cpp.o
      pybind11::tuple::tuple<unsigned long, 0>(unsigned long) in Main.cpp.o
  _PyTuple_SetItem, referenced from:
      void pybind11::detail::accessor_policies::tuple_item::set<unsigned long, 0>(pybind11::handle, unsigned long const&, pybind11::handle) in Main.cpp.o
  _PyTuple_Size, referenced from:
      pybind11::tuple::size() const in Main.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyType_IsSubtype, referenced from:
      PyObject_TypeCheck(_object*, _typeobject*) in Main.cpp.o
      PyObject_TypeCheck(_object*, _typeobject*) in PopsicleDemo.cpp.o
      bool pybind11::detail::type_caster_generic::load_impl<pybind11::detail::type_caster_generic>(pybind11::handle, bool) in PopsicleDemo.cpp.o
      bool pybind11::detail::type_caster_generic::load_impl<pybind11::detail::type_caster_generic>(pybind11::handle, bool) in PopsicleDemo.cpp.o
      PyObject_TypeCheck(_object*, _typeobject*) in juce_python.cpp.o
      PyObject_TypeCheck(_object*, _typeobject*) in juce_python_audio_basics.cpp.o
      PyObject_TypeCheck(_object*, _typeobject*) in juce_python_audio_devices.cpp.o
      ...
  _PyType_Ready, referenced from:
      pybind11::detail::make_static_property_type() in Main.cpp.o
      pybind11::detail::make_default_metaclass() in Main.cpp.o
      pybind11::detail::make_object_base_type(_typeobject*) in Main.cpp.o
      pybind11::detail::make_new_python_type(pybind11::detail::type_record const&) in PopsicleDemo.cpp.o
  _PyType_Type, referenced from:
      pybind11::detail::make_static_property_type() in Main.cpp.o
      pybind11::detail::make_default_metaclass() in Main.cpp.o
      pybind11::detail::make_default_metaclass() in Main.cpp.o
      _pybind11_meta_call in Main.cpp.o
      _pybind11_meta_setattro in Main.cpp.o
      _pybind11_meta_getattro in Main.cpp.o
      _pybind11_meta_dealloc in Main.cpp.o
      ...
  _PyUnicode_AsEncodedString, referenced from:
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::detail::error_fetch_and_normalize::format_value_and_trace() const in Main.cpp.o
      pybind11::detail::string_caster<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t>>, false>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyUnicode_AsUTF8AndSize, referenced from:
      pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::load(pybind11::handle, bool) in Main.cpp.o
      pybind11::detail::type_caster<juce::StringRef, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::String, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::Identifier, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::load(pybind11::handle, bool) in juce_python_core.cpp.o
  _PyUnicode_AsUTF8String, referenced from:
      pybind11::str::operator std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>() const in Main.cpp.o
  _PyUnicode_DecodeLatin1, referenced from:
      pybind11::detail::type_caster<char, void>::cast(char, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyUnicode_DecodeUTF32, referenced from:
      pybind11::detail::string_caster<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t>>, false>::decode_utfN(char const*, long) in juce_python_gui_basics.cpp.o
  _PyUnicode_DecodeUTF8, referenced from:
      pybind11::detail::string_caster<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, false>::decode_utfN(char const*, long) in PopsicleDemo.cpp.o
  _PyUnicode_FromFormat, referenced from:
      pybind11::detail::make_new_python_type(pybind11::detail::type_record const&) in PopsicleDemo.cpp.o
  _PyUnicode_FromString, referenced from:
      pybind11::detail::make_static_property_type() in Main.cpp.o
      pybind11::detail::make_default_metaclass() in Main.cpp.o
      pybind11::detail::make_object_base_type(_typeobject*) in Main.cpp.o
      pybind11::detail::dict_getitemstring(_object*, char const*) in Main.cpp.o
      pybind11::str::str(char const*) in Main.cpp.o
      pybind11::detail::make_new_python_type(pybind11::detail::type_record const&) in PopsicleDemo.cpp.o
  _PyUnicode_FromStringAndSize, referenced from:
      pybind11::str::str<unsigned long, 0>(char const*, unsigned long const&) in juce_python.cpp.o
      pybind11::detail::type_caster<juce::StringRef, void>::cast(juce::StringRef const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::String, void>::cast(juce::String const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  _PyWeakref_NewRef, referenced from:
      pybind11::weakref::weakref(pybind11::handle, pybind11::handle) in Main.cpp.o
  _Py_Finalize, referenced from:
      pybind11::finalize_interpreter() in juce_python.cpp.o
  _Py_InitializeFromConfig, referenced from:
      pybind11::initialize_interpreter(PyConfig*, int, char const* const*, bool) in juce_python.cpp.o
  _Py_IsInitialized, referenced from:
      pybind11::detail::embedded_module::embedded_module(char const*, _object* (*)()) in PopsicleDemo.cpp.o
      pybind11::detail::precheck_interpreter() in juce_python.cpp.o
  __PyObject_GetDictPtr, referenced from:
      pybind11::detail::clear_instance(_object*) in Main.cpp.o
      _pybind11_traverse in PopsicleDemo.cpp.o
      _pybind11_clear in PopsicleDemo.cpp.o
  __PyThreadState_UncheckedGet, referenced from:
      pybind11::detail::get_thread_state_unchecked() in Main.cpp.o
  __PyType_Lookup, referenced from:
      _pybind11_meta_setattro in Main.cpp.o
      _pybind11_meta_getattro in Main.cpp.o
  __Py_Dealloc, referenced from:
      Py_DECREF(_object*) in Main.cpp.o
      Py_DECREF(_object*) in PopsicleDemo.cpp.o
      Py_DECREF(_object*) in juce_python.cpp.o
      Py_DECREF(_object*) in juce_python_audio_basics.cpp.o
      Py_DECREF(_object*) in juce_python_audio_devices.cpp.o
      Py_DECREF(_object*) in juce_python_audio_formats.cpp.o
      Py_DECREF(_object*) in juce_python_audio_processors.cpp.o
      ...
  __Py_FalseStruct, referenced from:
      pybind11::detail::type_caster<bool, void>::load(pybind11::handle, bool) in PopsicleDemo.cpp.o
      pybind11::detail::type_caster<bool, void>::cast(bool, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
  __Py_NoneStruct, referenced from:
      pybind11::none::none() in Main.cpp.o
      pybind11::detail::object_api<pybind11::handle>::is_none() const in Main.cpp.o
      pybind11::detail::object_api<pybind11::detail::accessor<pybind11::detail::accessor_policies::generic_item>>::is_none() const in juce_python_audio_basics.cpp.o
      pybind11::detail::PyNone_Check(_object*) in juce_python_audio_devices.cpp.o
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
      pybind11::detail::type_caster<juce::var, void>::cast(juce::var const&, pybind11::return_value_policy, pybind11::handle) in juce_python_core.cpp.o
  __Py_NotImplementedStruct, referenced from:
      pybind11::cpp_function::dispatcher(_object*, _object*, _object*) in Main.cpp.o
  __Py_TrueStruct, referenced from:
      pybind11::detail::type_caster<bool, void>::load(pybind11::handle, bool) in PopsicleDemo.cpp.o
      pybind11::detail::type_caster<bool, void>::cast(bool, pybind11::return_value_policy, pybind11::handle) in juce_python_audio_basics.cpp.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [popsicle_demo_artefacts/Popsicle Demo.app/Contents/MacOS/Popsicle Demo] Error 1
make[1]: *** [CMakeFiles/popsicle_demo.dir/all] Error 2
make: *** [all] Error 2

I then made the following change to CMakeLists.txt:

# set (Python_ROOT_DIR "/Library/Frameworks/Python.framework/Versions/Current")
set (Python_ROOT_DIR "/opt/homebrew/opt/python@3.11/Frameworks/Python.framework/Versions/Current")

And then it built successfully.

Note that you can get the current homebrew python via brew --prefix python which gives /opt/homebrew/opt/python@3.11 then join it with /Frameworks/Python.framework/Versions/Curren and you have the above. If you want, I can add this via a PR to do this programmatically if homebrew python is found.

I saw that the resultant binary of Popsicle Demo is about 60 MB

Does this include the python standard lib? I searched for juce_add_binary_data but couldn't find an instance that added the python standard library.

kunitoki commented 6 months ago

Yes that thing is in the branch (the opened PR).

As you already discovered you have to change the python root (or comment it). I force that so i can move quickly among versions (i use upstream python so i have universal2 libraries, homebrew usually ship either intel or arm binaries, so i cannot test intel builds on my M1 by launching apps with arch -x86_64), you might also need to tweak or comment https://github.com/kunitoki/popsicle/blob/dev/small_updates/demo/CMakeLists.txt#L79 (depending on which platform you want)

kunitoki commented 6 months ago

I saw that the resultant binary of Popsicle Demo is about 60 MB

debug or release ? either way, the cmake in master will not strip bnaries (both debug and release) but the one in the branch will strip release builds and include the python library (it's 26mb for ARM only in release).

shakfu commented 6 months ago

debug or release?

It was built using cmake --build . --config Release.

either way, the cmake in master will not strip bnaries (both debug and release) but the one in the branch will strip release builds and include the python library (it's 26mb for ARM only in release).

Cool. Thanks, will check out the branch version when I can.

shakfu commented 6 months ago

@kunitoki

I've had a go at building the demo in the dev branch and I had to make a few changes to make it work on my Mac m1 air. I've attached the fixed files with the following caveats: universal arch build didn't work for me for some reason (maybe I need to install Rosetta) and the resulting .app was working ok, but it was not stripped from some reason and was 67.1 MB

The two files which I modified are demo/CMakeLists.txt and popsicle/cmake/ArchivePythonStdlib.py.

my-fixes.zip

kunitoki commented 6 months ago

Can you check the size of your python3.zip in the build folder ?

kunitoki commented 6 months ago

What is the problem with the python script ? Can you post the error message or the issue ?

shakfu commented 6 months ago

Can you check the size of your python3.zip in the build folder ?

ncdu 2.3 ~ Use the arrow keys to navigate, press ? for help
---$HOME/src/popsicle/demo/build --------------------------------
  113.9 MiB [###########] /CMakeFiles
   87.2 MiB [########   ] /JUCE
   64.0 MiB [######     ] /popsicle_demo_artefacts
   24.3 MiB [##         ] /juce_binarydata_BinaryData
   20.3 MiB [#          ] /python
    6.8 MiB [           ]  libBinaryData.a
    6.8 MiB [           ]  python311.zip
   72.0 KiB [           ]  Makefile
   24.0 KiB [           ]  CMakeCache.txt
   20.0 KiB [           ] /modules
    8.0 KiB [           ]  .DS_Store
    4.0 KiB [           ]  cmake_install.cmake

What is the problem with the python script ? Can you post the error message or the issue ?

The only issue with ArchivePythonStdlib.py was that it failed when it ran because build/python didn't yet exist, so basically this was solved with the following

-    shutil.rmtree(str(final_location))
+    if final_location.exists():
+        shutil.rmtree(final_location)
+    else:
+        final_location.mkdir()

The rest of the changes were trivial like not converting a pathfile.Path to a str() before using it in a shutil.* function since these are already recognized.

shakfu commented 6 months ago

@kunitoki congrats on the release which is pretty substantive!

Incidentally, I wanted to ask are python extensions from the sodlib embeddable using juce_add_binary_data?

kunitoki commented 6 months ago

Thanks! It's a small step towards a better integration.

Yes, but you need to unpack them at runtime into a site-packages

shakfu commented 6 months ago

Yes, but the whole pybind11 direction is coming together nicely.

Thanks for the clarification.

shakfu commented 6 months ago

@kunitoki

FYI, I just posted an update about the recent popsicle release in the dawdreamer-forum

kunitoki commented 6 months ago

That is nice from you @shakfu thanks a lot ! I was in the process of thinking how i could give this a bit more visibility, and you already did an unexpected great start !!!