Audio Units, auval and MIDI


#1

I’m struggling very hard to get my AU Synthesizer to accept MIDI Input.

So far my plugin seems to work with Garage Band and the Juce Plugin Host. When I run my plugin in AULab it doesn’t get any MIDI, and aulab tells me “ERROR: -4 IN CALL MusicDeviceSendMIDI” during the MIDI Test.
I used the introducer to create a new audio unit project to test its behaviour with auval. Even when I set JucePlugin_WantsMidiInput and JucePlugin_IsSynth both to one, auval does not test MIDI with the newly created audio unit at all.

During development I found out, that Garage Band and Juce Plugin Host use the old Component Manager based AU handling which is deprecated with Mountain Lion. AULab und auval seem to work without the component manager.
Could it be that Apple changed something in the MIDI handling in AU when they deprecated the component manager?

Does anyone know a document, that explains how MIDI works with AU? The Apple Documentation (Audio Unit Programming Guide) does not cover this topic at all.

Does anyone know, how plugin tells auval that it has to test MIDI?

Does anyone has a similar problem (and maybe a solution to it)?

Does anyone know, what ERROR -4 in auval means?


#2

I ran auval -v on my plugin under 10.6, 10.7 and 10.8.
The MIDI Error did not occured with Snow Leopard. So this could be an indication, that this error has to do with the new AU handling introduced with 10.7.


#3

Accidentally I found a solution, which is not nice but it workts:

When I edit the Infor-plist of my plugin and change the type from ‘aumu’ to ‘aufx’, auval -a lists my plugin twice; onse as a ‘aufx’ and once as ‘aumu’. I guess the last one comes from tho old style thng resource. (When I delete the .rsrc file in the bundle, AULab does not find it any more as a music device.)
The plugin will pass the auval -v MIDI test and AULab sends MIDI to the plugin.

I don’t like this solution. Maybe anyone has a better one.

Funny that I get things working, by hurting the specification. (That means, if there is a AU specification at all. I never found it.)


#4

Finally I understood what was going wrong, and found a good solution.

The short version

The macro JUCE_FACTORY_ENTRY does not produce a factory function with MIDI support.

To work around this define the macro JUCE_DISABLE_AU_FACTORY_ENTRY and set it to 1.

Define the entty function like this

extern "C" __attribute__((visibility("default"))) void* JucePlugin_AUExportPrefix##Factory (const AudioComponentDescription* desc)
{
  return AUMIDIEffectFactory<JuceAU>::Factory (desc);
}

The request to Jules

Please change the JUCE_FACTORY_ENTRYX macro in juce_AU_Wrapper.mm like this

#define JUCE_FACTORY_ENTRYX(Class, Name) \
    extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc); \
    extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc) \
    { \
        return AUMIDIEffectFactory<Class>::Factory (desc); \
    }

The background

The factory class does several lookups for the AU methods that have to be invoked. The files AUPluginDispatch.h and AUPluginDispatch.cpp define template classes that do the lookup for the class that is passed as template parameter. There are different template classes for audio units with different functionality. The class AUBaseFactory does only lookup for the very basic functionality, which does not include MIDI. The class AUMIDIEffectFactory does the same but includes lookup for the methods handling MIDI Events and SysEx. There is also a class AUMusicDeviceFactory that also looks up startNote and stopNote methods, but as for as I can see, the AUWrappen does not implement theese, so this factory template is not the one of choice.


#5

Interesting, thanks for the detailed report!

Seems unnecessary to use the midi base class for non-midi plugins though - how about this?

[code]#if JucePlugin_ProducesMidiOutput || JucePlugin_WantsMidiInput
#define FACTORY_BASE_CLASS AUMIDIEffectFactory
#else
#define FACTORY_BASE_CLASS AUBaseFactory
#endif

#define JUCE_FACTORY_ENTRYX(Class, Name)
extern “C” attribute((visibility(“default”))) void* Name ## Factory (const AudioComponentDescription* desc);
extern “C” attribute((visibility(“default”))) void* Name ## Factory (const AudioComponentDescription* desc)
{
return FACTORY_BASE_CLASS::Factory (desc);
}
[/code]


#6

You’re totally right.

As far as I can see tha plugin type should be consistent with the base. If the plugin is an aumf, then auval will expect MIDI functionality and the test will fail.
So the user (or the introjucer) has to make sure, that the plugin type in the Info.plist is aumf or aufx depending on the MIDI usage. Of course this holds also for the plugin type in the resources, but than can be controlled by the macros.

Of course it is not really a bug, when an aumf does not support MIDI and is derived from the AUBaseFactory. It’s only tha auval report that will be irritating. Ar least this is, what I can tell from my experiments.


#7

Thanks, I’ve added that change now.


#8

Using latest tip, the Juce Demo Plugin doesn’t work on OS X 10.8.

But with the workaround that dnop mentioned previously it does work:

(tested on 10.8.2 and 10.6.8 )


#9

[quote=“yairadix”]Using latest tip, the Juce Demo Plugin doesn’t work on OS X 10.8.

But with the workaround that dnop mentioned previously it does work:

(tested on 10.8.2 and 10.6.8 )[/quote]

Doesn’t work in which host? Could just be that the host only wants to load effects and not synths?


#10

“auval -v” and Logic


#11

Here we go: Same issue, next chapter.

I spent almost a day now, to build an Midi Effect. The auval test failed all the time with the strange error message "ERROR: -4 IN CALL MusicDeviceSendMIDI".

Finally I found out, that there is a bug in the Core Audio SDK. The funny thing about this is, that I found this comment in a SDK file:

// I don't know what I'm doing here; conflicts with the multiple inheritence in MusicDeviceBase.

Obviously the author has made a work around for MusicDeviceBase, but AUMidiEffectBase does not work properly. The Midi Dispatching is broken. Therefore building an aumf type plugin can't work

To fix this, I recommend following modification to juce_AU_Wrapper.mm: Define the following class:

class AUMIDIEffectBaseFixed : public AUMIDIEffectBase
{
public:
  AUMIDIEffectBaseFixed(AudioComponentInstance inInstance, bool inProcessesInPlace = false) : AUMIDIEffectBase(inInstance, inProcessesInPlace){}

  virtual OSStatus MIDIEvent(UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
  {
    return AUMIDIBase::MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame);
  }

  virtual OSStatus SysEx(const UInt8* inData, UInt32 inLength)
  {
    return AUMIDIBase::SysEx (inData, inLength);
  }
};

Then derive JuceAU from AUMIDIEffectBaseFixed instead.

A side effect of this is, that auval will warn, if you build an aufx type with MIDI support. This is OK, aufx are not meant to support MIDI. Surprisingly aufx with MIDI input seem to work in Ableton life.

 


#12

Excellent stuff, much appreciated! Will take a look at this right away..


#13

Ok, I've implemented a simpler version of this which I think should do the job - I don't have a test-case for this, so would appreciate feedback!


#14

I gave it a short test, but unfortunately it did not work.

When I build an instrument the auval test fails because of a wrong channel format. Maybe an aumu should still be derived from MusicDevice and not from AUMIDIEffectBase. Don't know.

When I build a Midi effect (aumf) the auval test still fails because of MIDI. I have never seen that using method, so I don't know exactly what it does. But here is, what I know:

AUMIDIEffectBase is derived from AUBase and from AUMIDIBase. Both have implementation of MIDIEvent and SysEx. The AUBase implemention just returns a code for "not implemented'. The implementation in AUMIDIBase does the work.

The lookup functions in the AU Factory class template casts the AU Instance to AUBase. When the derived class implements the Midi functions that lookup delivers the implementation of the derived class because the functions are virtual. This seems not to be the case with the using construct.

Hope, this helps.

 


#15

Strange - I'd have expected the using directive to have the same effect, but I've checked in another version now which just overrides them..


#16

Hi Jules,

Correct me if I am wrong, but I believe the fix that you implemented today will break synth type plugins since the JuceAU class in juce_AU_Wrapper.mm will no longer inherit from MusicDeviceBase when JucePlugin_IsSynth is defined.

When I encountered this problem recently, I simply added the following to the JuceAU class:

#if !JucePlugin_IsSynth
OSStatus MIDIEvent (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
{
    return AUMIDIBase::MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}

OSStatus SysEx (const UInt8* inData, UInt32 inLength)
{
    return AUMIDIBase::SysEx(inData, inLength);
}
#endif

This should make sure these two functions get properly routed to AUMIDIBase when the plugin is derived from AUMIDIEffectBase (as is the case when JucePlugin_IsSynth is not defined). The routing already works properly in synth plugins (inherited from MusicDeviceBase) because that class overrides these two functions and calls the desired base class versions in AUMIDIBase.

I hope this helps.

EDIT: By the way, auval only performs the "Test MIDI" test if there are one or more parameters defined in the plugin (AudioProcessor derived class returns > 0 from getNumParameters()). A plain vanilla Introjucer-created plugin will not fail auval validation. It looks like it may be related to testing parameter setting/scheduling as this test is also only performed if there are one or more parameters defined (see the following output from auval that is only there if getNumParameters() returns > 0).

Checking parameter setting
Using AudioUnitSetParameter
Using AudioUnitScheduleParameter
  PASS

Test MIDI
  PASS

Regards,
Brian


#17

Ok, I've made yet another check-in - would appreciate feedback!


#18

Hi,

I'm using latest JUCE head and AudioUnit 2014 headers (AU 1.1) and I have the following weird AUVAL issues:

I want my plug-in to accept MIDI input and show as MIDI Controlled Effect...

- When I run the plug-in in debug it pass the validation just fine!

- When I package/release suddenly I get the dreaded - "Test MIDI
ERROR: -4 IN CALL MusicDeviceSendMIDI
"

Anyone exprienced that issue?

Any idea how can I track it down? (especially when it is only on my release build :( )

(If I untick the Plug-in Accepts MIDI all is fine of course but I lack the functionallity I want).

 

 


#19

A bit late, be sure you got JucePlugin_WantsMidiInput defined to 1

perhaps we could get an assertion added to check that JucePlugin_WantsMidiInput is 1 if the type is kAudioUnitType_MusicDevice or kAudioUnitType_MusicEffect  ?


#20

Well, not really.. There are use-cases for building a synth that takes no midi input, e.g. a step sequencer or other free-running sound generator.