Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
MemaMessages.h
Go to the documentation of this file.
1/* Copyright (c) 2024-2025, Christian Ahrens
2 *
3 * This file is part of Mema <https://github.com/ChristianAhrens/Mema>
4 *
5 * This tool is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License version 3.0 as published
7 * by the Free Software Foundation.
8 *
9 * This tool is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this tool; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#pragma once
20
21#include <JuceHeader.h>
22
23#include <CustomLookAndFeel.h>
24
26
27namespace Mema
28{
29
30
31//==============================================================================
32/*
33 * Forward declarations of SerializableMessage-derived specializations
34 */
35class EnvironmentParametersMessage;
36class AnalyzerParametersMessage;
37class ReinitIOCountMessage;
38class AudioInputBufferMessage;
39class AudioOutputBufferMessage;
40class DataTrafficTypeSelectionMessage;
41class ControlParametersMessage;
42class PluginParameterInfosMessage;
43class PluginParameterValueMessage;
44
75class SerializableMessage : public juce::Message
76{
77public:
91
92public:
94 virtual ~SerializableMessage() = default;
95
97 void setId(int id) { m_userId = id; };
99 int getId() const { return m_userId; };
101 bool hasUserId() const { return -1 != m_userId; };
102
104 const SerializableMessageType getType() const { return m_type; };
105
112 juce::MemoryBlock getSerializedMessage() const
113 {
114 size_t contentSize = 0;
115 juce::MemoryBlock blob;
116 blob.append(&m_type, sizeof(SerializableMessageType));
117 auto sc = createSerializedContent(contentSize);
118 blob.append(sc.getData(), contentSize);
119 return blob;
120 };
129 static SerializableMessage* initFromMemoryBlock(const juce::MemoryBlock& blob)
130 {
131 auto minSize = sizeof(SerializableMessageType);
132 jassert(blob.getSize() >= minSize);
133 if (blob.getSize() < minSize)
134 return nullptr;
135
136 auto type = static_cast<SerializableMessageType>(blob[0]);
137 switch (type)
138 {
140 return reinterpret_cast<SerializableMessage*>(std::make_unique<EnvironmentParametersMessage>(blob).release());
142 return reinterpret_cast<SerializableMessage*>(std::make_unique<AnalyzerParametersMessage>(blob).release());
143 case ReinitIOCount:
144 return reinterpret_cast<SerializableMessage*>(std::make_unique<ReinitIOCountMessage>(blob).release());
145 case AudioInputBuffer:
146 return reinterpret_cast<SerializableMessage*>(std::make_unique<AudioInputBufferMessage>(blob).release());
148 return reinterpret_cast<SerializableMessage*>(std::make_unique<AudioOutputBufferMessage>(blob).release());
150 return reinterpret_cast<SerializableMessage*>(std::make_unique<DataTrafficTypeSelectionMessage>(blob).release());
152 return reinterpret_cast<SerializableMessage*>(std::make_unique<ControlParametersMessage>(blob).release());
154 return reinterpret_cast<SerializableMessage*>(std::make_unique<PluginParameterInfosMessage>(blob).release());
156 return reinterpret_cast<SerializableMessage*>(std::make_unique<PluginParameterValueMessage>(blob).release());
157 case None:
158 default:
159 return nullptr;
160 }
161 };
169 {
170 if (nullptr != message)
171 {
172 switch (message->getType())
173 {
175 {
176 auto apm = std::unique_ptr<AnalyzerParametersMessage>(reinterpret_cast<AnalyzerParametersMessage*>(message));
177 }
178 break;
179 case ReinitIOCount:
180 {
181 auto riocm = std::unique_ptr<ReinitIOCountMessage>(reinterpret_cast<ReinitIOCountMessage*>(message));
182 }
183 break;
184 case AudioInputBuffer:
185 {
186 auto aibm = std::unique_ptr<AudioInputBufferMessage>(reinterpret_cast<AudioInputBufferMessage*>(message));
187 }
188 break;
190 {
191 auto aobm = std::unique_ptr<AudioOutputBufferMessage>(reinterpret_cast<AudioOutputBufferMessage*>(message));
192 }
193 break;
195 {
196 auto dtsm = std::unique_ptr<DataTrafficTypeSelectionMessage>(reinterpret_cast<DataTrafficTypeSelectionMessage*>(message));
197 }
198 break;
200 {
201 auto cpm = std::unique_ptr<ControlParametersMessage>(reinterpret_cast<ControlParametersMessage*>(message));
202 }
203 break;
205 {
206 auto ppim = std::unique_ptr<PluginParameterInfosMessage>(reinterpret_cast<PluginParameterInfosMessage*>(message));
207 }
208 break;
210 {
211 auto ppvm = std::unique_ptr<PluginParameterValueMessage>(reinterpret_cast<PluginParameterValueMessage*>(message));
212 }
213 break;
214 case None:
215 default:
216 break;
217 }
218 }
219 };
220
221protected:
222 //==============================================================================
228 virtual juce::MemoryBlock createSerializedContent(size_t& contentSize) const = 0;
229
230 //==============================================================================
232 std::uint32_t ReadUint32(const char* buffer)
233 {
234 return (((static_cast<std::uint8_t>(buffer[0]) << 24) & 0xff000000) +
235 ((static_cast<std::uint8_t>(buffer[1]) << 16) & 0x00ff0000) +
236 ((static_cast<std::uint8_t>(buffer[2]) << 8) & 0x0000ff00) +
237 static_cast<std::uint8_t>(buffer[3]));
238 };
240 std::uint16_t ReadUint16(const char* buffer)
241 {
242 return (((static_cast<std::uint8_t>(buffer[0]) << 8) & 0xff00) +
243 static_cast<std::uint8_t>(buffer[1]));
244 };
245
246 //==============================================================================
248 int m_userId = -1;
249};
250
251//==============================================================================
264{
265public:
267 EnvironmentParametersMessage(JUCEAppBasics::CustomLookAndFeel::PaletteStyle paletteStyle) { m_type = SerializableMessageType::EnvironmentParameters; m_paletteStyle = paletteStyle; };
268 EnvironmentParametersMessage(const juce::MemoryBlock& blob)
269 {
271
273 blob.copyTo(&m_paletteStyle, sizeof(SerializableMessageType), sizeof(JUCEAppBasics::CustomLookAndFeel::PaletteStyle));
274
275 };
277
279 JUCEAppBasics::CustomLookAndFeel::PaletteStyle getPaletteStyle() const { return m_paletteStyle; };
280
281protected:
282 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
283 {
284 juce::MemoryBlock blob;
285 blob.append(&m_paletteStyle, sizeof(JUCEAppBasics::CustomLookAndFeel::PaletteStyle));
286 contentSize = blob.getSize();
287 return blob;
288 };
289
290private:
291 JUCEAppBasics::CustomLookAndFeel::PaletteStyle m_paletteStyle = JUCEAppBasics::CustomLookAndFeel::PS_Dark;
292};
293
294//==============================================================================
307{
308public:
310 AnalyzerParametersMessage(int sampleRate, int maximumExpectedSamplesPerBlock) { m_type = SerializableMessageType::AnalyzerParameters; m_sampleRate = std::uint16_t(sampleRate); m_maximumExpectedSamplesPerBlock = std::uint16_t(maximumExpectedSamplesPerBlock); };
311 AnalyzerParametersMessage(const juce::MemoryBlock& blob)
312 {
314
316 blob.copyTo(&m_sampleRate, sizeof(SerializableMessageType), sizeof(std::uint16_t));
317 blob.copyTo(&m_maximumExpectedSamplesPerBlock, sizeof(SerializableMessageType) + sizeof(std::uint16_t), sizeof(std::uint16_t));
318
319 };
321
323 int getSampleRate() const { return m_sampleRate; };
325 int getMaximumExpectedSamplesPerBlock() const { return m_maximumExpectedSamplesPerBlock; };
326
327protected:
328 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
329 {
330 juce::MemoryBlock blob;
331 blob.append(&m_sampleRate, sizeof(std::uint16_t));
332 blob.append(&m_maximumExpectedSamplesPerBlock, sizeof(std::uint16_t));
333 contentSize = blob.getSize();
334 return blob;
335 };
336
337private:
338 std::uint16_t m_sampleRate = 0;
339 std::uint16_t m_maximumExpectedSamplesPerBlock = 0;
340};
341
342//==============================================================================
355{
356public:
358 ReinitIOCountMessage(int inputs, int outputs) { m_type = SerializableMessageType::ReinitIOCount; m_inputCount = std::uint16_t(inputs); m_outputCount = std::uint16_t(outputs); };
359 ReinitIOCountMessage(const juce::MemoryBlock& blob)
360 {
361 jassert(SerializableMessageType::ReinitIOCount == static_cast<SerializableMessageType>(blob[0]));
362
364 blob.copyTo(&m_inputCount, sizeof(SerializableMessageType), sizeof(std::uint16_t));
365 blob.copyTo(&m_outputCount, sizeof(SerializableMessageType) + sizeof(std::uint16_t), sizeof(std::uint16_t));
366
367 };
369
371 std::uint16_t getInputCount() const { return m_inputCount; };
373 std::uint16_t getOutputCount() const { return m_outputCount; };
374
375protected:
376 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
377 {
378 juce::MemoryBlock blob;
379 blob.append(&m_inputCount, sizeof(std::uint16_t));
380 blob.append(&m_outputCount, sizeof(std::uint16_t));
381 contentSize = blob.getSize();
382 return blob;
383 };
384
385private:
386 std::uint16_t m_inputCount = 0;
387 std::uint16_t m_outputCount = 0;
388};
389
390//==============================================================================
408{
409public:
417
418public:
420 AudioBufferMessage(juce::AudioBuffer<float>& buffer) { m_buffer = buffer; };
422
424 const juce::AudioBuffer<float>& getAudioBuffer() const { return m_buffer; };
426 const FlowDirection getFlowDirection() const { return m_direction; };
427
428protected:
429 juce::MemoryBlock createSerializedContent(size_t& contentSize) const {
430 auto numChannels = std::uint16_t(m_buffer.getNumChannels());
431 auto numSamples = std::uint16_t(m_buffer.getNumSamples());
432 juce::MemoryBlock blob;
433 blob.append(&m_direction, sizeof(FlowDirection));
434 blob.append(&numChannels, sizeof(std::uint16_t));
435 blob.append(&numSamples, sizeof(std::uint16_t));
436 for (int channelNumber = 0; channelNumber < numChannels; channelNumber++)
437 blob.append(m_buffer.getReadPointer(channelNumber), sizeof(float) * numSamples);
438 contentSize = blob.getSize();
439
440 return blob;
441 };
442
444 juce::AudioBuffer<float> m_buffer;
445
446};
447
458{
459public:
462 AudioInputBufferMessage(const juce::MemoryBlock& blob)
463 {
465
467
468 auto readPos = int(sizeof(SerializableMessageType));
469
470 blob.copyTo(&m_direction, readPos, sizeof(FlowDirection));
472
473 readPos += sizeof(FlowDirection);
474 auto numChannels = std::uint16_t(0);
475 blob.copyTo(&numChannels, readPos, sizeof(std::uint16_t));
476 readPos += sizeof(std::uint16_t);
477 auto numSamples = std::uint16_t(0);
478 blob.copyTo(&numSamples, readPos, sizeof(std::uint16_t));
479 readPos += sizeof(std::uint16_t);
480 auto data = reinterpret_cast<const float*>(blob.begin() + readPos);
481
482 m_buffer = juce::AudioBuffer<float>(numChannels, numSamples);
483 auto samplePos = 0;
484 for (int i = 0; i < numChannels; i++)
485 {
486 m_buffer.copyFrom(i, 0, data + samplePos, numSamples);
487 samplePos += numSamples;
488 }
489 };
491};
492
503{
504public:
507 AudioOutputBufferMessage(const juce::MemoryBlock& blob)
508 {
510
511 auto readPos = int(sizeof(SerializableMessageType));
512
513 blob.copyTo(&m_direction, readPos, sizeof(FlowDirection));
515
516 readPos += sizeof(FlowDirection);
517 auto numChannels = std::uint16_t(0);
518 blob.copyTo(&numChannels, readPos, sizeof(std::uint16_t));
519 readPos += sizeof(std::uint16_t);
520 auto numSamples = std::uint16_t(0);
521 blob.copyTo(&numSamples, readPos, sizeof(std::uint16_t));
522 readPos += sizeof(std::uint16_t);
523 auto data = reinterpret_cast<const float*>(blob.begin() + readPos);
524
525 m_buffer = juce::AudioBuffer<float>(numChannels, numSamples);
526 auto samplePos = 0;
527 for (int i = 0; i < numChannels; i++)
528 {
529 m_buffer.copyFrom(i, 0, data + samplePos, numSamples);
530 samplePos += numSamples;
531 }
532 };
534};
535
536//==============================================================================
553{
554public:
556 DataTrafficTypeSelectionMessage(const std::vector<SerializableMessageType>& trafficTypes) { m_type = SerializableMessageType::DataTrafficTypeSelection; m_trafficTypes = trafficTypes; };
557 DataTrafficTypeSelectionMessage(const juce::MemoryBlock& blob)
558 {
560
562
563 auto readPos = int(sizeof(SerializableMessageType));
564
565 std::uint16_t typesCount;
566 blob.copyTo(&typesCount, readPos, sizeof(std::uint16_t));
567 readPos += sizeof(std::uint16_t);
568 m_trafficTypes.resize(typesCount);
569 for (int i = 0; i < typesCount; i++)
570 {
571 blob.copyTo(&m_trafficTypes[i], readPos, sizeof(SerializableMessageType));
572 readPos += sizeof(SerializableMessageType);
573 }
574
575 };
577
579 const std::vector<SerializableMessageType>& getTrafficTypes() const { return m_trafficTypes; };
580
581protected:
582 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
583 {
584 juce::MemoryBlock blob;
585 auto typesCount = std::uint16_t(m_trafficTypes.size());
586 blob.append(&typesCount, sizeof(std::uint16_t));
587 for (auto& trafficType : m_trafficTypes)
588 blob.append(&trafficType, sizeof(SerializableMessageType));
589 contentSize = blob.getSize();
590 return blob;
591 };
592
593private:
594 std::vector<SerializableMessageType> m_trafficTypes;
595};
596
627{
628public:
630 ControlParametersMessage(const std::map<std::uint16_t, bool>& inputMuteStates, const std::map<std::uint16_t, bool>& outputMuteStates,
631 const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& crosspointStates, const std::map<std::uint16_t, std::map<std::uint16_t, float>>& crosspointValues)
632 {
633#ifdef DEBUG
634 // sanity check symmetry of crosspoint states
635 auto crosspointStateOutCount = size_t(0);
636 if (0 != crosspointStates.size())
637 {
638 crosspointStateOutCount = crosspointStates.begin()->second.size();
639 for (auto const& cpStatKV : crosspointStates)
640 {
641 jassert(crosspointStateOutCount == cpStatKV.second.size());
642 }
643 }
644
645 // sanity check symmetry of crosspoint values
646 auto crosspointValOutCount = size_t(0);
647 if (0 != crosspointValues.size())
648 {
649 crosspointValOutCount = crosspointValues.begin()->second.size();
650 for (auto const& cpValKV : crosspointValues)
651 {
652 jassert(crosspointValOutCount == cpValKV.second.size());
653 }
654 }
655#endif
656
658 m_inputMuteStates = inputMuteStates;
659 m_outputMuteStates = outputMuteStates;
660 m_crosspointStates = crosspointStates;
661 m_crosspointValues = crosspointValues;
662 };
663 ControlParametersMessage(const juce::MemoryBlock& blob)
664 {
666
668
669 auto readPos = int(sizeof(SerializableMessageType));
670
671 std::uint16_t inputMuteStatesCount;
672 blob.copyTo(&inputMuteStatesCount, readPos, sizeof(std::uint16_t));
673 readPos += sizeof(inputMuteStatesCount);
674 for (int i = 0; i < inputMuteStatesCount; i++)
675 {
676 std::pair<std::uint16_t, bool> inputMuteState;
677 blob.copyTo(&inputMuteState.first, readPos, sizeof(inputMuteState.first));
678 readPos += sizeof(inputMuteState.first);
679 blob.copyTo(&inputMuteState.second, readPos, sizeof(inputMuteState.second));
680 readPos += sizeof(inputMuteState.second);
681
682 m_inputMuteStates[inputMuteState.first] = inputMuteState.second;
683 }
684
685 std::uint16_t outputMuteStatesCount;
686 blob.copyTo(&outputMuteStatesCount, readPos, sizeof(std::uint16_t));
687 readPos += sizeof(outputMuteStatesCount);
688 for (int i = 0; i < outputMuteStatesCount; i++)
689 {
690 std::pair<std::uint16_t, bool> outputMuteState;
691 blob.copyTo(&outputMuteState.first, readPos, sizeof(outputMuteState.first));
692 readPos += sizeof(outputMuteState.first);
693 blob.copyTo(&outputMuteState.second, readPos, sizeof(outputMuteState.second));
694 readPos += sizeof(outputMuteState.second);
695
696 m_outputMuteStates[outputMuteState.first] = outputMuteState.second;
697 }
698
699 std::uint16_t crosspointStatesCount;
700 blob.copyTo(&crosspointStatesCount, readPos, sizeof(std::uint16_t));
701 readPos += sizeof(crosspointStatesCount);
702 for (int i = 0; i < crosspointStatesCount; i++)
703 {
704 std::uint16_t in, out;
705 bool state;
706 blob.copyTo(&in, readPos, sizeof(in));
707 readPos += sizeof(in);
708 blob.copyTo(&out, readPos, sizeof(out));
709 readPos += sizeof(out);
710 blob.copyTo(&state, readPos, sizeof(state));
711 readPos += sizeof(state);
712
713 m_crosspointStates[in][out] = state;
714 }
715
716 std::uint16_t crosspointValuesCount;
717 blob.copyTo(&crosspointValuesCount, readPos, sizeof(std::uint16_t));
718 readPos += sizeof(crosspointValuesCount);
719 for (int i = 0; i < crosspointValuesCount; i++)
720 {
721 std::uint16_t in, out;
722 float value;
723 blob.copyTo(&in, readPos, sizeof(in));
724 readPos += sizeof(in);
725 blob.copyTo(&out, readPos, sizeof(out));
726 readPos += sizeof(out);
727 blob.copyTo(&value, readPos, sizeof(value));
728 readPos += sizeof(value);
729
730 m_crosspointValues[in][out] = value;
731 }
732 };
734
736 const std::map<std::uint16_t, bool>& getInputMuteStates() const { return m_inputMuteStates; };
738 const std::map<std::uint16_t, bool>& getOutputMuteStates() const { return m_outputMuteStates; };
740 const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& getCrosspointStates() const { return m_crosspointStates; };
742 const std::map<std::uint16_t, std::map<std::uint16_t, float>>& getCrosspointValues() const { return m_crosspointValues; };
743
744protected:
745 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
746 {
747 juce::MemoryBlock blob;
748
749 auto inputMuteStatesCount = std::uint16_t(m_inputMuteStates.size());
750 blob.append(&inputMuteStatesCount, sizeof(inputMuteStatesCount));
751 for (auto& inputMuteStateKV : m_inputMuteStates)
752 {
753 blob.append(&inputMuteStateKV.first, sizeof(inputMuteStateKV.first));
754 blob.append(&inputMuteStateKV.second, sizeof(inputMuteStateKV.second));
755 }
756
757 auto outputMuteStatesCount = std::uint16_t(m_outputMuteStates.size());
758 blob.append(&outputMuteStatesCount, sizeof(outputMuteStatesCount));
759 for (auto& outputMuteStateKV : m_outputMuteStates)
760 {
761 blob.append(&outputMuteStateKV.first, sizeof(outputMuteStateKV.first));
762 blob.append(&outputMuteStateKV.second, sizeof(outputMuteStateKV.second));
763 }
764
765 auto crosspointStatesCount = std::uint16_t(0);
766 if (0 < m_crosspointStates.size())
767 crosspointStatesCount = std::uint16_t(m_crosspointStates.size() * m_crosspointStates.begin()->second.size());
768 blob.append(&crosspointStatesCount, sizeof(crosspointStatesCount));
769 auto crosspointStatesCountRef = std::uint16_t(0);
770 for (auto& crosspointStatesFirstDKV : m_crosspointStates)
771 {
772 for (auto& crosspointStatesSecDKV : crosspointStatesFirstDKV.second)
773 {
774 auto& in = crosspointStatesFirstDKV.first;
775 blob.append(&in, sizeof(in));
776 auto& out = crosspointStatesSecDKV.first;
777 blob.append(&out, sizeof(out));
778 auto& state = crosspointStatesSecDKV.second;
779 blob.append(&state, sizeof(state));
780 crosspointStatesCountRef++;
781 }
782 }
783 jassert(crosspointStatesCount == crosspointStatesCountRef);
784
785 auto crosspointValuesCount = std::uint16_t(0);
786 if (0 < m_crosspointValues.size())
787 crosspointValuesCount = std::uint16_t(m_crosspointValues.size() * m_crosspointValues.begin()->second.size());
788 blob.append(&crosspointValuesCount, sizeof(crosspointValuesCount));
789 auto crosspointValuesCountRef = std::uint16_t(0);
790 for (auto& crosspointValuesFirstDKV : m_crosspointValues)
791 {
792 for (auto& crosspointValuesSecDKV : crosspointValuesFirstDKV.second)
793 {
794 auto& in = crosspointValuesFirstDKV.first;
795 blob.append(&in, sizeof(in));
796 auto& out = crosspointValuesSecDKV.first;
797 blob.append(&out, sizeof(out));
798 auto& value = crosspointValuesSecDKV.second;
799 blob.append(&value, sizeof(value));
800 crosspointValuesCountRef++;
801 }
802 }
803 jassert(crosspointValuesCount == crosspointValuesCountRef);
804
805 contentSize = blob.getSize();
806 return blob;
807 };
808
809private:
810 std::map<std::uint16_t, bool> m_inputMuteStates;
811 std::map<std::uint16_t, bool> m_outputMuteStates;
812 std::map<std::uint16_t, std::map<std::uint16_t, bool>> m_crosspointStates;
813 std::map<std::uint16_t, std::map<std::uint16_t, float>> m_crosspointValues;
814};
815
836{
837public:
839 PluginParameterInfosMessage(const std::string& pluginName, const std::vector<PluginParameterInfo>& parameterInfos)
840 {
842 m_parameterInfos = parameterInfos;
843 m_pluginName = pluginName;
844 }
845
846 PluginParameterInfosMessage(const juce::MemoryBlock& blob)
847 {
849
851
852 auto readPos = int(sizeof(SerializableMessageType));
853
854 // Read plugin string length and string
855 std::uint16_t pluginNameLength;
856 blob.copyTo(&pluginNameLength, readPos, sizeof(std::uint16_t));
857 readPos += sizeof(std::uint16_t);
858 m_pluginName = juce::String(juce::CharPointer_UTF8(static_cast<const char*>(blob.begin()) + readPos), pluginNameLength);
859 readPos += pluginNameLength;
860
861 std::uint16_t paramCount;
862 blob.copyTo(&paramCount, readPos, sizeof(std::uint16_t));
863 readPos += sizeof(std::uint16_t);
864
865 m_parameterInfos.reserve(paramCount);
866
867 for (int i = 0; i < paramCount; i++)
868 {
870
871 // Read index
872 std::int32_t index;
873 blob.copyTo(&index, readPos, sizeof(std::int32_t));
874 info.index = index;
875 readPos += sizeof(std::int32_t);
876
877 // Read id string length and string
878 std::uint16_t idLength;
879 blob.copyTo(&idLength, readPos, sizeof(std::uint16_t));
880 readPos += sizeof(std::uint16_t);
881 info.id = juce::String(juce::CharPointer_UTF8(static_cast<const char*>(blob.begin()) + readPos), idLength);
882 readPos += idLength;
883
884 // Read name string length and string
885 std::uint16_t nameLength;
886 blob.copyTo(&nameLength, readPos, sizeof(std::uint16_t));
887 readPos += sizeof(std::uint16_t);
888 info.name = juce::String(juce::CharPointer_UTF8(static_cast<const char*>(blob.begin()) + readPos), nameLength);
889 readPos += nameLength;
890
891 // Read float values
892 blob.copyTo(&info.defaultValue, readPos, sizeof(float));
893 readPos += sizeof(float);
894 blob.copyTo(&info.currentValue, readPos, sizeof(float));
895 readPos += sizeof(float);
896
897 // Read label string length and string
898 std::uint16_t labelLength;
899 blob.copyTo(&labelLength, readPos, sizeof(std::uint16_t));
900 readPos += sizeof(std::uint16_t);
901 info.label = juce::String(juce::CharPointer_UTF8(static_cast<const char*>(blob.begin()) + readPos), labelLength);
902 readPos += labelLength;
903
904 // Read bool values
905 blob.copyTo(&info.isAutomatable, readPos, sizeof(bool));
906 readPos += sizeof(bool);
907 blob.copyTo(&info.isRemoteControllable, readPos, sizeof(bool));
908 readPos += sizeof(bool);
909
910 // Read category as int
911 std::int32_t categoryInt;
912 blob.copyTo(&categoryInt, readPos, sizeof(std::int32_t));
913 info.category = static_cast<juce::AudioProcessorParameter::Category>(categoryInt);
914 readPos += sizeof(std::int32_t);
915
916 // Read range values
917 blob.copyTo(&info.minValue, readPos, sizeof(float));
918 readPos += sizeof(float);
919 blob.copyTo(&info.maxValue, readPos, sizeof(float));
920 readPos += sizeof(float);
921 blob.copyTo(&info.stepSize, readPos, sizeof(float));
922 readPos += sizeof(float);
923 blob.copyTo(&info.isDiscrete, readPos, sizeof(bool));
924 readPos += sizeof(bool);
925
926 // Read type
927 blob.copyTo(&info.type, readPos, sizeof(ParameterControlType));
928 readPos += sizeof(ParameterControlType);
929
930 // Read stepCount
931 std::int32_t stepCount;
932 blob.copyTo(&stepCount, readPos, sizeof(std::int32_t));
933 info.stepCount = stepCount;
934 readPos += sizeof(std::int32_t);
935
936 // Read stepNames (count already known from stepCount)
937 for (int s = 0; s < stepCount; s++)
938 {
939 std::uint16_t stepNameLength;
940 blob.copyTo(&stepNameLength, readPos, sizeof(std::uint16_t));
941 readPos += sizeof(std::uint16_t);
942 auto stepName = juce::String(juce::CharPointer_UTF8(
943 static_cast<const char*>(blob.begin()) + readPos), stepNameLength);
944 readPos += stepNameLength;
945 info.stepNames.push_back(stepName.toStdString());
946 }
947
948 m_parameterInfos.push_back(info);
949 }
950 }
951
953
955 const juce::String& getPluginName() const { return m_pluginName; }
957 const std::vector<PluginParameterInfo>& getParameterInfos() const { return m_parameterInfos; }
958
959protected:
960 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
961 {
962 juce::MemoryBlock blob;
963
964 // Write name string (length + UTF8 bytes)
965 auto pluginNameUtf8 = m_pluginName.toUTF8();
966 std::uint16_t pluginNameLength = std::uint16_t(strlen(pluginNameUtf8));
967 blob.append(&pluginNameLength, sizeof(std::uint16_t));
968 blob.append(pluginNameUtf8, pluginNameLength);
969
970 auto paramCount = std::uint16_t(m_parameterInfos.size());
971 blob.append(&paramCount, sizeof(std::uint16_t));
972
973 for (const auto& info : m_parameterInfos)
974 {
975 // Write index
976 std::int32_t index = info.index;
977 blob.append(&index, sizeof(std::int32_t));
978
979 // Write id string (length + UTF8 bytes)
980 auto idUtf8 = info.id.toUTF8();
981 std::uint16_t idLength = std::uint16_t(strlen(idUtf8));
982 blob.append(&idLength, sizeof(std::uint16_t));
983 blob.append(idUtf8, idLength);
984
985 // Write name string (length + UTF8 bytes)
986 auto nameUtf8 = info.name.toUTF8();
987 std::uint16_t nameLength = std::uint16_t(strlen(nameUtf8));
988 blob.append(&nameLength, sizeof(std::uint16_t));
989 blob.append(nameUtf8, nameLength);
990
991 // Write float values
992 blob.append(&info.defaultValue, sizeof(float));
993 blob.append(&info.currentValue, sizeof(float));
994
995 // Write label string (length + UTF8 bytes)
996 auto labelUtf8 = info.label.toUTF8();
997 std::uint16_t labelLength = std::uint16_t(strlen(labelUtf8));
998 blob.append(&labelLength, sizeof(std::uint16_t));
999 blob.append(labelUtf8, labelLength);
1000
1001 // Write bool values
1002 blob.append(&info.isAutomatable, sizeof(bool));
1003 blob.append(&info.isRemoteControllable, sizeof(bool));
1004
1005 // Write category as int
1006 std::int32_t categoryInt = static_cast<std::int32_t>(info.category);
1007 blob.append(&categoryInt, sizeof(std::int32_t));
1008
1009 // Write range values
1010 blob.append(&info.minValue, sizeof(float));
1011 blob.append(&info.maxValue, sizeof(float));
1012 blob.append(&info.stepSize, sizeof(float));
1013 blob.append(&info.isDiscrete, sizeof(bool));
1014
1015 // Write type
1016 blob.append(&info.type, sizeof(ParameterControlType));
1017
1018 // Write stepCount
1019 std::int32_t stepCount = info.stepCount;
1020 blob.append(&stepCount, sizeof(std::int32_t));
1021
1022 // Write stepNames
1023 for (const auto& stepName : info.stepNames)
1024 {
1025 juce::String juceStepName(stepName);
1026 auto stepNameUtf8 = juceStepName.toUTF8();
1027 std::uint16_t stepNameLength = std::uint16_t(strlen(stepNameUtf8));
1028 blob.append(&stepNameLength, sizeof(std::uint16_t));
1029 blob.append(stepNameUtf8, stepNameLength);
1030 }
1031 }
1032
1033 contentSize = blob.getSize();
1034 return blob;
1035 }
1036
1037private:
1038 std::vector<PluginParameterInfo> m_parameterInfos;
1039 juce::String m_pluginName;
1040};
1041
1058{
1059public:
1061 PluginParameterValueMessage(std::uint16_t parameterIndex, const juce::String& parameterId, float value)
1062 {
1064 m_parameterIndex = parameterIndex;
1065 m_parameterId = parameterId;
1066 m_currentValue = value;
1067 }
1068
1069 PluginParameterValueMessage(const juce::MemoryBlock& blob)
1070 {
1072
1074
1075 auto readPos = int(sizeof(SerializableMessageType));
1076
1077 // Read index
1078 blob.copyTo(&m_parameterIndex, readPos, sizeof(std::uint16_t));
1079 readPos += sizeof(std::uint16_t);
1080
1081 // Read id string length and string
1082 std::uint16_t idLength;
1083 blob.copyTo(&idLength, readPos, sizeof(std::uint16_t));
1084 readPos += sizeof(std::uint16_t);
1085 m_parameterId = juce::String(juce::CharPointer_UTF8(static_cast<const char*>(blob.begin()) + readPos), idLength);
1086 readPos += idLength;
1087
1088 // Read current value
1089 blob.copyTo(&m_currentValue, readPos, sizeof(float));
1090 readPos += sizeof(float);
1091 }
1092
1094
1096 std::uint16_t getParameterIndex() const { return m_parameterIndex; }
1098 const juce::String& getParameterId() const { return m_parameterId; }
1100 float getCurrentValue() const { return m_currentValue; }
1101
1102protected:
1103 juce::MemoryBlock createSerializedContent(size_t& contentSize) const override
1104 {
1105 juce::MemoryBlock blob;
1106
1107 // Write index
1108 blob.append(&m_parameterIndex, sizeof(std::uint16_t));
1109
1110 // Write id string (length + UTF8 bytes)
1111 auto idUtf8 = m_parameterId.toUTF8();
1112 std::uint16_t idLength = std::uint16_t(strlen(idUtf8));
1113 blob.append(&idLength, sizeof(std::uint16_t));
1114 blob.append(idUtf8, idLength);
1115
1116 // Write current value
1117 blob.append(&m_currentValue, sizeof(float));
1118
1119 contentSize = blob.getSize();
1120 return blob;
1121 }
1122
1123private:
1124 std::uint16_t m_parameterIndex = 0;
1125 juce::String m_parameterId;
1126 float m_currentValue = 0.0f;
1127};
1128
1129
1130#ifdef NIX // DEBUG
1131#define RUN_MESSAGE_TESTS
1132#endif
1133#ifdef RUN_MESSAGE_TESTS
1134static void runTests()
1135{
1136 auto inputs = 11;
1137 auto outputs = 12;
1138 auto buffer = juce::AudioBuffer<float>();
1139 auto refSample = 11.11f;
1140 auto sr = 48000;
1141 auto mespb = 256;
1142
1143 // test AnalyzerParametersMessage
1144 auto apm = std::make_unique<AnalyzerParametersMessage>(sr, mespb);
1145 auto apmb = apm->getSerializedMessage();
1146 auto apmcpy = AnalyzerParametersMessage(apmb);
1147 auto test5 = apmcpy.getSampleRate();
1148 auto test6 = apmcpy.getMaximumExpectedSamplesPerBlock();
1149 jassert(test5 == sr);
1150 jassert(test6 == mespb);
1151
1152 // test ReinitIOCountMessage
1153 auto rcm = std::make_unique<ReinitIOCountMessage>(inputs, outputs);
1154 auto rcmb = rcm->getSerializedMessage();
1155 auto rcmcpy = ReinitIOCountMessage(rcmb);
1156 auto test7 = rcmcpy.getInputCount();
1157 auto test8 = rcmcpy.getOutputCount();
1158 jassert(test7 == inputs);
1159 jassert(test8 == outputs);
1160
1161 // test AudioInputBufferMessage
1162 auto channelCount = 2;
1163 auto sampleCount = 6;
1164 buffer.setSize(channelCount, sampleCount, false, true, false);
1165 for (int i = 0; i < channelCount; i++)
1166 {
1167 for (int j = 0; j < sampleCount; j++)
1168 {
1169 buffer.setSample(i, j, ++refSample);
1170 }
1171 }
1172 auto rrefSample1 = refSample;
1173 auto aibm1 = std::make_unique<AudioInputBufferMessage>(buffer);
1174 for (int i = channelCount - 1; i >= 0; i--)
1175 {
1176 for (int j = sampleCount - 1; j >= 0; j--)
1177 {
1178 auto test1 = aibm1->getAudioBuffer().getSample(i, j);
1179 jassert(int(test1) == int(refSample));
1180 refSample--;
1181 }
1182 }
1183 auto aibmb1 = aibm1->getSerializedMessage();
1184 auto aibmcpy1 = AudioInputBufferMessage(aibmb1);
1185 for (int i = channelCount - 1; i >= 0; i--)
1186 {
1187 for (int j = sampleCount - 1; j >= 0; j--)
1188 {
1189 auto test1 = aibmcpy1.getAudioBuffer().getSample(i, j);
1190 jassert(int(test1) == int(rrefSample1));
1191 rrefSample1--;
1192 }
1193 }
1194
1195 // test AudioOutputBufferMessage
1196 buffer.setSize(channelCount, sampleCount, false, true, false);
1197 for (int i = 0; i < channelCount; i++)
1198 {
1199 for (int j = 0; j < sampleCount; j++)
1200 {
1201 buffer.setSample(i, j, ++refSample);
1202 }
1203 }
1204 auto rrefSample2 = refSample;
1205 auto aibm2 = std::make_unique<AudioOutputBufferMessage>(buffer);
1206 for (int i = channelCount - 1; i >= 0; i--)
1207 {
1208 for (int j = sampleCount - 1; j >= 0; j--)
1209 {
1210 auto test2 = aibm2->getAudioBuffer().getSample(i, j);
1211 jassert(int(test2) == int(rrefSample2));
1212 rrefSample2--;
1213 }
1214 }
1215 auto aibmb2 = aibm2->getSerializedMessage();
1216 auto aibmcpy2 = AudioOutputBufferMessage(aibmb2);
1217 for (int i = channelCount - 1; i >= 0; i--)
1218 {
1219 for (int j = sampleCount - 1; j >= 0; j--)
1220 {
1221 auto test2 = aibmcpy2.getAudioBuffer().getSample(i, j);
1222 jassert(int(test2) == int(refSample));
1223 refSample--;
1224 }
1225 }
1226
1227 // test EnvironmentParametersMessage
1228 auto paletteStyle = JUCEAppBasics::CustomLookAndFeel::PaletteStyle::PS_Light;
1229 auto epm = std::make_unique<EnvironmentParametersMessage>(paletteStyle);
1230 auto epmb = epm->getSerializedMessage();
1231 auto epmcpy = EnvironmentParametersMessage(epmb);
1232 auto test9 = epmcpy.getPaletteStyle();
1233 jassert(test9 == paletteStyle);
1234
1235 // test DataTrafficTypeSelectionMessage
1236 auto trafficTypes = std::vector<SerializableMessage::SerializableMessageType>({ SerializableMessage::ControlParameters, SerializableMessage::AnalyzerParameters });
1237 auto dttm = std::make_unique<DataTrafficTypeSelectionMessage>(trafficTypes);
1238 auto dttmb = dttm->getSerializedMessage();
1239 auto dttmcpy = DataTrafficTypeSelectionMessage(dttmb);
1240 auto test10 = dttmcpy.getTrafficTypes();
1241 jassert(test10 == trafficTypes);
1242
1243 // test ControlParametersMessage
1244 auto inputMuteStates = std::map<std::uint16_t, bool>{ { std::uint16_t(1), true}, { std::uint16_t(2), false}, { std::uint16_t(3), true} };
1245 auto outputMuteStates = std::map<std::uint16_t, bool>{ { std::uint16_t(4), false}, { std::uint16_t(5), true}, { std::uint16_t(6), false} };
1246 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
1247 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
1248 crosspointStates[1][1] = false;
1249 crosspointStates[1][2] = true;
1250 crosspointStates[2][1] = true;
1251 crosspointStates[2][2] = true;
1252 crosspointValues[1][1] = 0.0f;
1253 crosspointValues[1][2] = 1.0f;
1254 crosspointValues[2][1] = 0.5f;
1255 crosspointValues[2][2] = 0.7f;
1256 auto cpm = std::make_unique<ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues);
1257 auto cpmb = cpm->getSerializedMessage();
1258 auto cpmcpy = ControlParametersMessage(cpmb);
1259 auto test11 = cpmcpy.getInputMuteStates();
1260 auto test12 = cpmcpy.getOutputMuteStates();
1261 auto test13 = cpmcpy.getCrosspointStates();
1262 auto test14 = cpmcpy.getCrosspointValues();
1263 jassert(test11 == inputMuteStates);
1264 jassert(test12 == outputMuteStates);
1265 jassert(test13 == crosspointStates);
1266 jassert(test14 == crosspointValues);
1267}
1268#endif
1269
1270
1271};
Carries audio-device parameters (sample rate, block size) from Mema to clients.
int getSampleRate() const
Returns the audio device sample rate in Hz.
int getMaximumExpectedSamplesPerBlock() const
Returns the maximum number of samples per processing block.
AnalyzerParametersMessage(int sampleRate, int maximumExpectedSamplesPerBlock)
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
AnalyzerParametersMessage(const juce::MemoryBlock &blob)
Base message carrying a serialised audio buffer and its flow-direction metadata.
juce::MemoryBlock createSerializedContent(size_t &contentSize) const
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
FlowDirection
Identifies whether the buffer contains pre-matrix input or post-matrix output samples.
@ Invalid
Uninitialised direction.
@ Input
Pre-matrix input samples (as seen by the input analyzers).
@ Output
Post-matrix output samples (as seen by the output analyzers).
FlowDirection m_direction
Input or output flow direction.
const juce::AudioBuffer< float > & getAudioBuffer() const
Returns a const reference to the decoded audio buffer.
const FlowDirection getFlowDirection() const
Returns the flow direction encoded in the message.
juce::AudioBuffer< float > m_buffer
Decoded float audio buffer.
AudioBufferMessage(juce::AudioBuffer< float > &buffer)
Carries a pre-matrix input audio buffer streamed continuously from Mema to subscribed clients.
AudioInputBufferMessage(const juce::MemoryBlock &blob)
AudioInputBufferMessage(juce::AudioBuffer< float > &buffer)
Carries a post-matrix output audio buffer streamed continuously from Mema to subscribed clients.
AudioOutputBufferMessage(const juce::MemoryBlock &blob)
AudioOutputBufferMessage(juce::AudioBuffer< float > &buffer)
Full routing-matrix state snapshot exchanged bidirectionally between Mema and Mema....
const std::map< std::uint16_t, std::map< std::uint16_t, bool > > & getCrosspointStates() const
Returns the crosspoint enable matrix (input index → output index → enabled).
const std::map< std::uint16_t, bool > & getOutputMuteStates() const
Returns the per-output mute states (channel index → muted).
ControlParametersMessage(const juce::MemoryBlock &blob)
const std::map< std::uint16_t, std::map< std::uint16_t, float > > & getCrosspointValues() const
Returns the crosspoint gain matrix (input index → output index → linear gain [0, 1]).
ControlParametersMessage(const std::map< std::uint16_t, bool > &inputMuteStates, const std::map< std::uint16_t, bool > &outputMuteStates, const std::map< std::uint16_t, std::map< std::uint16_t, bool > > &crosspointStates, const std::map< std::uint16_t, std::map< std::uint16_t, float > > &crosspointValues)
const std::map< std::uint16_t, bool > & getInputMuteStates() const
Returns the per-input mute states (channel index → muted).
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
Sent by a client to opt in to receiving specific message types from Mema.
DataTrafficTypeSelectionMessage(const juce::MemoryBlock &blob)
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
DataTrafficTypeSelectionMessage(const std::vector< SerializableMessageType > &trafficTypes)
const std::vector< SerializableMessageType > & getTrafficTypes() const
Returns the list of message types this client wants to receive.
Carries the active look-and-feel palette style from Mema to connected clients.
EnvironmentParametersMessage(JUCEAppBasics::CustomLookAndFeel::PaletteStyle paletteStyle)
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
EnvironmentParametersMessage(const juce::MemoryBlock &blob)
JUCEAppBasics::CustomLookAndFeel::PaletteStyle getPaletteStyle() const
Returns the palette style carried by this message.
Carries the plugin name and complete parameter descriptor list from Mema to Mema.Re clients.
const std::vector< PluginParameterInfo > & getParameterInfos() const
Returns the ordered list of parameter descriptors for the loaded plugin.
PluginParameterInfosMessage(const std::string &pluginName, const std::vector< PluginParameterInfo > &parameterInfos)
PluginParameterInfosMessage(const juce::MemoryBlock &blob)
const juce::String & getPluginName() const
Returns the display name of the currently loaded plugin.
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
Carries a single normalised plugin parameter value from Mema.Re to Mema.
float getCurrentValue() const
Returns the normalised parameter value in [0, 1].
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
const juce::String & getParameterId() const
Returns the stable string identifier of the parameter (used for cross-session safety).
PluginParameterValueMessage(std::uint16_t parameterIndex, const juce::String &parameterId, float value)
std::uint16_t getParameterIndex() const
Returns the zero-based parameter index within the plugin's parameter list.
PluginParameterValueMessage(const juce::MemoryBlock &blob)
Instructs clients to tear down and rebuild their UI for a new channel count.
ReinitIOCountMessage(const juce::MemoryBlock &blob)
ReinitIOCountMessage(int inputs, int outputs)
std::uint16_t getInputCount() const
Returns the new number of active input channels.
std::uint16_t getOutputCount() const
Returns the new number of active output channels.
juce::MemoryBlock createSerializedContent(size_t &contentSize) const override
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
Base class for all messages exchanged between Mema, Mema.Mo, and Mema.Re over TCP.
juce::MemoryBlock getSerializedMessage() const
Serialises the message to a MemoryBlock ready to send over the socket.
virtual juce::MemoryBlock createSerializedContent(size_t &contentSize) const =0
Subclass hook — produces the type-specific payload bytes (everything after the type discriminator).
const SerializableMessageType getType() const
Returns the concrete message type discriminator.
std::uint32_t ReadUint32(const char *buffer)
Reads a big-endian uint32 from buffer.
static void freeMessageData(SerializableMessage *message)
Type-correctly destroys a SerializableMessage* returned by initFromMemoryBlock().
void setId(int id)
Tags the message with a connection-id used for echo-suppression on the server.
int m_userId
Optional connection-id tag for echo-suppression (-1 = not set).
int getId() const
Returns the connection-id tag, or -1 if not set.
bool hasUserId() const
Returns true when a non-default connection-id has been assigned.
static SerializableMessage * initFromMemoryBlock(const juce::MemoryBlock &blob)
Deserialises a raw TCP frame into the correct concrete SerializableMessage subclass.
std::uint16_t ReadUint16(const char *buffer)
Reads a big-endian uint16 from buffer.
@ None
Sentinel / uninitialised type.
@ PluginParameterValue
Single parameter value update sent from Mema.Re to Mema.
@ DataTrafficTypeSelection
Sent by a client to opt in/out of specific message types (bandwidth control).
@ AnalyzerParameters
Audio device sample rate and block size; lets clients initialise their local ProcessorDataAnalyzer.
@ ReinitIOCount
New input/output channel count; clients must rebuild their UI accordingly.
@ ControlParameters
Full routing-matrix state snapshot; sent by Mema on connect and echoed by Mema.Re on change.
@ AudioInputBuffer
Raw PCM input buffer streamed from Mema to subscribed clients.
@ AudioOutputBuffer
Raw PCM output buffer streamed from Mema to subscribed clients.
@ EnvironmentParameters
Look-and-feel palette sent by Mema to clients on connect.
@ PluginParameterInfos
Plugin name and full parameter descriptor list; sent by Mema when a plugin is loaded or changed.
virtual ~SerializableMessage()=default
SerializableMessageType m_type
Type discriminator stored in the first 4 bytes of every serialised frame.
Definition Mema.cpp:27
Metadata describing a single plugin parameter exposed for remote control.
std::vector< std::string > stepNames
Display names for each discrete step.
juce::String name
Human-readable parameter name.
bool isRemoteControllable
Whether this parameter is exposed for remote control.
int stepCount
Number of discrete steps (0 if continuous).
bool isAutomatable
Whether the host can automate this parameter.
int index
Zero-based parameter index within the plugin.
ParameterControlType type
Control widget type (slider, combo, toggle).
float defaultValue
Factory default value (normalised 0..1).
juce::String id
Unique string identifier of the parameter.
float stepSize
Step interval for discrete parameters (0 = continuous).
float minValue
Minimum value in the parameter's native range.
float currentValue
Current parameter value (normalised 0..1).
float maxValue
Maximum value in the parameter's native range.
bool isDiscrete
True if the parameter has a finite set of steps.
juce::AudioProcessorParameter::Category category
JUCE parameter category.
juce::String label
Unit label (e.g. "dB", "Hz").