Skip to content

Commit

Permalink
refactor(spa): revamp pod types
Browse files Browse the repository at this point in the history
Curve committed Oct 12, 2024
1 parent 4819354 commit 1e01d66
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
@@ -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>
@@ -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();

@@ -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
@@ -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>
@@ -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;
}
@@ -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;
@@ -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;
@@ -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;

@@ -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 &);
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"
38 changes: 21 additions & 17 deletions include/rohrkabel/spa/pod/pod.hpp
Original file line number Diff line number Diff line change
@@ -3,14 +3,16 @@
#include <string>
#include <memory>
#include <vector>

#include <optional>
#include <cstdint>

struct spa_pod;
struct spa_type_info;

namespace pipewire::spa
{
enum class pod_type : std::uint8_t
enum class type : std::uint8_t
{
string = 8,
boolean = 2,
@@ -19,7 +21,14 @@ namespace pipewire::spa
array = 13,
};

class pod_object_body;
enum class prop : std::uint32_t
{
mute = 65540,
channel_volumes = 65544,
};

class pod_prop;
class pod_object;

class pod
{
@@ -46,25 +55,23 @@ namespace pipewire::spa
pod &operator=(const pod &) noexcept;

public:
[[nodiscard]] pod_type type() const;
[[nodiscard]] std::size_t size() const;
[[nodiscard]] std::string name() const;
[[nodiscard]] std::optional<pod_prop> find(prop) const;
[[nodiscard]] std::optional<pod_prop> find_recursive(prop) const;

public:
template <typename T>
[[nodiscard]] T body() const = delete;
[[nodiscard]] spa::type type() const;
[[nodiscard]] std::size_t size() const;

public:
template <typename T>
[[nodiscard]] T read() const = delete;
[[nodiscard]] T as() const = delete;

public:
template <typename T>
void write(T) = delete;

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

public:
[[nodiscard]] operator raw_type *() const &;
@@ -76,21 +83,18 @@ namespace pipewire::spa
};

template <>
bool pod::read() const;
bool pod::as() const;
template <>
float pod::read() const;
float pod::as() const;
template <>
std::vector<float> pod::read() const;
pod_object pod::as() const;
template <>
std::string pod::read() const;

std::string pod::as() const;
template <>
pod_object_body pod::body<pod_object_body>() const;
std::vector<void *> pod::as() const;

template <>
void pod::write(bool);
template <>
void pod::write(float);
template <>
void pod::write(std::vector<float>);
} // namespace pipewire::spa
7 changes: 1 addition & 6 deletions include/rohrkabel/spa/pod/prop.hpp
Original file line number Diff line number Diff line change
@@ -2,12 +2,10 @@

#include "pod.hpp"

#include <string>
#include <memory>
#include <cstdint>

struct spa_pod_prop;
struct spa_type_info;

namespace pipewire::spa
{
@@ -17,7 +15,6 @@ namespace pipewire::spa

public:
using raw_type = spa_pod_prop;
using raw_info = spa_type_info;

private:
std::unique_ptr<impl> m_impl;
@@ -38,19 +35,17 @@ namespace pipewire::spa

public:
[[nodiscard]] pod value() const;
[[nodiscard]] std::string name() const;
[[nodiscard]] std::uint32_t key() const;
[[nodiscard]] std::uint32_t flags() const;

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

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

public:
[[nodiscard]] static pod_prop view(raw_type *, const raw_info *);
[[nodiscard]] static pod_prop view(raw_type *);
};
} // namespace pipewire::spa
129 changes: 75 additions & 54 deletions src/spa/spa.pod.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
#include "spa/pod/pod.hpp"
#include "spa/pod/object/body.hpp"

#include "spa/pod/prop.hpp"
#include "spa/pod/object/object.hpp"

#include <cassert>
#include <spa/debug/pod.h>
#include <spa/pod/builder.h>

namespace pipewire::spa
{
// TODO: Make move-only?

struct pod::impl
{
const spa_type_info *type;
std::shared_ptr<raw_type> pod;
};

pod::~pod() = default;

pod::pod(std::shared_ptr<raw_type> pod) : m_impl(std::make_unique<impl>())
{
m_impl->type = spa_debug_type_find(nullptr, pod->type);
m_impl->pod = std::move(pod);
m_impl->pod = std::move(pod);
}

pod::pod(pod &&other) noexcept : m_impl(std::move(other.m_impl)) {}
@@ -44,108 +46,127 @@ namespace pipewire::spa
return *this;
}

pod_type pod::type() const
std::optional<pod_prop> pod::find(prop key) const
{
return static_cast<pod_type>(m_impl->pod->type);
const auto *prop = spa_pod_find_prop(m_impl->pod.get(), nullptr, static_cast<std::uint32_t>(key));

if (!prop)
{
return std::nullopt;
}

// TODO: const_cast :>(
return pod_prop::view(const_cast<spa_pod_prop *>(prop)); // NOLINT(*-const-cast)
}

std::size_t pod::size() const
std::optional<pod_prop> pod::find_recursive(prop key) const
{
return m_impl->pod->size;
auto find_recursive = [key](const auto &pod, auto &self) -> std::optional<pod_prop> // NOLINT(*-recursion)
{
if (auto ret = pod.find(key); ret)
{
return ret;
}

if (pod.type() != spa::type::object)
{
return std::nullopt;
}

auto object = pod.template as<spa::pod_object>();

for (const auto &child : object)
{
auto ret = self(child.value(), self);

if (!ret)
{
continue;
}

return ret;
}

return std::nullopt;
};

return find_recursive(*this, find_recursive);
}

std::string pod::name() const
spa::type pod::type() const
{
return m_impl->type->name;
return static_cast<spa::type>(m_impl->pod->type);
}

template <>
pod_object_body pod::body<pod_object_body>() const
std::size_t pod::size() const
{
auto *body = reinterpret_cast<spa_pod_object_body *>(SPA_POD_BODY(m_impl->pod.get()));
return pod_object_body::view(body, size());
return m_impl->pod->size;
}

template <>
bool pod::read() const
bool pod::as() const
{
assert(type() == pod_type::boolean);
assert(type() == spa::type::boolean);
return reinterpret_cast<spa_pod_bool *>(m_impl->pod.get())->value;
}

template <>
float pod::read() const
float pod::as() const
{
assert(type() == pod_type::num_float);
assert(type() == spa::type::num_float);
return reinterpret_cast<spa_pod_float *>(m_impl->pod.get())->value;
}

template <>
std::vector<float> pod::read() const
pod_object pod::as<pod_object>() const
{
assert(type() == spa::type::object);
return pod_object::view(reinterpret_cast<spa_pod_object *>(m_impl->pod.get()));
}
template <>
std::string pod::as() const
{
assert(type() == pod_type::array);
assert(type() == spa::type::string);
return {reinterpret_cast<const char *>(SPA_POD_CONTENTS(spa_pod_string, m_impl->pod.get()))};
}

template <>
std::vector<void *> pod::as() const
{
assert(type() == spa::type::array);

std::vector<float> rtn;
std::vector<void *> rtn;

auto *array = reinterpret_cast<spa_pod_array *>(m_impl->pod.get());
float *iter = {};
void *iter = {};

SPA_POD_ARRAY_FOREACH(array, iter)
{
rtn.emplace_back(*iter);
rtn.emplace_back(iter);
}

return rtn;
}

template <>
std::string pod::read() const
{
assert(type() == pod_type::string);
return {reinterpret_cast<const char *>(SPA_POD_CONTENTS(spa_pod_string, m_impl->pod.get()))};
}

template <>
void pod::write(bool value)
{
assert(type() == pod_type::boolean);
assert(type() == spa::type::boolean);
reinterpret_cast<spa_pod_bool *>(m_impl->pod.get())->value = value;
}

template <>
void pod::write(float value)
{
assert(type() == pod_type::num_float);
assert(type() == spa::type::num_float);
reinterpret_cast<spa_pod_float *>(m_impl->pod.get())->value = value;
}

template <>
void pod::write(std::vector<float> value)
{
assert(type() == pod_type::array);

auto *array = reinterpret_cast<spa_pod_array *>(m_impl->pod.get());

float *iter = {};
std::size_t i = 0;

SPA_POD_ARRAY_FOREACH(array, iter)
{
assert(i < value.size());
*iter = value.at(i++);
}
}

pod::raw_type *pod::get() const
{
return m_impl->pod.get();
}

const spa_type_info *pod::type_info() const
{
return m_impl->type;
}

pod::operator raw_type *() const &
{
return get();
119 changes: 0 additions & 119 deletions src/spa/spa.pod.object.body.cpp

This file was deleted.

69 changes: 69 additions & 0 deletions src/spa/spa.pod.object.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "spa/pod/object/object.hpp"
#include "spa/pod/object/iterator.hpp"

#include <spa/pod/pod.h>
#include <spa/pod/iter.h>
#include <spa/debug/pod.h>

namespace pipewire::spa
{
struct pod_object::impl
{
spa_pod_object *object;
};

pod_object::~pod_object() = default;

pod_object::pod_object(raw_type *object) : m_impl(std::make_unique<impl>())
{
m_impl->object = object;
}

pod_object::pod_object(pod_object &&other) noexcept : m_impl(std::move(other.m_impl)) {}

pod_object &pod_object::operator=(pod_object &&other) noexcept
{
m_impl = std::move(other.m_impl);
return *this;
}

spa::pod pod_object::pod() const
{
return spa::pod::view(&m_impl->object->pod);
}

spa::type pod_object::type() const
{
return static_cast<spa::type>(m_impl->object->body.type);
}

std::uint32_t pod_object::id() const
{
return m_impl->object->body.id;
}

pod_object::sentinel pod_object::end() const // NOLINT(*-static)
{
return {};
}

pod_object::iterator pod_object::begin() const
{
return {this};
}

pod_object::raw_type *pod_object::get() const
{
return m_impl->object;
}

pod_object::operator raw_type *() const &
{
return get();
}

pod_object pod_object::view(raw_type *object)
{
return {object};
}
} // namespace pipewire::spa
22 changes: 14 additions & 8 deletions src/spa/spa.pod.object.iterator.cpp
Original file line number Diff line number Diff line change
@@ -4,12 +4,15 @@

namespace pipewire::spa
{
using iterator = pod_object_body::iterator;
using iterator = pod_object::iterator;

struct iterator::impl
{
spa_pod_prop *iter;
const pod_object_body *body;
const pod_object *object;

public:
bool operator==(const impl &) const = default;
};

iterator::~iterator() = default;
@@ -21,10 +24,10 @@ namespace pipewire::spa
*m_impl = *other.m_impl;
}

iterator::iterator(const pod_object_body *body) : iterator()
iterator::iterator(const pod_object *object) : iterator()
{
m_impl->iter = spa_pod_prop_first(body->get());
m_impl->body = body;
m_impl->iter = spa_pod_prop_first(&object->get()->body);
m_impl->object = object;
}

iterator &iterator::operator=(const iterator &other)
@@ -59,16 +62,19 @@ namespace pipewire::spa

pod_prop iterator::operator*() const
{
return pod_prop::view(m_impl->iter, m_impl->body->type_info());
return pod_prop::view(m_impl->iter);
}

bool iterator::operator==(const iterator &other) const
{
return m_impl->iter == other.m_impl->iter && m_impl->body == other.m_impl->body;
return *m_impl == *other.m_impl;
}

bool iterator::operator==(const sentinel &) const
{
return !spa_pod_prop_is_inside(m_impl->body->get(), m_impl->body->size(), m_impl->iter);
auto *body = &m_impl->object->get()->body;
auto &pod = m_impl->object->get()->pod;

return !spa_pod_prop_is_inside(body, pod.size, m_impl->iter);
}
} // namespace pipewire::spa
16 changes: 3 additions & 13 deletions src/spa/spa.pod.prop.cpp
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ namespace pipewire::spa
struct pod_prop::impl
{
raw_type *prop;
const raw_info *type;
};

pod_prop::~pod_prop() = default;
@@ -44,11 +43,6 @@ namespace pipewire::spa
return pod::view(&m_impl->prop->value);
}

std::string pod_prop::name() const
{
return m_impl->type->name;
}

std::uint32_t pod_prop::key() const
{
return m_impl->prop->key;
@@ -64,21 +58,17 @@ namespace pipewire::spa
return m_impl->prop;
}

const pod_prop::raw_info *pod_prop::type_info() const
{
return m_impl->type;
}

pod_prop::operator raw_type *() const &
{
return get();
}

pod_prop pod_prop::view(raw_type *prop, const raw_info *type)
pod_prop pod_prop::view(raw_type *prop)
{
// TODO: Improve, use new pw_unique_ptr

pod_prop rtn;

rtn.m_impl->type = spa_debug_type_find(type->values, prop->key);
rtn.m_impl->prop = prop;

return rtn;

0 comments on commit 1e01d66

Please sign in to comment.