Skip to content

Commit

Permalink
refactor(spa): revamp pod types
Browse files Browse the repository at this point in the history
  • Loading branch information
Curve committed Oct 12, 2024
1 parent 4819354 commit 1e01d66
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 397 deletions.
2 changes: 0 additions & 2 deletions examples/mute-microphone/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# 🔇 `mute-microphone` Example

This example demonstrates how to mute a `pipewire::device` by enumerating its `params()`.

> Note: See the comment in the source code to find out how mute most devices directly.
51 changes: 4 additions & 47 deletions examples/mute-microphone/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <iostream>

#include <rohrkabel/device/device.hpp>
#include <rohrkabel/spa/pod/object/body.hpp>
#include <rohrkabel/spa/pod/object/object.hpp>

#include <rohrkabel/registry/events.hpp>
#include <rohrkabel/registry/registry.hpp>
Expand Down Expand Up @@ -67,48 +67,16 @@ int main()
auto params = device.params();
core->update();

auto get_mute = [](const pw::spa::pod &pod) {
// NOLINTNEXTLINE
auto impl = [](const pw::spa::pod_prop *parent, const pw::spa::pod &pod,
auto &self) -> std::optional<pw::spa::pod_prop> {
if (pod.type() == pw::spa::pod_type::object)
{
for (const auto &item : pod.body<pw::spa::pod_object_body>())
{
auto rtn = self(&item, item.value(), self);

if (!rtn.has_value())
{
continue;
}

return rtn;
}
}

if (parent && pod.type() == pw::spa::pod_type::boolean && parent->name().find("mute") != std::string::npos)
{
return *parent;
}

return std::nullopt;
};

return impl(nullptr, pod, impl);
};

for (const auto &[pod_id, pod] : params.get())
{
auto mute = get_mute(pod);
auto prop = pod.find_recursive(pw::spa::prop::mute);

if (!mute)
if (!prop)
{
continue;
}

std::cout << std::format("Mute-Prop: {} ({}) [{}]", mute->name(), pod_id, mute->key()) << std::endl;
mute->value().write(!mute->value().read<bool>());

prop->value().write(!prop->value().as<bool>());
device.set_param(pod_id, 0, pod);
core->update();

Expand All @@ -119,14 +87,3 @@ int main()
std::cout << "Could not find mute prop for device!" << std::endl;
return 1;
}

//? Instead of enumerating all pods you could also use the short version:
/*
auto mute =
pods.at(13).body<pipewire::spa::pod_object_body>().at(10).value().body<pipewire::spa::pod_object_body>().at(65540).value();
mute.as<bool>() = !mute.as<bool>();
device.set_param(13, pods.at(13).get());
core.sync();
*/
73 changes: 18 additions & 55 deletions examples/volume/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <format>
#include <iostream>

#include <rohrkabel/node/node.hpp>
#include <rohrkabel/spa/pod/object/body.hpp>
#include <rohrkabel/device/device.hpp>
#include <rohrkabel/spa/pod/object/object.hpp>

#include <rohrkabel/registry/events.hpp>
#include <rohrkabel/registry/registry.hpp>
Expand All @@ -17,25 +17,25 @@ int main()
auto core = pw::core::create(context);
auto reg = pw::registry::create(core);

std::vector<pw::node> devices;
std::vector<pw::device> devices;

auto listener = reg->listen();

auto on_global = [&](const pipewire::global &global) {
if (global.type != pipewire::node::type)
if (global.type != pipewire::device::type)
{
return;
}

auto device = reg->bind<pipewire::node>(global.id).get();
auto props = device->info().props;
auto device = reg->bind<pipewire::device>(global.id).get();
auto info = device->info();

if (!props.contains("node.nick"))
if (info.props["media.class"] != "Audio/Device")
{
return;
}

if (props["media.class"].find("Audio") == std::string::npos)
if (!info.props.contains("device.description"))
{
return;
}
Expand All @@ -49,11 +49,9 @@ int main()
for (auto i = 0u; devices.size() > i; i++)
{
auto &device = devices.at(i);
auto name = device.info().props.at("device.description");

auto name = device.info().props.at("node.nick");
auto media = device.info().props.at("media.class");

std::cout << std::format("{}. {} ({})", i, name, media) << std::endl;
std::cout << std::format("{}. {}", i, name) << std::endl;
}

std::cout << std::endl;
Expand All @@ -63,7 +61,7 @@ int main()
std::cin >> selection;

auto &device = devices.at(selection);
std::cout << "Input new volume for '" << device.info().props.at("node.nick") << "': ";
std::cout << "Input new volume for '" << device.info().props.at("device.description") << "': ";

float volume{0};
std::cin >> volume;
Expand All @@ -72,64 +70,29 @@ int main()
auto params = device.params();
core->update();

auto get_channels = [](const pw::spa::pod &pod) {
// NOLINTNEXTLINE
auto impl = [](const pw::spa::pod_prop *parent, const pw::spa::pod &pod,
auto &self) -> std::optional<pw::spa::pod_prop> {
if (pod.type() == pw::spa::pod_type::object)
{
for (const auto &item : pod.body<pw::spa::pod_object_body>())
{
auto rtn = self(&item, item.value(), self);

if (!rtn.has_value())
{
continue;
}

return rtn;
}
}

if (!parent || !parent->name().ends_with("channelVolumes"))
{
return std::nullopt;
}

return *parent;
};

return impl(nullptr, pod, impl);
};

for (const auto &[pod_id, pod] : params.get())
{
auto channels = get_channels(pod);
auto prop = pod.find_recursive(pw::spa::prop::channel_volumes);

if (!channels)
if (!prop)
{
continue;
}

// pipewire uses cubic volumes! (that's why we use std::cbrt, and std::pow)

std::cout << std::format("Channels: {} ({}) [{}]", channels->name(), pod_id, channels->key()) << std::endl;

auto volumes = channels->value().read<std::vector<float>>();
std::cout << std::format("Changed volume from {}% to {}%", std::cbrt(volumes[0]) * 100, volume) << std::endl;

auto new_volumes = volumes | std::views::transform([volume](auto &&...) {
return std::pow(volume / 100, 3);
});
auto channels = prop->value().as<std::vector<void *>>();
auto cubic_volume = std::powf(volume / 100, 3);

channels->value().write<std::vector<float>>({new_volumes.begin(), new_volumes.end()});
*reinterpret_cast<float *>(channels[0]) = cubic_volume;
*reinterpret_cast<float *>(channels[1]) = cubic_volume;

device.set_param(pod_id, 0, pod);
core->update();

return 0;
}

std::cout << "Could not find channels for node!" << std::endl;
std::cout << "Could not find volume prop for device!" << std::endl;
return 1;
}
70 changes: 0 additions & 70 deletions include/rohrkabel/spa/pod/object/body.hpp

This file was deleted.

13 changes: 7 additions & 6 deletions include/rohrkabel/spa/pod/object/iterator.hpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
#pragma once

#include "body.hpp"
#include "object.hpp"
#include "../prop.hpp"

#include <memory>
#include <cstddef>
#include <iterator>

struct spa_pod_prop;

namespace pipewire::spa
{
class pod_object_body::sentinel
class pod_object::sentinel
{
};

class pod_object_body::iterator : public std::forward_iterator_tag
class pod_object::iterator : public std::forward_iterator_tag
{
struct impl;

Expand All @@ -30,8 +29,10 @@ namespace pipewire::spa

public:
iterator();

public:
iterator(const iterator &);
iterator(const pod_object_body *);
iterator(const pod_object *);

public:
iterator &operator=(const iterator &);
Expand Down
59 changes: 59 additions & 0 deletions include/rohrkabel/spa/pod/object/object.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include "../pod.hpp"

#include <memory>
#include <cstdint>

struct spa_pod_object;

namespace pipewire::spa
{
class pod_object
{
struct impl;

public:
using raw_type = spa_pod_object;

public:
class iterator;
class sentinel;

private:
std::unique_ptr<impl> m_impl;

public:
~pod_object();

private:
pod_object(raw_type *);

public:
pod_object(pod_object &&) noexcept;

public:
pod_object &operator=(pod_object &&) noexcept;

public:
[[nodiscard]] spa::pod pod() const;
[[nodiscard]] spa::type type() const;
[[nodiscard]] std::uint32_t id() const;

public:
[[nodiscard]] sentinel end() const;
[[nodiscard]] iterator begin() const;

public:
[[nodiscard]] raw_type *get() const;

public:
[[nodiscard]] operator raw_type *() const &;
[[nodiscard]] operator raw_type *() const && = delete;

public:
[[nodiscard]] static pod_object view(raw_type *);
};
} // namespace pipewire::spa

#include "iterator.hpp"
Loading

0 comments on commit 1e01d66

Please sign in to comment.