Skip to content

Commit

Permalink
Opening the Dynamic Mappings editor from the matrix only showed the f…
Browse files Browse the repository at this point in the history
…irst STREAM_PORT, it now shows all STREAM_PORTs
  • Loading branch information
christophe-calmejane committed Sep 8, 2023
1 parent 4f695bc commit d33f4df
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 182 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Possible crash for entities returning an out-of-bounds MemoryObject length value
- [Files can be loaded/saved from/to UTF8 path](https://github.com/christophe-calmejane/Hive/issues/156)
- Crash when displaying vendor specific control values
- Opening the Dynamic Mappings editor from the matrix only showed the first STREAM_PORT, it now shows all STREAM_PORTs

## [1.2.9] - 2023-01-13
### Added
Expand Down
323 changes: 211 additions & 112 deletions src/avdecc/mappingsHelper.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/avdecc/mappingsHelper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
#include <set>
#include <utility>
#include <QObject>
#include <optional>

namespace avdecc
{
namespace mappingsHelper
{
void showMappingsEditor(QObject* obj, la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamIndex const streamIndex) noexcept;
void showMappingsEditor(QObject* obj, la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::AudioUnitIndex const audioUnitIndex, la::avdecc::entity::model::DescriptorType const streamPortType, std::optional<la::avdecc::entity::model::StreamPortIndex> const streamPortIndex, la::avdecc::entity::model::StreamIndex const streamIndex) noexcept;
la::avdecc::entity::model::AudioMappings getMaximumAudioMappings(la::avdecc::entity::model::AudioMappings const& mappings, size_t const offset) noexcept;
/** Adds new input audio mappings. Entity is expected to be under ExclusiveAccess. */
void batchAddInputAudioMappings(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::AudioMappings const& mappings, hive::modelsLibrary::ControllerManager::AddStreamPortInputAudioMappingsHandler const& handler = {}) noexcept;
Expand Down
109 changes: 45 additions & 64 deletions src/connectionMatrix/headerView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,20 @@ void HeaderView::paintSection(QPainter* painter, QRect const& rect, int logicalI
painter->restore();
}

template<class StreamPorts>
bool atLeastOneDynamicMappings(StreamPorts const& streamPorts)
{
for (auto const& [streamPortIndex, streamPortNode] : streamPorts)
{
if (streamPortNode.staticModel.hasDynamicAudioMap)
{
return true;
}
}

return false;
}

void HeaderView::contextMenuEvent(QContextMenuEvent* event)
{
auto const logicalIndex = logicalIndexAt(event->pos());
Expand Down Expand Up @@ -645,47 +659,30 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
struct MapSupTypeIndex
{
bool supportsDynamicMappings{ false };
la::avdecc::entity::model::AudioUnitIndex audioUnitIndex{ la::avdecc::entity::model::getInvalidDescriptorIndex() };
la::avdecc::entity::model::DescriptorType streamPortType{ la::avdecc::entity::model::DescriptorType::Invalid };
la::avdecc::entity::model::StreamPortIndex streamPortIndex{ la::avdecc::entity::model::getInvalidDescriptorIndex() };
la::avdecc::entity::model::StreamIndex streamIndex{ la::avdecc::entity::model::getInvalidDescriptorIndex() };
};
auto findMappingsSupportTypeIndexForDescriptor = [](la::avdecc::controller::ControlledEntity const& controlledEntity, la::avdecc::entity::model::DescriptorType const& descriptorType)
{
// Currently returning the first audio unit - TODO: If we have devices with multiple audio units, we shall probably offer to choose which audio unit to edit
auto mti = MapSupTypeIndex{};
auto constexpr audioUnitIndex = la::avdecc::entity::model::AudioUnitIndex{ 0u };

try
{
auto const& audioUnitNode = controlledEntity.getAudioUnitNode(controlledEntity.getCurrentConfigurationIndex(), audioUnitIndex);
if (descriptorType == la::avdecc::entity::model::DescriptorType::StreamInput)
{
for (auto const& [audioUnitIndex, audioUnitNode] : controlledEntity.getCurrentConfigurationNode().audioUnits)
{
for (auto const& [spi, streamPortNode] : audioUnitNode.streamPortInputs)
{
if (streamPortNode.staticModel.hasDynamicAudioMap)
{
mti.supportsDynamicMappings = true;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortInput;
mti.streamPortIndex = spi;
break;
}
}
}
mti.supportsDynamicMappings = atLeastOneDynamicMappings(audioUnitNode.streamPortInputs);
mti.audioUnitIndex = audioUnitIndex;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortInput;
}
else if (descriptorType == la::avdecc::entity::model::DescriptorType::StreamOutput)
{
for (auto const& [audioUnitIndex, audioUnitNode] : controlledEntity.getCurrentConfigurationNode().audioUnits)
{
for (auto const& [spi, streamPortNode] : audioUnitNode.streamPortOutputs)
{
if (streamPortNode.staticModel.hasDynamicAudioMap)
{
mti.supportsDynamicMappings = true;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortOutput;
mti.streamPortIndex = spi;
break;
}
}
}
mti.supportsDynamicMappings = atLeastOneDynamicMappings(audioUnitNode.streamPortOutputs);
mti.audioUnitIndex = audioUnitIndex;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortOutput;
}
}
catch (la::avdecc::controller::ControlledEntity::Exception const&)
Expand All @@ -697,6 +694,7 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
};
auto findMappingsSupportTypeIndexForStreamNode = [](la::avdecc::controller::ControlledEntity const& controlledEntity, la::avdecc::controller::model::StreamNode const& streamNode)
{
// Currently returning the first audio unit - TODO: If we have devices with multiple audio units, we shall probably offer to choose which audio unit to edit
auto mti = MapSupTypeIndex{};

auto const isValidStreamFormat = [](auto const& streamNode)
Expand All @@ -712,47 +710,30 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
{
for (auto const& [audioUnitIndex, audioUnitNode] : controlledEntity.getCurrentConfigurationNode().audioUnits)
{
// Search which StreamPort has the same ClockDomain than passed StreamNode
for (auto const& [spi, streamPortNode] : audioUnitNode.streamPortInputs)
// Search which AudioUnit has the same ClockDomain than passed StreamNode
if (audioUnitNode.staticModel.clockDomainIndex == streamNode.staticModel.clockDomainIndex)
{
if (streamPortNode.staticModel.clockDomainIndex == streamNode.staticModel.clockDomainIndex)
{
if (streamPortNode.staticModel.hasDynamicAudioMap)
{
auto const& streamInputNode = static_cast<la::avdecc::controller::model::StreamInputNode const&>(streamNode);
auto const sfi = la::avdecc::entity::model::StreamFormatInfo::create(streamInputNode.dynamicModel.streamFormat);

mti.supportsDynamicMappings = isValidStreamFormat(streamInputNode);
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortInput;
mti.streamPortIndex = spi;
// Don't allow StreamInput to be configured individually to prevent user errors
}
break;
}
mti.supportsDynamicMappings = atLeastOneDynamicMappings(audioUnitNode.streamPortInputs);
mti.audioUnitIndex = audioUnitIndex;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortInput;
// Don't allow StreamInput to be configured individually to prevent user errors
break;
}
}
}
else if (streamNode.descriptorType == la::avdecc::entity::model::DescriptorType::StreamOutput)
{
for (auto const& [audioUnitIndex, audioUnitNode] : controlledEntity.getCurrentConfigurationNode().audioUnits)
{
// Search which StreamPort has the same ClockDomain than passed StreamNode
for (auto const& [spi, streamPortNode] : audioUnitNode.streamPortOutputs)
// Search which AudioUnit has the same ClockDomain than passed StreamNode
if (audioUnitNode.staticModel.clockDomainIndex == streamNode.staticModel.clockDomainIndex)
{
if (streamPortNode.staticModel.clockDomainIndex == streamNode.staticModel.clockDomainIndex)
{
if (streamPortNode.staticModel.hasDynamicAudioMap)
{
auto const& streamOutputNode = static_cast<la::avdecc::controller::model::StreamOutputNode const&>(streamNode);
auto const sfi = la::avdecc::entity::model::StreamFormatInfo::create(streamOutputNode.dynamicModel.streamFormat);

mti.supportsDynamicMappings = isValidStreamFormat(streamOutputNode);
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortOutput;
mti.streamPortIndex = spi;
mti.streamIndex = streamOutputNode.descriptorIndex;
}
break;
}
auto const& streamOutputNode = static_cast<la::avdecc::controller::model::StreamOutputNode const&>(streamNode);
mti.supportsDynamicMappings = atLeastOneDynamicMappings(audioUnitNode.streamPortOutputs) && isValidStreamFormat(streamOutputNode);
mti.audioUnitIndex = audioUnitIndex;
mti.streamPortType = la::avdecc::entity::model::DescriptorType::StreamPortOutput;
mti.streamIndex = streamOutputNode.descriptorIndex;
break;
}
}
}
Expand Down Expand Up @@ -833,7 +814,7 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
}
else if (action == editMappingsAction)
{
handleEditMappingsClicked(entityID, mti.streamPortType, mti.streamPortIndex, mti.streamIndex);
handleEditMappingsClicked(entityID, mti.audioUnitIndex, mti.streamPortType, mti.streamIndex);
}
}
}
Expand Down Expand Up @@ -868,7 +849,7 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
{
if (action == editMappingsAction)
{
handleEditMappingsClicked(entityID, mti.streamPortType, mti.streamPortIndex, mti.streamIndex);
handleEditMappingsClicked(entityID, mti.audioUnitIndex, mti.streamPortType, mti.streamIndex);
}
}
}
Expand Down Expand Up @@ -899,7 +880,7 @@ void HeaderView::contextMenuEvent(QContextMenuEvent* event)
}
else if (action == editMappingsAction)
{
handleEditMappingsClicked(entityID, mti.streamPortType, mti.streamPortIndex, mti.streamIndex);
handleEditMappingsClicked(entityID, mti.audioUnitIndex, mti.streamPortType, mti.streamIndex);
}
}
}
Expand Down Expand Up @@ -939,9 +920,9 @@ void HeaderView::leaveEvent(QEvent* event)
QHeaderView::leaveEvent(event);
}

void HeaderView::handleEditMappingsClicked(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamIndex const streamIndex)
void HeaderView::handleEditMappingsClicked(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::AudioUnitIndex const audioUnitIndex, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamIndex const streamIndex)
{
avdecc::mappingsHelper::showMappingsEditor(this, entityID, streamPortType, streamPortIndex, streamIndex);
avdecc::mappingsHelper::showMappingsEditor(this, entityID, audioUnitIndex, streamPortType, std::nullopt, streamIndex);
}

} // namespace connectionMatrix
Expand Down
2 changes: 1 addition & 1 deletion src/connectionMatrix/headerView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class HeaderView final : public QHeaderView
void handleSectionInserted(QModelIndex const& parent, int first, int last);
void handleSectionRemoved(QModelIndex const& parent, int first, int last);
void handleModelReset();
void handleEditMappingsClicked(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamIndex const streamIndex);
void handleEditMappingsClicked(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::AudioUnitIndex const audioUnitIndex, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamIndex const streamIndex);
void updateSectionVisibility(int const logicalIndex);
void applyFilterPattern();

Expand Down
2 changes: 2 additions & 0 deletions src/mappingMatrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@

#include <vector>
#include <string>
#include <any>

namespace mappingMatrix
{
struct Node
{
std::string name{};
std::vector<std::string> sockets{};
std::any userData{};
};

using Nodes = std::vector<Node>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@
/* ************************************************************ */
/* StreamPortDynamicTreeWidgetItem */
/* ************************************************************ */
StreamPortDynamicTreeWidgetItem::StreamPortDynamicTreeWidgetItem(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamPortNodeStaticModel const* const /*staticModel*/, la::avdecc::entity::model::StreamPortNodeDynamicModel const* const /*dynamicModel*/, QTreeWidget* parent)
StreamPortDynamicTreeWidgetItem::StreamPortDynamicTreeWidgetItem(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::AudioUnitIndex const audioUnitIndex, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamPortNodeStaticModel const* const /*staticModel*/, la::avdecc::entity::model::StreamPortNodeDynamicModel const* const /*dynamicModel*/, QTreeWidget* parent)
: QTreeWidgetItem(parent)
, _entityID(entityID)
, _audioUnitIndex(audioUnitIndex)
, _streamPortType(streamPortType)
, _streamPortIndex(streamPortIndex)
{
AVDECC_ASSERT(audioUnitIndex != la::avdecc::entity::model::getInvalidDescriptorIndex(), "Invalid AudioUnitIndex");

// StreamPortInfo
{
// Create fields
Expand Down Expand Up @@ -82,7 +85,7 @@ StreamPortDynamicTreeWidgetItem::StreamPortDynamicTreeWidgetItem(la::avdecc::Uni

void StreamPortDynamicTreeWidgetItem::editMappingsButtonClicked()
{
avdecc::mappingsHelper::showMappingsEditor(this, _entityID, _streamPortType, _streamPortIndex, la::avdecc::entity::model::getInvalidDescriptorIndex());
avdecc::mappingsHelper::showMappingsEditor(this, _entityID, _audioUnitIndex, _streamPortType, _streamPortIndex, la::avdecc::entity::model::getInvalidDescriptorIndex());
}

void StreamPortDynamicTreeWidgetItem::clearMappingsButtonClicked()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
class StreamPortDynamicTreeWidgetItem : public QObject, public QTreeWidgetItem
{
public:
StreamPortDynamicTreeWidgetItem(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamPortNodeStaticModel const* const staticModel, la::avdecc::entity::model::StreamPortNodeDynamicModel const* const dynamicModel, QTreeWidget* parent = nullptr);
StreamPortDynamicTreeWidgetItem(la::avdecc::UniqueIdentifier const entityID, la::avdecc::entity::model::AudioUnitIndex const audioUnitIndex, la::avdecc::entity::model::DescriptorType const streamPortType, la::avdecc::entity::model::StreamPortIndex const streamPortIndex, la::avdecc::entity::model::StreamPortNodeStaticModel const* const staticModel, la::avdecc::entity::model::StreamPortNodeDynamicModel const* const dynamicModel, QTreeWidget* parent = nullptr);

private:
void editMappingsButtonClicked();
void clearMappingsButtonClicked();
void updateMappings();

la::avdecc::UniqueIdentifier const _entityID{};
la::avdecc::entity::model::AudioUnitIndex const _audioUnitIndex{ 0u };
la::avdecc::entity::model::DescriptorType const _streamPortType{ la::avdecc::entity::model::DescriptorType::Entity };
la::avdecc::entity::model::StreamPortIndex const _streamPortIndex{ 0u };
QListWidget* _mappingsList{ nullptr };
Expand Down
4 changes: 3 additions & 1 deletion src/nodeTreeWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ class NodeTreeWidgetPrivate : public QObject, public NodeVisitor
auto* dynamicItem = new AudioUnitDynamicTreeWidgetItem(_controlledEntityID, node.descriptorIndex, &node.staticModel, &node.dynamicModel, q);
dynamicItem->setText(0, "Dynamic Info");
}
_currentAudioUnitIndex = node.descriptorIndex;
}

virtual void visit(la::avdecc::controller::ControlledEntity const* const controlledEntity, bool const isActiveConfiguration, la::avdecc::controller::model::StreamInputNode const& node) noexcept override
Expand Down Expand Up @@ -700,7 +701,7 @@ class NodeTreeWidgetPrivate : public QObject, public NodeVisitor
auto const hasAtLeastOneDynamicInfo = node.staticModel.hasDynamicAudioMap;
if (isActiveConfiguration && hasAtLeastOneDynamicInfo)
{
auto* dynamicItem = new StreamPortDynamicTreeWidgetItem(_controlledEntityID, node.descriptorType, node.descriptorIndex, &node.staticModel, &node.dynamicModel, q);
auto* dynamicItem = new StreamPortDynamicTreeWidgetItem(_controlledEntityID, _currentAudioUnitIndex, node.descriptorType, node.descriptorIndex, &node.staticModel, &node.dynamicModel, q);
dynamicItem->setText(0, "Dynamic Info");
}
}
Expand Down Expand Up @@ -1600,6 +1601,7 @@ class NodeTreeWidgetPrivate : public QObject, public NodeVisitor
Q_DECLARE_PUBLIC(NodeTreeWidget);

la::avdecc::UniqueIdentifier _controlledEntityID{};
la::avdecc::entity::model::AudioUnitIndex _currentAudioUnitIndex{ la::avdecc::entity::model::getInvalidDescriptorIndex() };
};

/** Linear Values - Clause 7.3.5.2.1 */
Expand Down

0 comments on commit d33f4df

Please sign in to comment.