35 jassert(!DeviceController::getInstance()->onRemoteObjectReceived);
41 m_loudspeakersInAreaPaintComponent = std::make_unique<UmsciLoudspeakersPaintComponent>();
42 addAndMakeVisible(m_loudspeakersInAreaPaintComponent.get());
43 m_soundobjectsInAreaPaintComponent = std::make_unique<UmsciSoundobjectsPaintComponent>();
44 addAndMakeVisible(m_soundobjectsInAreaPaintComponent.get());
45 m_soundobjectsInAreaPaintComponent->onSourcePositionChanged = [
this](std::int16_t sourceId, std::array<std::float_t, 3> position) {
46 m_sourcePosition[sourceId] = position;
47 DeviceController::getInstance()->SetObjectValue(
51 NanoOcp1::Variant(position.at(0), position.at(1), position.at(2))
55 m_upmixIndicatorPaintAndControlComponent = std::make_unique<UmsciUpmixIndicatorPaintNControlComponent>();
56 addAndMakeVisible(m_upmixIndicatorPaintAndControlComponent.get());
57 m_upmixIndicatorPaintAndControlComponent->onTransformChanged = [
this]() {
61 m_upmixIndicatorPaintAndControlComponent->onSourcePositionChanged = [
this](std::int16_t sourceId, std::array<std::float_t, 3> position) {
62 m_sourcePosition[sourceId] = position;
63 m_soundobjectsInAreaPaintComponent->setSourcePosition(sourceId, position);
64 DeviceController::getInstance()->SetObjectValue(
68 NanoOcp1::Variant(position.at(0), position.at(1), position.at(2))
75 auto syncViewportZoom = [
this](
float factor, juce::Point<float> panOffset) {
76 m_loudspeakersInAreaPaintComponent->setZoom(factor, panOffset);
77 m_soundobjectsInAreaPaintComponent->setZoom(factor, panOffset);
78 m_upmixIndicatorPaintAndControlComponent->setZoom(factor, panOffset);
80 m_loudspeakersInAreaPaintComponent->onViewportZoomChanged = syncViewportZoom;
81 m_soundobjectsInAreaPaintComponent->onViewportZoomChanged = syncViewportZoom;
82 m_upmixIndicatorPaintAndControlComponent->onViewportZoomChanged = syncViewportZoom;
88 if (m_nativePinchViewHandle)
89 JUCEAppBasics::iOS_utils::unregisterNativePinchOnView(m_nativePinchViewHandle);
95 g.fillAll(getLookAndFeel().findColour(juce::Slider::ColourIds::backgroundColourId));
97 if (!isDatabaseComplete())
99 g.setColour(getLookAndFeel().findColour(juce::TextEditor::ColourIds::textColourId));
100 g.drawFittedText(
"Data not ready... misconfigured IO size?", getLocalBounds().reduced(35), juce::Justification::centred, 2);
107 auto bounds = getLocalBounds();
109 if (m_loudspeakersInAreaPaintComponent && m_loudspeakersInAreaPaintComponent->isVisible())
110 m_loudspeakersInAreaPaintComponent->setBounds(bounds);
111 if (m_soundobjectsInAreaPaintComponent && m_soundobjectsInAreaPaintComponent->isVisible())
112 m_soundobjectsInAreaPaintComponent->setBounds(bounds);
113 if (m_upmixIndicatorPaintAndControlComponent && m_upmixIndicatorPaintAndControlComponent->isVisible())
114 m_upmixIndicatorPaintAndControlComponent->setBounds(bounds);
120 auto* peer = getPeer();
121 auto nativeHandle = peer ? peer->getNativeHandle() :
nullptr;
123 if (nativeHandle && !m_nativePinchViewHandle)
125 m_nativePinchViewHandle = nativeHandle;
126 JUCEAppBasics::iOS_utils::registerNativePinchOnView(
128 [
this](
float incrementalScale,
float cx,
float cy) {
131 auto localPt = m_loudspeakersInAreaPaintComponent->getLocalPoint(
132 nullptr, juce::Point<float>(cx, cy));
135 m_loudspeakersInAreaPaintComponent->simulatePinchZoom(incrementalScale, localPt);
138 else if (!nativeHandle && m_nativePinchViewHandle)
140 JUCEAppBasics::iOS_utils::unregisterNativePinchOnView(m_nativePinchViewHandle);
141 m_nativePinchViewHandle =
nullptr;
152 return controlConfigStateXml;
161 auto newIoSize = std::make_pair(ocp1IOSize.upToFirstOccurrenceOf(
"x",
false,
true).getIntValue(), ocp1IOSize.fromLastOccurrenceOf(
"x",
false,
true).getIntValue());
175 m_ocp1IOSize = ioSize;
178 rebuildOcp1ObjectTree();
181void UmsciControlComponent::rebuildOcp1ObjectTree()
184 setDatabaseComplete(
false);
185 m_deviceName.clear();
186 m_speakerPosition.clear();
187 m_sourcePosition.clear();
188 m_sourceDelayMode.clear();
189 m_sourceSpread.clear();
190 m_sourceName.clear();
191 m_speakerName.clear();
194 auto ocp1ObjectTree = std::vector<DeviceController::RemoteObject>();
197 for (std::int16_t i = 1; i <= m_ocp1IOSize.first; i++)
206 for (std::int16_t o = 1; o <= m_ocp1IOSize.second; o++)
214 DeviceController::getInstance()->SetActiveRemoteObjects(ocp1ObjectTree);
224 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_STRING == obj.
Var.GetDataType());
228 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_STRING == obj.
Var.GetDataType());
232 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_UINT8 == obj.
Var.GetDataType());
236 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_FLOAT32 == obj.
Var.GetDataType());
240 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_BLOB == obj.
Var.GetDataType());
244 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_UINT16 == obj.
Var.GetDataType());
248 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_FLOAT32 == obj.
Var.GetDataType());
252 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_STRING == obj.
Var.GetDataType());
256 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_UINT8 == obj.
Var.GetDataType());
260 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_FLOAT32 == obj.
Var.GetDataType());
264 jassert(NanoOcp1::Ocp1DataType::OCP1DATATYPE_BLOB == obj.
Var.GetDataType());
325 <<
" (" <<
static_cast<int>(obj.
Addr.
pri) <<
"," <<
static_cast<int>(obj.
Addr.
sec) <<
") ");
329 if (!m_databaseComplete && checkIsDatabaseComplete())
331 setDatabaseComplete(
true);
335bool UmsciControlComponent::checkIsDatabaseComplete()
337 bool complete =
true;
339 complete = complete && !m_deviceName.empty();
341 complete = complete && m_sourceName.size() == m_ocp1IOSize.first;
342 complete = complete && m_sourceMute.size() == m_ocp1IOSize.first;
343 complete = complete && m_sourceGain.size() == m_ocp1IOSize.first;
344 complete = complete && m_sourceSpread.size() == m_ocp1IOSize.first;
345 complete = complete && m_sourceDelayMode.size() == m_ocp1IOSize.first;
346 complete = complete && m_sourcePosition.size() == m_ocp1IOSize.first;
348 complete = complete && m_speakerName.size() == m_ocp1IOSize.second;
349 complete = complete && m_speakerMute.size() == m_ocp1IOSize.second;
350 complete = complete && m_speakerGain.size() == m_ocp1IOSize.second;
351 complete = complete && m_speakerPosition.size() == m_ocp1IOSize.second;
356bool UmsciControlComponent::isDatabaseComplete()
358 return m_databaseComplete;
361void UmsciControlComponent::updatePaintComponents()
363 auto realBoundingRect = getRealBoundingRect();
366 m_boundsRealRef = realBoundingRect.expanded(realBoundingRect.getWidth() * 0.2f,
367 realBoundingRect.getHeight() * 0.2f);
369 m_loudspeakersInAreaPaintComponent->setBoundsRealRef(m_boundsRealRef);
370 m_loudspeakersInAreaPaintComponent->setSpeakerPositions(m_speakerPosition);
372 m_soundobjectsInAreaPaintComponent->setBoundsRealRef(m_boundsRealRef);
373 m_soundobjectsInAreaPaintComponent->setSourcePositions(m_sourcePosition);
375 m_upmixIndicatorPaintAndControlComponent->setSpeakersRealBoundingCube(getRealBoundingCube());
376 m_upmixIndicatorPaintAndControlComponent->setBoundsRealRef(m_boundsRealRef);
377 m_upmixIndicatorPaintAndControlComponent->setSourcePositions(m_sourcePosition);
382void UmsciControlComponent::setDatabaseComplete(
bool complete)
384 DBG(juce::String(__FUNCTION__) << (complete ?
" compl." :
" incmplt."));
385 m_databaseComplete = complete;
389 updatePaintComponents();
396 m_deviceName.clear();
398 m_sourceName.clear();
399 m_sourceMute.clear();
400 m_sourceGain.clear();
401 m_sourceSpread.clear();
402 m_sourceDelayMode.clear();
403 m_sourcePosition.clear();
405 m_speakerName.clear();
406 m_speakerMute.clear();
407 m_speakerGain.clear();
408 m_speakerPosition.clear();
412const juce::Rectangle<float> UmsciControlComponent::getRealBoundingRect()
415 auto isValid = [](
const std::array<std::float_t, 6>& pos) {
416 return pos.at(0) != 0.0f || pos.at(1) != 0.0f || pos.at(2) != 0.0f
417 || pos.at(3) != 0.0f || pos.at(4) != 0.0f || pos.at(5) != 0.0f;
420 auto it = std::find_if(m_speakerPosition.begin(), m_speakerPosition.end(),
421 [&](
const auto& kv) { return isValid(kv.second); });
422 if (it == m_speakerPosition.end())
425 juce::Range<float> xRange = { it->second.at(3), it->second.at(3) };
426 juce::Range<float> yRange = { it->second.at(4), it->second.at(4) };
427 juce::Range<float> zRange = { it->second.at(5), it->second.at(5) };
429 for (
auto const& speakerPosition : m_speakerPosition)
431 if (!isValid(speakerPosition.second))
433 xRange = xRange.getUnionWith(speakerPosition.second.at(3));
434 yRange = yRange.getUnionWith(speakerPosition.second.at(4));
435 zRange = zRange.getUnionWith(speakerPosition.second.at(5));
441 return juce::Rectangle<float>({ yRange.getStart(), xRange.getStart() }, { yRange.getEnd(), xRange.getEnd() });
445const std::array<float, 6> UmsciControlComponent::getRealBoundingCube()
448 auto isValid = [](
const std::array<std::float_t, 6>& pos) {
449 return pos.at(0) != 0.0f || pos.at(1) != 0.0f || pos.at(2) != 0.0f
450 || pos.at(3) != 0.0f || pos.at(4) != 0.0f || pos.at(5) != 0.0f;
453 auto it = std::find_if(m_speakerPosition.begin(), m_speakerPosition.end(),
454 [&](
const auto& kv) { return isValid(kv.second); });
455 if (it == m_speakerPosition.end())
458 juce::Range<float> xRange = { it->second.at(3), it->second.at(3) };
459 juce::Range<float> yRange = { it->second.at(4), it->second.at(4) };
460 juce::Range<float> zRange = { it->second.at(5), it->second.at(5) };
462 for (
auto const& speakerPosition : m_speakerPosition)
464 if (!isValid(speakerPosition.second))
466 xRange = xRange.getUnionWith(speakerPosition.second.at(3));
467 yRange = yRange.getUnionWith(speakerPosition.second.at(4));
468 zRange = zRange.getUnionWith(speakerPosition.second.at(5));
471 return std::array<float, 6>({ xRange.getStart(), yRange.getStart(), zRange.getStart(), xRange.getEnd(), yRange.getEnd(), zRange.getEnd() });
476 setDatabaseComplete(
false);
486 m_sourceName[sourceId] = name;
491 switch (ocp1MuteValue)
494 m_sourceMute[sourceId] =
false;
498 m_sourceMute[sourceId] =
true;
506 m_sourceGain[sourceId] = gain;
511 m_sourcePosition[sourceId] = position;
513 if (m_databaseComplete)
515 m_soundobjectsInAreaPaintComponent->setSourcePosition(sourceId, position);
516 m_upmixIndicatorPaintAndControlComponent->setSourcePosition(sourceId, position);
522 m_sourceDelayMode[sourceId] = delayMode;
527 m_sourceSpread[sourceId] = spread;
532 m_speakerName[speakerId] = name;
537 switch (ocp1MuteValue)
540 m_speakerMute[speakerId] =
false;
544 m_speakerMute[speakerId] =
true;
551 m_speakerGain[speakerId] = gain;
556 m_speakerPosition[speakerId] = position;
558 if (m_databaseComplete)
561 auto realBoundingRect = getRealBoundingRect();
562 auto expandAmount = ((realBoundingRect.getAspectRatio() > 1.0f) ? realBoundingRect.getWidth() * 0.2f : realBoundingRect.getHeight() * 0.2f);
563 m_boundsRealRef = realBoundingRect.expanded(expandAmount, expandAmount);
565 m_loudspeakersInAreaPaintComponent->setBoundsRealRef(m_boundsRealRef);
566 m_loudspeakersInAreaPaintComponent->setSpeakerPosition(speakerId, position);
568 m_soundobjectsInAreaPaintComponent->setBoundsRealRef(m_boundsRealRef);
570 m_upmixIndicatorPaintAndControlComponent->setSpeakersRealBoundingCube(getRealBoundingCube());
571 m_upmixIndicatorPaintAndControlComponent->setBoundsRealRef(m_boundsRealRef);
579 if (m_upmixIndicatorPaintAndControlComponent)
580 m_upmixIndicatorPaintAndControlComponent->setChannelConfiguration(upmixChannelConfig);
581 updateSourceIdFilter();
586 if (m_upmixIndicatorPaintAndControlComponent)
587 return m_upmixIndicatorPaintAndControlComponent->getChannelConfiguration();
594 if (m_upmixIndicatorPaintAndControlComponent)
595 m_upmixIndicatorPaintAndControlComponent->setSourceStartId(startId);
596 updateSourceIdFilter();
601 if (m_upmixIndicatorPaintAndControlComponent)
602 return m_upmixIndicatorPaintAndControlComponent->getSourceStartId();
608 if (m_upmixIndicatorPaintAndControlComponent)
609 m_upmixIndicatorPaintAndControlComponent->setLiveMode(liveMode);
614 if (m_upmixIndicatorPaintAndControlComponent)
615 return m_upmixIndicatorPaintAndControlComponent->getLiveMode();
621 if (m_upmixIndicatorPaintAndControlComponent)
622 m_upmixIndicatorPaintAndControlComponent->setShape(shape);
627 if (m_upmixIndicatorPaintAndControlComponent)
628 return m_upmixIndicatorPaintAndControlComponent->getShape();
634 if (m_loudspeakersInAreaPaintComponent)
635 m_loudspeakersInAreaPaintComponent->setControlsSize(size);
636 if (m_soundobjectsInAreaPaintComponent)
637 m_soundobjectsInAreaPaintComponent->setControlsSize(size);
638 if (m_upmixIndicatorPaintAndControlComponent)
639 m_upmixIndicatorPaintAndControlComponent->setControlsSize(size);
644 if (m_loudspeakersInAreaPaintComponent)
645 return m_loudspeakersInAreaPaintComponent->getControlsSize();
651 if (m_upmixIndicatorPaintAndControlComponent)
652 m_upmixIndicatorPaintAndControlComponent->setUpmixTransform(rot, trans, heightTrans, angleStretch);
657 if (m_upmixIndicatorPaintAndControlComponent)
658 return m_upmixIndicatorPaintAndControlComponent->getUpmixRot();
664 if (m_upmixIndicatorPaintAndControlComponent)
665 return m_upmixIndicatorPaintAndControlComponent->getUpmixTrans();
671 if (m_upmixIndicatorPaintAndControlComponent)
672 return m_upmixIndicatorPaintAndControlComponent->getUpmixHeightTrans();
678 if (m_upmixIndicatorPaintAndControlComponent)
679 return m_upmixIndicatorPaintAndControlComponent->getUpmixAngleStretch();
685 if (m_upmixIndicatorPaintAndControlComponent)
686 m_upmixIndicatorPaintAndControlComponent->setUpmixOffset(x, y);
691 if (m_upmixIndicatorPaintAndControlComponent)
692 m_upmixIndicatorPaintAndControlComponent->notifyTransformChanged();
697 if (m_upmixIndicatorPaintAndControlComponent)
698 m_upmixIndicatorPaintAndControlComponent->triggerFlashCheck();
703 if (m_upmixIndicatorPaintAndControlComponent)
704 return m_upmixIndicatorPaintAndControlComponent->getUpmixOffsetX();
710 if (m_upmixIndicatorPaintAndControlComponent)
711 return m_upmixIndicatorPaintAndControlComponent->getUpmixOffsetY();
717 m_showAllSources = showAll;
718 updateSourceIdFilter();
723 return m_showAllSources;
726void UmsciControlComponent::updateSourceIdFilter()
728 if (!m_soundobjectsInAreaPaintComponent)
731 if (m_showAllSources)
733 m_soundobjectsInAreaPaintComponent->setSourceIdFilter({});
739 std::set<std::int16_t> allowedIds;
740 for (
int i = 0; i < channelCount; ++i)
741 allowedIds.insert(
static_cast<std::int16_t
>(startId + i));
742 m_soundobjectsInAreaPaintComponent->setSourceIdFilter(allowedIds);
@ IOSIZE
String: "inputs,outputs" channel count pair.
static juce::String getTagName(TagID ID)
static juce::String getAttributeName(AttributeID ID)
@ CONTROLCONFIG
Control-component settings.
float getUpmixTrans() const
Radial scale factor.
void setUpmixChannelConfiguration(const juce::AudioChannelSet &upmixChannelConfig)
void setSpeakerGain(std::int16_t speakerId, const std::float_t &gain)
void setUpmixTransform(float rot, float trans, float heightTrans, float angleStretch=1.0f)
bool getShowAllSources() const
void setUpmixShape(UmsciUpmixIndicatorPaintNControlComponent::IndicatorShape shape)
UmsciUpmixIndicatorPaintNControlComponent::IndicatorShape getUpmixShape() const
void setSpeakerPosition(std::int16_t speakerId, const std::array< std::float_t, 6 > &position)
Sets the 6-component speaker position: {X, Y, Z, horizontal angle, vertical angle,...
void setSourceName(std::int16_t sourceId, const std::string &name)
void setSourceDelayMode(std::int16_t sourceId, const std::uint16_t &delayMode)
Delay mode: 0 = off, 1 = compensate, 2 = reflect (DS100-specific enum).
void setShowAllSources(bool showAll)
When false, only sound objects that are part of the upmix group (i.e. channels >= sourceStartId withi...
std::unique_ptr< XmlElement > createStateXml() override
void setControlsSize(UmsciPaintNControlComponentBase::ControlsSize size)
Sets the visual size of source/speaker icons (small / medium / large).
void setUpmixOffset(float x, float y)
std::function< void()> onUpmixTransformChanged
Fired on the message thread whenever the user changes any upmix transform parameter via the on-screen...
const juce::AudioChannelSet getUpmixChannelConfiguration()
const std::pair< int, int > & getOcp1IOSize()
Sets the DS100 input/output channel count for this session.
void triggerUpmixTransformApplied()
Fires live-mode position callbacks and onUpmixTransformChanged after a programmatic transform update ...
bool setStateXml(XmlElement *stateXml) override
float getUpmixHeightTrans() const
Height (Z) translation.
void setUpmixLiveMode(bool liveMode)
true = follow live DS100 positions.
void setSourceGain(std::int16_t sourceId, const std::float_t &gain)
Gain in dB.
float getUpmixOffsetY() const
void setOcp1IOSize(const std::pair< int, int > &ioSize)
void resetData()
Clears all cached source/speaker data and marks the database incomplete.
void setSourcePosition(std::int16_t sourceId, const std::array< std::float_t, 3 > &position)
Sets the 3-component (X, Y, Z) absolute position in normalised real-world coordinates.
void triggerUpmixFlashCheck()
Starts the flash animation on the upmix indicator if the ideal ring positions diverge from the curren...
void parentHierarchyChanged() override
void setSpeakerName(std::int16_t speakerId, const std::string &name)
void setSpeakerMute(std::int16_t speakerId, const std::uint8_t &mute)
bool getUpmixLiveMode() const
int getUpmixSourceStartId() const
void setUpmixSourceStartId(int startId)
1-based DS100 channel of the first upmix input.
void setDeviceName(const std::string &name)
Updates the device name label (shown as a title/overlay in the UI).
std::function< void()> onDatabaseComplete
Fired on the message thread when all initially subscribed values have been received from the DS100 (i...
float getUpmixAngleStretch() const
Per-angle stretch factor (1.0 = uniform).
UmsciPaintNControlComponentBase::ControlsSize getControlsSize() const
void setSourceMute(std::int16_t sourceId, const std::uint8_t &mute)
0 = unmuted, nonzero = muted.
float getUpmixOffsetX() const
void paint(juce::Graphics &g) override
~UmsciControlComponent() override
void setSourceSpread(std::int16_t sourceId, const std::float_t &spread)
Spread factor 0.0–1.0 (0 = point source, 1 = full spread).
float getUpmixRot() const
Ring rotation in normalised units (0–1 → 0–360°).
ControlsSize
Visual size of source/speaker icons. Multiplier accessible via getControlsSizeMultiplier().
IndicatorShape
The geometric shape used to draw the upmix speaker ring.
Two-dimensional address of a remote object on the DS100.
std::int16_t pri
Primary index (channel, speaker, group, zone…). 0 = not used.
juce::String toNiceString() const
static constexpr std::int16_t sc_INV
std::int16_t sec
Secondary index (mapping area, output ch, group…). 0 = not used.
A fully-qualified remote parameter including its type, address, and current value.
static juce::String GetObjectDescription(const RemObjIdent roi)
@ CoordinateMappingSettings_P1virtual
@ Positioning_SpeakerPosition
@ CoordinateMapping_SourcePosition
@ Status_AudioNetworkSampleStatus
@ MatrixOutput_LevelMeterPostMute
@ CoordinateMappingSettings_P3real
@ CoordinateMappingSettings_P4real
@ MatrixInput_ReverbSendGain
@ CoordinateMappingSettings_Name
@ CoordinateMappingSettings_Flip
@ CoordinateMappingSettings_P1real
@ MatrixInput_LevelMeterPostMute
@ MatrixSettings_ReverbPredelayFactor
@ Positioning_SourceSpread
@ CoordinateMappingSettings_P3virtual
@ ReverbInputProcessing_Mute
@ Positioning_SourcePosition
@ CoordinateMappingSettings_P2real
@ MatrixOutput_LevelMeterPreMute
@ ReverbInputProcessing_EqEnable
@ Positioning_SourceDelayMode
@ MatrixOutput_DelayEnable
@ SoundObjectRouting_Gain
@ MatrixOutput_ChannelName
@ ReverbInputProcessing_LevelMeter
@ MatrixInput_DelayEnable
@ MatrixInput_LevelMeterPreMute
@ ReverbInputProcessing_Gain
@ Fixed_GUID
Read-only 8-char device GUID; queried before any subscriptions.
@ MatrixSettings_ReverbRearLevel
@ SoundObjectRouting_Mute
@ MatrixInput_ChannelName
@ MatrixSettings_ReverbRoomId
@ FunctionGroup_SpreadFactor