25 _handle = CreateFileW(device_name_.c_str(), FILE_SHARE_READ | FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
26 if (_handle == INVALID_HANDLE_VALUE) {
33 if (_handle != INVALID_HANDLE_VALUE) {
34 if (not CloseHandle(_handle)) {
40 [[nodiscard]] ULONG pin_count()
const noexcept
43 return wide_cast<ULONG>(get_pin_property<DWORD>(0, KSPROPERTY_PIN_CTYPES));
45 hi_log_error(
"Could not get pin-count on device {}: {}", _device_name, e.
what());
50 [[nodiscard]]
std::string pin_name(ULONG pin_nr)
const noexcept
53 return get_pin_property<std::string>(pin_nr, KSPROPERTY_PIN_NAME);
55 hi_log_error(
"Could not get pin-name on device {}: {}", _device_name, e.
what());
60 [[nodiscard]] generator<ULONG> find_streaming_pins(audio_direction direction)
const noexcept
62 auto const num_pins = pin_count();
63 for (ULONG pin_nr = 0; pin_nr != num_pins; ++pin_nr) {
64 if (is_streaming_pin(pin_nr, direction)) {
70 [[nodiscard]] GUID pin_category(ULONG pin_nr)
const noexcept
73 return get_pin_property<GUID>(0, KSPROPERTY_PIN_CATEGORY);
75 hi_log_error(
"Could not get pin-category on device {}: {}", _device_name, e.
what());
80 [[nodiscard]] KSPIN_COMMUNICATION pin_communication(ULONG pin_nr)
const noexcept
83 return get_pin_property<KSPIN_COMMUNICATION>(0, KSPROPERTY_PIN_COMMUNICATION);
85 hi_log_error(
"Could not get pin-communication on device {}: {}", _device_name, e.
what());
86 return KSPIN_COMMUNICATION_NONE;
90 [[nodiscard]] generator<audio_format_range> get_format_ranges(ULONG pin_nr)
const noexcept
92 for (
auto *format_range : get_pin_properties<KSDATARANGE>(pin_nr, KSPROPERTY_PIN_DATARANGES)) {
93 if (IsEqualGUID(format_range->MajorFormat, KSDATAFORMAT_TYPE_AUDIO)) {
95 auto has_float =
false;
96 if (IsEqualGUID(format_range->SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) {
98 }
else if (IsEqualGUID(format_range->SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
100 }
else if (IsEqualGUID(format_range->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)) {
109 auto const *format_range_ =
reinterpret_cast<KSDATARANGE_AUDIO
const *
>(format_range);
110 if (format_range_->MinimumBitsPerSample > 64) {
112 "Bad KSDATARANGE_AUDIO MinimumBitsPerSample == {} for device {}",
113 format_range_->MinimumBitsPerSample,
117 if (format_range_->MaximumBitsPerSample > 64) {
119 "Bad KSDATARANGE_AUDIO MaximumBitsPerSample == {} for device {}",
120 format_range_->MaximumBitsPerSample,
124 if (format_range_->MinimumBitsPerSample > format_range_->MaximumBitsPerSample) {
126 "Bad KSDATARANGE_AUDIO MinimumBitsPerSample == {}, MaximumBitsPerSample {} for device {}",
127 format_range_->MinimumBitsPerSample,
128 format_range_->MaximumBitsPerSample,
135 "Bad KSDATARANGE_AUDIO MaximumChannels == {} for device {}",
136 format_range_->MaximumChannels,
141 if (format_range_->MinimumSampleFrequency > format_range_->MaximumSampleFrequency) {
143 "Bad KSDATARANGE_AUDIO MinimumSampleFrequency == {}, MaximumSampleFrequency {} for device {}",
144 format_range_->MinimumSampleFrequency,
145 format_range_->MaximumSampleFrequency,
150 auto const num_bits_first = format_range_->MinimumBitsPerSample;
151 auto const num_bits_last = format_range_->MaximumBitsPerSample;
152 auto const num_channels = narrow_cast<uint16_t>(format_range_->MaximumChannels);
153 auto const min_sample_rate = narrow_cast<uint32_t>(format_range_->MinimumSampleFrequency);
154 auto const max_sample_rate = narrow_cast<uint32_t>(format_range_->MaximumSampleFrequency);
158 for (
auto num_bits = num_bits_first; num_bits <= num_bits_last; ++num_bits) {
159 auto const num_bytes = narrow_cast<uint8_t>((num_bits + 7) / 8);
161 auto const num_minor_bits = narrow_cast<uint8_t>(num_bits - 1);
162 auto const sample_format =
pcm_format{
false, std::endian::native,
true, num_bytes, 0, num_minor_bits};
164 sample_format, num_channels, min_sample_rate, max_sample_rate, surround_mode::none};
166 if (has_float and num_bits == 32) {
167 auto const sample_format =
pcm_format{
true, std::endian::native,
true, num_bytes, 8, 23};
169 sample_format, num_channels, min_sample_rate, max_sample_rate, surround_mode::none};
183 [[nodiscard]] generator<audio_format_range>
get_format_ranges(audio_direction direction)
const noexcept
185 for (
auto pin_nr : find_streaming_pins(direction)) {
186 for (
auto const& range : get_format_ranges(pin_nr)) {
193 [[nodiscard]] generator<T const *> get_pin_properties(ULONG pin_id, KSPROPERTY_PIN property)
const
195 auto r = get_pin_property_data(pin_id, property);
198 auto *header = std::launder(
reinterpret_cast<KSMULTIPLE_ITEM
const *
>(ptr));
200 auto const expected_size = header->Count *
sizeof(T) +
sizeof(KSMULTIPLE_ITEM);
201 if (header->Size != expected_size) {
202 throw io_error(
"KSMULTIPLE_ITEM header corrupt");
205 ptr +=
sizeof(KSMULTIPLE_ITEM);
206 for (
auto i = 0_uz; i != header->Count; ++i) {
207 co_yield std::launder(
reinterpret_cast<T
const *
>(ptr));
217 [[nodiscard]] generator<KSDATARANGE const *>
get_pin_properties(ULONG pin_id, KSPROPERTY_PIN property)
const
219 auto r = get_pin_property_data(pin_id, property);
222 auto *header = std::launder(
reinterpret_cast<KSMULTIPLE_ITEM
const *
>(ptr));
224 ptr +=
sizeof(KSMULTIPLE_ITEM);
225 for (
auto i = 0_uz; i != header->Count; ++i) {
226 auto *item = std::launder(
reinterpret_cast<KSDATARANGE
const *
>(ptr));
229 ptr += item->FormatSize;
237 [[nodiscard]]
std::string get_pin_property_string(ULONG pin_id, KSPROPERTY_PIN property)
const;
240 [[nodiscard]] T get_pin_property(ULONG pin_id, KSPROPERTY_PIN property)
const
242 auto property_info = KSP_PIN{};
243 property_info.Property.Set = KSPROPSETID_Pin;
244 property_info.Property.Id = property;
245 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
246 property_info.PinId = pin_id;
247 property_info.Reserved = 0;
251 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info,
sizeof(KSP_PIN), &r,
sizeof(T), &r_size, NULL)) {
255 if (r_size !=
sizeof(T)) {
256 throw io_error(
"Unexpected return size");
264 auto property_info = KSP_PIN{};
265 property_info.Property.Set = KSPROPSETID_Pin;
266 property_info.Property.Id = property;
267 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
268 property_info.PinId = pin_id;
269 property_info.Reserved = 0;
272 if (DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info,
sizeof(KSP_PIN), NULL, 0, &r_size, NULL)) {
273 throw io_error(
"Unexpected return size 0");
275 if (GetLastError() != ERROR_MORE_DATA) {
279 if (r_size <
sizeof(KSMULTIPLE_ITEM)) {
280 throw io_error(
"Unexpected return size");
283 auto r = std::make_unique<char[]>(r_size);
284 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info,
sizeof(KSP_PIN), r.get(), r_size, &r_size, NULL)) {
288 if (r_size <
sizeof(KSMULTIPLE_ITEM)) {
289 throw io_error(
"Incomplete header read");
292 auto *header = std::launder(
reinterpret_cast<KSMULTIPLE_ITEM *
>(r.get()));
293 if (r_size < header->Size) {
294 throw io_error(
"Incomplete read");
301 [[nodiscard]]
std::string get_pin_property<std::string>(ULONG pin_id, KSPROPERTY_PIN property)
const
303 auto property_info = KSP_PIN{};
304 property_info.Property.Set = KSPROPSETID_Pin;
305 property_info.Property.Id = property;
306 property_info.Property.Flags = KSPROPERTY_TYPE_GET;
307 property_info.PinId = pin_id;
308 property_info.Reserved = 0;
311 if (DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info,
sizeof(KSP_PIN), NULL, 0, &r_size, NULL)) {
315 if (GetLastError() != ERROR_MORE_DATA) {
319 if (r_size % 2 != 0) {
320 throw io_error(
"Expected even number of bytes in return value.");
323 auto r =
std::wstring(r_size /
sizeof(
wchar_t{}) - 1,
wchar_t{});
324 hi_assert(r_size == (r.size() + 1) *
sizeof(
wchar_t{}));
326 if (not DeviceIoControl(_handle, IOCTL_KS_PROPERTY, &property_info,
sizeof(KSP_PIN), r.data(), r_size, &r_size, NULL)) {
330 return hi::to_string(r);
333 [[nodiscard]]
bool is_streaming_interface(ULONG pin_nr)
const noexcept
336 for (
auto *identifier : get_pin_properties<KSIDENTIFIER>(pin_nr, KSPROPERTY_PIN_INTERFACES)) {
337 if (not IsEqualGUID(identifier->Set, KSINTERFACESETID_Standard)) {
340 if (identifier->Id == KSINTERFACE_STANDARD_STREAMING or identifier->Id == KSINTERFACE_STANDARD_LOOPED_STREAMING) {
345 hi_log_error(
"Could not get pin-interface property for {} pin_nr {}: {}", _device_name, pin_nr, e.
what());
350 [[nodiscard]]
bool is_standerdio_medium(ULONG pin_nr)
const noexcept
353 for (
auto const& identifier : get_pin_properties<KSIDENTIFIER>(pin_nr, KSPROPERTY_PIN_MEDIUMS)) {
354 if (not IsEqualGUID(identifier->Set, KSMEDIUMSETID_Standard)) {
357 if (identifier->Id == KSMEDIUM_STANDARD_DEVIO) {
362 hi_log_error(
"Could not get pin-medium property for {} pin_nr {}: {}", _device_name, pin_nr, e.
what());
367 [[nodiscard]]
bool is_streaming_pin(ULONG pin_nr, audio_direction direction)
const noexcept
370 if (not is_streaming_interface(pin_nr)) {
374 if (not is_standerdio_medium(pin_nr)) {
379 switch (get_pin_property<KSPIN_DATAFLOW>(pin_nr, KSPROPERTY_PIN_DATAFLOW)) {
380 case KSPIN_DATAFLOW_OUT:
381 if (direction != audio_direction::input and direction != audio_direction::bidirectional) {
385 case KSPIN_DATAFLOW_IN:
386 if (direction != audio_direction::output and direction != audio_direction::bidirectional) {