HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
win32_device_interface.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "audio_direction.hpp"
8#include "audio_format_range.hpp"
9#include "../generator.hpp"
10#include "../utility/module.hpp"
11#include "../log.hpp"
12#include <string>
13
14namespace hi::inline v1 {
15
17public:
20
21 [[nodiscard]] ULONG pin_count() const noexcept;
22 [[nodiscard]] std::string pin_name(ULONG pin_nr) const noexcept;
23
24 [[nodiscard]] generator<ULONG> find_streaming_pins(audio_direction direction) const noexcept;
25
26 [[nodiscard]] GUID pin_category(ULONG pin_nr) const noexcept;
27
28 [[nodiscard]] KSPIN_COMMUNICATION pin_communication(ULONG pin_nr) const noexcept;
29
30 [[nodiscard]] generator<audio_format_range> get_format_ranges(ULONG pin_nr) const noexcept;
31
39 [[nodiscard]] generator<audio_format_range> get_format_ranges(audio_direction direction) const noexcept;
40
41 template<typename T>
42 [[nodiscard]] generator<T const *> get_pin_properties(ULONG pin_id, KSPROPERTY_PIN property) const
43 {
44 auto r = get_pin_property_data(pin_id, property);
45 auto *ptr = r.get();
46
47 auto *header = std::launder(reinterpret_cast<KSMULTIPLE_ITEM const *>(ptr));
48
49 hilet expected_size = header->Count * sizeof(T) + sizeof(KSMULTIPLE_ITEM);
50 if (header->Size != expected_size) {
51 throw io_error("KSMULTIPLE_ITEM header corrupt");
52 }
53
54 ptr += sizeof(KSMULTIPLE_ITEM);
55 for (auto i = 0_uz; i != header->Count; ++i) {
56 co_yield std::launder(reinterpret_cast<T const *>(ptr));
57 ptr += sizeof(T);
58 }
59 }
60
65 template<>
66 [[nodiscard]] generator<KSDATARANGE const *> get_pin_properties(ULONG pin_id, KSPROPERTY_PIN property) const
67 {
68 auto r = get_pin_property_data(pin_id, property);
69 auto *ptr = r.get();
70
71 auto *header = std::launder(reinterpret_cast<KSMULTIPLE_ITEM const *>(ptr));
72
73 ptr += sizeof(KSMULTIPLE_ITEM);
74 for (auto i = 0_uz; i != header->Count; ++i) {
75 auto *item = std::launder(reinterpret_cast<KSDATARANGE const *>(ptr));
76 co_yield item;
77
78 ptr += item->FormatSize;
79 }
80 }
81
82private:
83 std::string _device_name;
84 HANDLE _handle;
85
86 [[nodiscard]] std::string get_pin_property_string(ULONG pin_id, KSPROPERTY_PIN property) const;
87
88 template<typename T>
89 [[nodiscard]] T get_pin_property(ULONG pin_id, KSPROPERTY_PIN property) const
90 {
91 auto property_info = KSP_PIN{};
92 property_info.Property.Set = KSPROPSETID_Pin;
93 property_info.Property.Id = property;
94 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
95 property_info.PinId = pin_id;
96 property_info.Reserved = 0;
97
98 DWORD r_size;
99 T r;
100 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info, sizeof(KSP_PIN), &r, sizeof(T), &r_size, NULL)) {
101 throw io_error(get_last_error_message());
102 }
103
104 if (r_size != sizeof(T)) {
105 throw io_error("Unexpected return size");
106 }
107
108 return r;
109 }
110
111 std::unique_ptr<char[]> get_pin_property_data(ULONG pin_id, KSPROPERTY_PIN property) const
112 {
113 auto property_info = KSP_PIN{};
114 property_info.Property.Set = KSPROPSETID_Pin;
115 property_info.Property.Id = property;
116 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
117 property_info.PinId = pin_id;
118 property_info.Reserved = 0;
119
120 DWORD r_size;
121 if (DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info, sizeof(KSP_PIN), NULL, 0, &r_size, NULL)) {
122 throw io_error("Unexpected return size 0");
123 }
124 if (GetLastError() != ERROR_MORE_DATA) {
125 throw io_error(get_last_error_message());
126 }
127
128 if (r_size < sizeof(KSMULTIPLE_ITEM)) {
129 throw io_error("Unexpected return size");
130 }
131
132 auto r = std::make_unique<char[]>(r_size);
133 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info, sizeof(KSP_PIN), r.get(), r_size, &r_size, NULL)) {
134 throw io_error(get_last_error_message());
135 }
136
137 if (r_size < sizeof(KSMULTIPLE_ITEM)) {
138 throw io_error("Incomplete header read");
139 }
140
141 auto *header = std::launder(reinterpret_cast<KSMULTIPLE_ITEM *>(r.get()));
142 if (r_size < header->Size) {
143 throw io_error("Incomplete read");
144 }
145
146 return r;
147 }
148
149 template<>
150 [[nodiscard]] std::string get_pin_property<std::string>(ULONG pin_id, KSPROPERTY_PIN property) const
151 {
152 auto property_info = KSP_PIN{};
153 property_info.Property.Set = KSPROPSETID_Pin;
154 property_info.Property.Id = property;
155 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
156 property_info.PinId = pin_id;
157 property_info.Reserved = 0;
158
159 DWORD r_size;
160 if (DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info, sizeof(KSP_PIN), NULL, 0, &r_size, NULL)) {
161 return std::string{};
162 }
163
164 if (GetLastError() != ERROR_MORE_DATA) {
165 throw io_error(get_last_error_message());
166 }
167
168 if (r_size % 2 != 0) {
169 throw io_error("Expected even number of bytes in return value.");
170 }
171
172 auto r = std::wstring(r_size / sizeof(wchar_t{}) - 1, wchar_t{});
173 hi_assert(r_size == (r.size() + 1) * sizeof(wchar_t{}));
174
175 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info, sizeof(KSP_PIN), r.data(), r_size, &r_size, NULL)) {
176 throw io_error(get_last_error_message());
177 }
178
179 return hi::to_string(r);
180 }
181
182 [[nodiscard]] bool is_streaming_interface(ULONG pin_nr) const noexcept;
183
184 [[nodiscard]] bool is_standerdio_medium(ULONG pin_nr) const noexcept;
185
186 [[nodiscard]] bool is_streaming_pin(ULONG pin_nr, audio_direction direction) const noexcept;
187};
188
189} // namespace hi::inline v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
constexpr std::string to_string(std::u32string_view rhs) noexcept
Conversion from UTF-32 to UTF-8.
Definition to_string.hpp:215
DOXYGEN BUG.
Definition algorithm.hpp:13
std::string get_last_error_message() noexcept
Get the OS error message from the last error received on this thread.
Definition win32_device_interface.hpp:16
generator< audio_format_range > get_format_ranges(audio_direction direction) const noexcept
Get all the audio formats supported by this device.
generator< KSDATARANGE const * > get_pin_properties(ULONG pin_id, KSPROPERTY_PIN property) const
Definition win32_device_interface.hpp:66
A return value for a generator-function.
Definition generator.hpp:29