MidiFX Plugin issues in Logic Pro X
-
Testing a MidiFX Plugin built in Hise in Logic.
Having the MidiFX plugin on one track, with recorded midi on it, and in the slot under it I log the midi messages it sends out.If I have the MidiFX track selected, it logs the intended midi messages, that's great!
If I have another track selected none of the intended midi messages is coming out, instead this is what is logged from that track:Error: send() called with malformed MIDI event [NoteOff channel:16 pitch:72 [C4] velocity:1] [Aftertouch channel:1 value:93] [ControlChange channel:14 number:7 [Volume] value:111] [PolyAftertouch channel:12 pitch:1 [C#-2] value:0] [NoteOn channel:1 pitch:171 [D#12] velocity:62] [PolyAftertouch channel:8 pitch:1 [C#-2] value:0] [Aftertouch channel:13 value:0] >
Anybody know what is happening here?
Is this "malformed MIDI" generated inside the Hise plugin or is it coming from Logic? -
@ulrik said in MidiFX Plugin issues in Logic Pro X:
171
that's a high note. I just took the liberty to calculate the frequency and this will play your sample at the root frequency of 159kHz :)
I'm pretty sure you've done some calculations wrong. Make sure to limit the range into 0 ... 127 for every MIDI parameter.
-
@Christoph-Hart I think you misunderstood my question, this midi events is not generated by the coding or calculations inside the plugin but it's sent from the plugin, or from Logic it self, when another track them the plugin track is selected.
I'll upload a video
-
@Christoph-Hart Take a look at this
VIDEOIf another track than the plugin track is selected, the MidiFX plugin or Logic, outputs a lot of blaha events, and I wonder why this is happening?
-
@ulrik I know that Logic might change the buffer size and other processing specs for the selected and armed track so this might have anything to do with it.
Is this happening with a vanilla midi fx plugin too?
-
@Christoph-Hart Ok, well something strange is really happening.
Take a look and a listen to what is happening here when I change selected track.... It seems to add a lot of garbage events, in this recording it lower the pitch as well.
And notice that only the recorded midi is sounding (you can see it in the logg as well)Here's also the snippet I used, very very simple, it only adds an interval to the original played note.
HiseSnippet 1022.3oc6W0saaaCElx1bn1acnEcO.Z8J4sLC4tt1BLTL033LXrlDiorfcWGqHsMajHEnnbq2PA5i1dW1KvdC1NT+XI2pz3YjLfNLegg4gme9NGxyGOdpRFvRRjJjU2SWEyPVeB1ekPuXzBBWflb.x5l3QgLh3HNke3Oi1eULIIgQQVVs+diJVc6fx97me29jPhHfUIBgNSxCXOkGw0URm58C7vvCIT1o7nZZeeuIARwHYnLEfSarKJlDbNYN6XhQsVXj0GMlx0RkulnYIHqN6Koq7WHeoHW+y3I7mGxLKFh7AGkK9PYH0fXiTznE7P5zxzNAgrvSqJBsyKBeF1jtqkWULtU1F1UVTudX0ZS30dC3MrN7bqAuFfjUMH0IGR2F6Gn3w5pcL34iwSDZlZFAJ60gRttnVyaiGIAMD5AQjyYGpfEqsv4Att6YCe0+a60CJ8IZ6kDk8SnzLUVRBserco4yY5QxnXo.V3b2Z5b2JqifZyS4vOdr8XwbtfMHPwfZwQExcLpNKUDn4RgsTbrTyNQ3zu2u0q6QPRAm0C3yEREa7RSXzpTFXR2dcM3RH.+VpF.Gi0GmF8blx32LUVxB2TmyXgx.td0ZM3UYF3uurdxloOILk4jGyxrYPRobgXO6rtiADJs.7C2y1HFh7d1t8ysjOy1Ycf9bSj52qaC9qTmF8Z0lGQf8TxTA0wjfegs6f60uHZutm8aWPmM6Jrh9OndkmChbHr+pr.Ng5rNsmWUFeu0os1Okl9t0AykVkLLzjHWRoXSCMLRE1zrKcDY0G3.2.g0JBsoa16iu3d+5TSA4cW0TTJlH35ShYhKhvBUzRZ3IJPEnpNik3SKXI7C4TlBwoF96ZmYnLXWw4dKOzOM4.hlT5LvuPrhYJM2jFVGvVBL34TPcwGvRNWKiyzsfK.Dukg9UqCq2K7VsdwaHdujS0Kp18W7Vv3yWT6IiIdIZVrO+WY0euIp9yON+dD4U0V5gdWNTf8VRSCI5MozMuiUrAbVuAOpA7hDf.odbux342VHda7TtNXQyXrUCXDNMuNvXwqi2DOd1LVftBfcvu0nAWKg+FWvDJ2I6Q4SR0lKvfPlZ3UwbJwa8bJVsuNlKoyGvykzs7HoooRr9Cr8kLEPBSPOUV3Cms6ctFrYKdR3RhzGVOHrSj4+OQ4+IHJ+QYplKlCCKp3vCgXXPNefgJfAQWHXgl6fVsL2VxW6ZVap.9PGP1h+B9Tr4PyZqhMGVt4+JwHhDnjOKHu6xP5biLIPdKx9ef.0hYs8vxYYvX2AtHy3gOKHvTh+JnIoYat2NXyWuC1b+cvluYGr4A6fMObGr4QuWaLjmOIUKixa4.ASGmQCYYMVPfauY2zQ+M3CeZFN
-
@Christoph-Hart It seems as soon as another track, than the plugin track, is selected, the plugin stops modifying the recorded events, and also jumps in to a "midi through" state.
-
@Christoph-Hart I found this thread in Juce forum:
https://forum.juce.com/t/garbage-midi-data-midi-fx-logic-pro-mac-m1/51774/13The garbage seems to come out only when Logic runs in Apple silicon processors, I switched my logic to run under Rosetta and that made the garbage go away.
I also found a fix patch from Februari this year, in the Juce code, for this behavior under silicon.
https://github.com/juce-framework/JUCE/commit/d5ad26a162ebde406f685ab36738b878c9b2d4efCould we have that fix in Hise as well (the juce code in the Hise code is older than Februari this year I suspect?)
-
@Christoph-Hart I downloaded Juce 7 and then built the ArppegiatorDemo project from the Juce examples, with these settings in Projucer:
Plugin Charasteristics:
- Plugin Midi Input
- Plugin Midi Output
- Midi Effect Plugin
Valid Architectures:
- X86_64
- arm64
- arm64e
I launched it in Logic Pro X under silicon processor M1, and all works as it should.
- No garbage midi events logged
- The Midi FX Plugin runs and outputs plugin generated events even if I have another track selected than the Midi FX plugin track
See/hear the result here:
The Juce plugin code for this project
/* ============================================================================== This file is part of the JUCE examples. Copyright (c) 2022 - Raw Material Software Limited The code included in this file is provided under the terms of the ISC license http://www.isc.org/downloads/software-support-policy/isc-license. Permission To use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ /******************************************************************************* The block below describes the properties of this PIP. A PIP is a short snippet of code that can be read by the Projucer and used to generate a JUCE project. BEGIN_JUCE_PIP_METADATA name: ArpeggiatorPlugin version: 1.0.0 vendor: JUCE website: http://juce.com description: Arpeggiator audio plugin. dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, juce_audio_plugin_client, juce_audio_processors, juce_audio_utils, juce_core, juce_data_structures, juce_events, juce_graphics, juce_gui_basics, juce_gui_extra exporters: xcode_mac, vs2022 moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1 type: AudioProcessor mainClass: Arpeggiator useLocalCopy: 1 pluginCharacteristics: pluginWantsMidiIn, pluginProducesMidiOut, pluginIsMidiEffectPlugin END_JUCE_PIP_METADATA *******************************************************************************/ #pragma once //============================================================================== class Arpeggiator : public AudioProcessor { public: //============================================================================== Arpeggiator() : AudioProcessor (BusesProperties()) // add no audio buses at all { addParameter (speed = new AudioParameterFloat ({ "speed", 1 }, "Arpeggiator Speed", 0.0, 1.0, 0.5)); } //============================================================================== void prepareToPlay (double sampleRate, int samplesPerBlock) override { ignoreUnused (samplesPerBlock); notes.clear(); currentNote = 0; lastNoteValue = -1; time = 0; rate = static_cast<float> (sampleRate); } void releaseResources() override {} void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override { // A pure MIDI plugin shouldn't be provided any audio data jassert (buffer.getNumChannels() == 0); // however we use the buffer to get timing information auto numSamples = buffer.getNumSamples(); // get note duration auto noteDuration = static_cast<int> (std::ceil (rate * 0.25f * (0.1f + (1.0f - (*speed))))); for (const auto metadata : midi) { const auto msg = metadata.getMessage(); if (msg.isNoteOn()) notes.add (msg.getNoteNumber()); else if (msg.isNoteOff()) notes.removeValue (msg.getNoteNumber()); } midi.clear(); if ((time + numSamples) >= noteDuration) { auto offset = jmax (0, jmin ((int) (noteDuration - time), numSamples - 1)); if (lastNoteValue > 0) { midi.addEvent (MidiMessage::noteOff (1, lastNoteValue), offset); lastNoteValue = -1; } if (notes.size() > 0) { currentNote = (currentNote + 1) % notes.size(); lastNoteValue = notes[currentNote]; midi.addEvent (MidiMessage::noteOn (1, lastNoteValue, (uint8) 127), offset); } } time = (time + numSamples) % noteDuration; } using AudioProcessor::processBlock; //============================================================================== bool isMidiEffect() const override { return true; } //============================================================================== AudioProcessorEditor* createEditor() override { return new GenericAudioProcessorEditor (*this); } bool hasEditor() const override { return true; } //============================================================================== const String getName() const override { return "Arpeggiator"; } bool acceptsMidi() const override { return true; } bool producesMidi() const override { return true; } double getTailLengthSeconds() const override { return 0; } //============================================================================== int getNumPrograms() override { return 1; } int getCurrentProgram() override { return 0; } void setCurrentProgram (int) override {} const String getProgramName (int) override { return "None"; } void changeProgramName (int, const String&) override {} //============================================================================== void getStateInformation (MemoryBlock& destData) override { MemoryOutputStream (destData, true).writeFloat (*speed); } void setStateInformation (const void* data, int sizeInBytes) override { speed->setValueNotifyingHost (MemoryInputStream (data, static_cast<size_t> (sizeInBytes), false).readFloat()); } private: //============================================================================== AudioParameterFloat* speed; int currentNote, lastNoteValue; int time; float rate; SortedSet<int> notes; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Arpeggiator) };
Would it be possible to make Hise compile like this?
-
@Christoph-Hart do you think it's possible to fix this in Hise?
Or do we need to integrate Juce v.7 for this to work? -
@Christoph-Hart bump...
-
@Christoph-Hart this is a minimal MidiFX example, when any key is pressed a timer will spit out that key every 100ms
NotePnl.setTimerCallback(function() { var ev = Synth.addNoteOn(1, this.data.nn, this.data.vel, 0); Synth.noteOffDelayedByEventId(ev, 1000); });
Still I get all the crazy data from it when using Logic, and if a different track, than the plugin track, is selected, it will not work.
Do you think you have the time to take look and see what is wrong?HiseSnippet 925.3oc4V0saiSDEdljZfDnRrR7.XsW4JEhRfkEjPUTZRJJZosQjREb0po1GGOpimwxdbVrP7.v6FOL7F.mi+Yi8RVZUD6UDoVkyOey7Mm47MmrJ03CYYlTFevMEI.i+QNqKz1nYQBolsbNieryZYbhBbuAxrryKRDYYP.iy6+cTJ7AGwJ+7mey4BkP6C6bwX2Zj9v2Kik1cdWc1KjJ0Eh.3FYbqre1YK8M5YFkIGoSemIrDg+8hMvUBJsdNL96sHPZMoqsBKjw3GctInXcj4U5p7uUlIuSAjwT1ZbgpbegQEPLl7xlEIUAqZN1YLbUVsqHzupH7INWJCju1+thwGWFvcGh10CdutzqeG5MsM8lzhd6gR8ZQoipnzSbV6mJSr6hP74CcVpsPZn.K6soRUtrd+dOmYFLCscbr3d3hTz30H7d9jIibw+cxWObHV5yrtaEotWYrvJsx8T2Fna.6LSbhQiFdOsN9SIT0eebFXoKyzYBk5N7VyKLW6akFs2IC+0gCnkE1hqXYu0XQP.A7Zs2zQt1HY13.gULVqaasETibIpMnBjlPDFNGThBH37hEaQ1rLvC1Nxc5jxCwug+0rwtFc8dTxfKwZF1JMVtQaRgRrd1zbfV+gCZNF0z.IZS93QmBdUd7cPpGkcmbQR1M4aAkwWZK5jZlUjVUe7lVQzgtuIOCCeLDUF55UUNHhkGuJEnq7W.EYdm3d5oXAa3fV6qIoZa+G6Ic0lZTJJ1CrscAVudDl8ujd5xZ0HrWRg3aRDaj6pNbd6pi1hW+pdvVIZzK0R60If9sIoY0Mt329wkyw6ITRwq8g4k.oVIQA9bXK99Tk.afybH6drhUlac2Ni+91xnG2H+DZPwjAT90kY1uz9Euh1FuRFXi143mOKBjahrs8rs4Xy6ydrp9AkuBcctceZd9e339.hfLPGbiodM7db8iO.l2re5+ecM641CmVXBxUBa2QHzby5.XMnycH81rNCe+n8b0+ylq7Xo3SbVIs9Q6mi81CGoQVuC3X8z3icVDFB91cD7HmK9o2MidY+fI2J0atTXSknv1AejcM9CR7Ab20n1m5A48ntkJ6IjMUAViBjRi+B+TGbJYyqCNsIHKV3mZdoeUmOMu+CJ8fbRW9ScP8MY6NkUpFPbNSFOgEihuW56SG+OEaf2OlO6.v74G.lmc.X9hC.yyO.Le4Af4q9WwPu68s4VSbkb.crZQ4SDb9Bs.6rJ6BY+M2dkKcB
-
@ulrik you should use the synth timer. In a non deferred script.
-
@d-healey yeah but this will make no difference to the issue, still the same garbage events and will still not work if another track is selected
-
@ulrik You've tried the synth timer? A panel timer isn't accurate so I would expect problems with that.
-
@d-healey yes, same issue
-
@Christoph-Hart
Is it possible to modify Hise to compile working MidiFX plugins?
I know that you have been very busy the last days and I don't expect you to make this happen right away, but what do you think, will it be on the roadmap for Hise in a near future? -
@Christoph-Hart bump
-
@Christoph-Hart bumpelibump
-
@Christoph-Hart I feel I'm a bit nagging but do you have any plans for making Hise compile decent MidiFX plugins?