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

Verified

This commit was signed with the committer’s verified signature.
Curve Noah
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.