Using JUCE within command line tool or MaxMSP external


#1

Hi

 

I'm looking to build a Max external and could make use of some JUCE classes. I had thought it would be as simple as adding JuceLibraryCode to the project (as compile sources), plus #include "JuceHeader.h"

But there are errors.

 

So I started to look at doing a simple "hello world" command line program, and link JUCE in with that. I get the same error, namely that the code in main.cpp is:

printf("hello world\n");

MidiMessage m;

and causes the error:

Undefined symbols for architecture x86_64:
  "juce::MidiMessage::MidiMessage(int, int, int, double)", referenced from:
      _main in main.o
  "juce::MidiMessage::~MidiMessage()", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

So it must be that it's not finding what it needs. I've checked that I add JuceLibraryCode and JuceLibraryCode/modules to the search path.

I'm not sure what else would be required. The JuceLibraryCode was added to compile sources.

What is it that Introducer does when creating a tool that I've mentioned here?

 

thanks,

Andrew

 

 


#2

hi andrew,

Looks like the JUCE classes are not being compiled in your project. Rather than doing it like you describe, a nicer way would be to use the Introjucer to build a new Static Library project and select the JUCE modules that you need, then just link your max external to the library and #include "JuceHeader.h"

hope that helps.

oli

 


#3

For my part ( https://github.com/nicolasdanet/Jojo ) i always use “The Introjucer” to make externals with JUCE. No need in that case to build and link to any library. It is a matter of taste.

Note that for most of the classes (specially those that are not in the core module) JUCE needs an event loop to run in the background. I did NOT experiment on Windows platform and/or Max 7 so i have no idea if extra work is needed to manage it.

@olilarkin : have you successfully use JUCE on Windows and/or with Max 7?


#4

that way looks fine too

@olilarkin : have you successfully use JUCE on Windows and/or with Max 7?

no sorry


#5

Thanks, someone else then?


#6

thanks Oli.

I think I have something working now!

 

So first off, I had tried the static library route and found loads of errors.

Things like
:

ndefined symbols for architecture i386:
  ".objc_class_name_NSAppleScript", referenced from:
      pointer-to-literal-objc-class-name in libStaticJuce.a(juce_core.o)
  ".objc_class_name_NSArray", referenced from:
      pointer-to-literal-objc-class-name in libStaticJuce.a(juce_core.o)

 

So if you look at this post:

http://www.juce.com/forum/topic/juce-static-library-includes-link-nsapp

it appears that this is caused by requiring the Cocoa framework to be linked, i.e. in the Build Phases, link Binary with Libraries, add Cocoa.framework. Similarly,

Undefined symbols for architecture i386:
  "_vDSP_vclr", referenced from:
      juce::FloatVectorOperations::clear(float*, int) in libStaticJuce.a(juce_audio_basics.o)

is due to requiring the Acclerate frameworks for DSP.

(So these dependencies are not included when you build a static library?)

 

the method to build the static lib, is to create one using the Introjucer, and then find that in Build/Debug of teh static library project; add that in build phases, link binary with libraries, as with the in-built OS X frameworks.

So then I have a command line tool!

 

Then turn to the Max external. I added Accelerate and Cocoa to get down to 28 mysterious errors. This kind of thing:

Undefined symbols for architecture x86_64:
  "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__init(char const*, unsigned long)", referenced from:
      juce::String::toStdString() const in libStaticJuce.a(juce_core.o)
  "std::__1::basic_ostream<char, std::__1::char_traits<char> >::~basic_ostream()", referenced from:
      juce::NumberToStringConverters::StackArrayStream::writeDouble(double, int) in libStaticJuce.a(juce_core.o)
  "std::__1::basic_ostream<char, std::__1::char_traits<char> >::operator<<(double)", referenced from:
      juce::NumberToStringConverters::StackArrayStream::writeDouble(double, int) in libStaticJuce.a(juce_core.o)
  "std::__1::basic_streambuf<char, std::__1::char_traits<char> >::sync()", referenced from:

 

It seems that migt be due to use of the wrong compiler, Gromit. this post seemed to indicate such a thing:

http://stackoverflow.com/questions/24891962/yaml-cpp-link-error-on-macos-and-gcc-4-8

 

so I switched compiler to use libc++

 

and the external now compiles!

 

any thoughts on why the compiler did need changing from libstdc++, could be helpful.

 

thanks Oli for getting back!

 

 


#7

wow, this is really interesting Nicolas.

 

I think I need a fair bit more explanation though. I downloaded your repository. By adding the externals to file preferences, I an the externals you've built - eg [jojo] runs a JUCE component window - inside Max!

is this working because Max 5 to 7 have been built with Juce?

how are these examples to be compiled? Say, for instance, I wanted to compile my own version of jojo, would that be done in the MaxSDK 6.1.4?

At present, I moved a copy of the project there and opened the .jucer file but it won't compile there - about 14 errors, mainly I think it doesn;t find what it needs. Probably unsurprisingly.

So I wonder, how did you intend this to be used? It would be good to hear more on how you compile the externals.

Also, as you mentioned, there must be a load of event scheduling in Max that's hidden from you. But this could be a great way to bring a lot of functionality we have in JUCE to MaxMSP...

 

 

re the way it's coded: I can see you have the classic definition of max object:

typedef struct _jojo {

public:
    _jojo() : window_ (new MainWindow()) { }

public:
    t_object ob_;
    ulong error_;
    ScopedPointer < MainWindow > window_;
    
    } t_jojo;

 

and you define your external as you would in the Max SDK:


void *jojo_new (t_symbol *s, long argc, t_atom *argv)
{
    t_jojo *x = NULL;
    
    if ((x = (t_jojo *)object_alloc (jojo_class))) {
    //
    ulong err = (x->error_ = JOJO_GOOD);
    
    try {
        new (x) t_jojo;
    }
    
    catch (...) {
        err = (x->error_ = JOJO_ERROR);
    }
    
    if (err) {
        object_free (x);
        x = NULL;
    }
    //
    }
    
    return x;
}

 

following it up with typical function calls, such as when you get a bang messge:

void jojo_bang (t_jojo *x)
{
    if (!systhread_ismainthread()) { error ("Always in the main thread!"); }
    else {
        x->window_->setVisible (true);
    }
}

 


#8

that's not the compiler but which C++ std library to use. On mac these days there are two and the libc++ one supports C++11 IIRC. It think you'll need to have matching std libraries in the max external xcode project and in the juce library project. You can set the juce one using introjucer.


#9

yeah, that figures. Maybe Introjucer selected libc++ and then the Max project is set to compiler default. Good that it's possible to switch as required.


#10

So I've managed to compile your external 'jojo.mxo', but not without some difficulty.

I think you do need a version of JUCE. Maybe you have included that due to the way your project is setup?

 

firstly it had juce:: style linking errors - not finding juce classes I think.

I added in the static JUCE library as mentioned elsewhere in this thread: compiled with Introjucer (need to check it is i386/x86_64 as you require)

The JuceLibraryCode folder was also added in compile sources, with the modules folder inside it. The search path to this is in the header search path.

Your code for jojo.cpp needs a simple change - 'ulong' is not recognised - so maybe put 'unsigned long' there instead?

However, it then throws a load of errors - 86 in all.

But these seem to do with finding the requisite frameworks. In particular: Cocoa, Accelerare, IOKit, OpenGL, QuartzCore, WebKit are all required for jojo.mxo

 

But then I have a version of jojo.mxo that is compiled in my sdk-debug folder.

 

So I hope that's useful for you and others looking to compile your JoJo collection.

 

A few things more:

Have you checked comiling on other machines? Maybe some kind of guide like ths could be useful for the README?

I also wonder with what you mentioned before, that using Introjucer should be fine for building externals. There is an interesting link between Max and JUCE, namely that Max is built with JUCE - that's presumably why your externals work, even if I don't understand the details. There is a hint from one of the cycling74 developers here:

https://cycling74.com/forums/topic/juce-2/

 

the other question I have might be about timing threads. How is the paint() function in the Juce component window thrown up by JoJo called now? Max has it's own GUI thread,

plus it has a MIDI event thread that happens every millisecond (for messages such as bang(), float(double f) or myChosenMessage(float f)

plus an audio thread for MSP events of the process(float* buffer) kind

so how do we think about these threads if we have a Juce component window open? Is this painted using the same paint call that happens to paint the Max window?

 

anyhow, interesting work here!

 


#11
Is this working because Max 5 to 7 have been built with Juce?

Not really.

It works on mac mainly because max uses the Cocoa framework and such is a NSApplication.
Here a dangereous hack to see it in action ( https://github.com/nicolasdanet/Flop/blob/master/Projects/Flop/flopNSAppWalkie.m ).

AFAIK max runs the event loop in the main thread. That’s the reason why the JUCE messages machinery works fine. And that’s the reason why you must call the GUI stuff only from that thread.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/#//apple_ref/doc/uid/20000012-SW9

All that is “crossed fingers” experiments NOT tested intensively (i mean on stage or driving a nuclear submarine). I guess that’s it’s not portable to Windows without extra works.

Would that be done in the MaxSDK 6.1.4?

I don’t have max 6-7 and i never tested JUCE with those SDK. IMHO it should. But 64-bit changes must be done accordingly. I have no way to do it (no time and no money). Let me know if you want to give a try.

How are these examples to be compiled?

On my computer the “Jojo” folder, the “c74support” folder and the “JUCE” folder are all siblings. Once at the same level the “.jucer” projects may compile fine out of the box.

But this could be a great way to bring a lot of functionality we have in JUCE to MaxMSP...

It is.

Max is closed source and i’m not a cycling74 insider. So all that is providing with no warranties :wink:

EDIT: Cross posting as usual :wink:


#12

sorry Nicolas, I don't really follow, but would be intruiged to hear from you how it works.

if you have MaxSDK-5, what is the file/folder structure?

The 'c74support' folder will be there. Then do you have a JUCE folder in there, and the project folder? (normally these would go in a sub-folder of examples). When trying this I have the same errors.

And is there any documentation on JoJo ? i.e. your aims of integrating Juce within Max?

 

 

 

 


#13

Sorry i forget that i changed the “MaxSDK” folder…

On my computer it is at the same level:

Folder/
    JUCE/
    c74support/
    Jojo/

I’ll probably change the repository to make it easier :wink:


#14

Updated my repository. File hierarchy below must works now.

Folder/
    JUCE/
    MaxSDK-5.1.7/
    Jojo/

But please note that i am with an “obsolete” OS X 10.6 and i’m pretty sure that with the newest it will not work “out of the box”. I do not have any plan to support it. No time, no money, no fun.


#15

After much head-scratching, I've reached an impasse with JoJo that is puzzling me. Using Xcode 6.4, the latest normal version of JUCE, and Max SDK 7.0.3. I'm using the 10.6 SDK and the deployment target is 10.6. 

 

I _think_ I have everything set up correctly for this, but I'm getting a strange error in one of the JUCE modules. I've attached a screenshot of the problem. Obviously, this is the same JUCE install we're using for our commercial products, and it doesn't throw this error, so this leads me to believe this is a compiler or linker setting. Any thoughts? 

 


#16

IMHO it’s more a conflict between the global “post” function from the Max SDK (that is a macro now in Max 7 SDK) and the MessageBase::post() function.

( https://github.com/Cycling74/max-sdk/blob/master/source/c74support/max-includes/ext.h#L62 )
( https://github.com/julianstorer/JUCE/blob/master/modules/juce_events/messages/juce_MessageManager.h#L180 )

Undef before to include the JUCE header?

( https://github.com/nicolasdanet/Jojo/blob/master/Projects/JojoSlider/Source/jojoSlider.h#L51 )

#include "ext_systhread.h"

#undef post
#undef error

#include "JuceHeader.h"

Tell me if it works (i could modify the code accordingly).


#17

That definitely worked. The project compiles now. (I put it at line 52 of jojoSlider.h) 

Now I'm on to some linker errors for 64-bit. I thought these were in the Foundation framework, which I added to the project, but that didn't seem to help. 

 

 

Undefined symbols for architecture x86_64:

  "_class_addProperty", referenced from:

      transcribeProperties(objc_class*, glue_class_ro_t*) in libarclite_macosx.a(arclite.o)

  "_objc_autoreleasePoolPush", referenced from:

      __ARCLite__load() in libarclite_macosx.a(arclite.o)

  "_objc_retain", referenced from:

      __ARCLite__load() in libarclite_macosx.a(arclite.o)

     (maybe you meant: _objc_retainedObject)

  "_property_copyAttributeList", referenced from:

      transcribeProperties(objc_class*, glue_class_ro_t*) in libarclite_macosx.a(arclite.o)

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)


#18

Ah, my mistake. I had made an entirely new project, and "Implicitely Link Objective-C Runtime Support" was defaulted to "Yes." Changed it to the usual "No" and we're good to go. 

 

Built and instances, but the UI doesn't show. About to start debugging. 

EDIT: After following things in the debugger for a bit, here's what I can report: the external "works" in as much as it passes values from the input to the output. MainComponent is called, and a slider object is created and added to the view. However, the view is never shown, and paint() is never called. 

Man, I would really like this to work, as I have a UI project I'm working on where the clients are using Max to create the app (I know) and it needs a lot of custom UI work; I'd very much like to use JUCE for the UI stuff instead of making custom Max UI externs or using JS Painter. 

Once again, this is the latest JUCE, the Max 7.0.3 SDK, the latest JoJo stuff, and Max 7.0.4. I built Universal Binary against the 10.6 SDK in Xcode 6.4. 


#19

Once the [jojoSlider] external is added is there any window visible when you “bang” it?

Open the “jojoSlider.maxhelp” patch if you do not see what i mean.


#20

Finally does it work? Or is there something broken with Max 7? Thanks for feedback.