HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
audio_stream_format_win32.hpp
1// Copyright Take Vos 2021.
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_stream_format.hpp"
8#include "speaker_mapping_win32.hpp"
9#include "../macros.hpp"
10#include "../win32_headers.hpp"
11
12hi_export_module(hikogui.audio.audio_stream_format_win32);
13
14hi_export namespace hi { inline namespace v1 {
15
16hi_export [[nodiscard]] inline bool win32_use_extensible(audio_stream_format x) noexcept{
17 if (to_bool(x.speaker_mapping)) {
18 // If we have an non-direct speaker mapping we MUST use extensible as it requires the
19 // extra dwChannelMask field.
20 return true;
21 }
22
23 return false;
24}
25
26hi_export [[nodiscard]] inline WAVEFORMATEXTENSIBLE audio_stream_format_to_win32(audio_stream_format stream_format, bool extensible) noexcept{
27 hi_axiom(stream_format.holds_invariant());
28 hi_axiom(not win32_use_extensible(stream_format) or extensible);
29
30 auto r = WAVEFORMATEXTENSIBLE{};
31
32 if (extensible) {
33 r.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
34 // difference between sizeof(WAVEFORMATEXT) and sizeof(WAVEFORMATEXTENSIBLE).
35 // But API documentation says it must be "22".
36 r.Format.cbSize = 22;
37 } else {
38 r.Format.wFormatTag = stream_format.format.floating_point() ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
39 r.Format.cbSize = 0;
40 }
41
42 // These are the fields of WAVEFORMATEXT.
43 r.Format.nChannels = narrow_cast<WORD>(stream_format.num_channels);
44 r.Format.nSamplesPerSec = narrow_cast<DWORD>(stream_format.sample_rate);
45 r.Format.nAvgBytesPerSec = narrow_cast<DWORD>(stream_format.sample_rate * stream_format.num_channels * stream_format.format.num_bytes());
46 r.Format.nBlockAlign = narrow_cast<WORD>(stream_format.num_channels * stream_format.format.num_bytes());
47 r.Format.wBitsPerSample = narrow_cast<WORD>(stream_format.format.num_bytes() * 8);
48
49 // These are the fields of WAVEFORMATEXTENSIBLE which are ignored for WAVEFORMATEXT.
50 r.Samples.wValidBitsPerSample = narrow_cast<WORD>(stream_format.format.num_bits());
51 r.dwChannelMask = speaker_mapping_to_win32(stream_format.speaker_mapping);
52 r.SubFormat = stream_format.format.floating_point() ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
53 return r;
54}
55
56hi_export [[nodiscard]] inline audio_stream_format audio_stream_format_from_win32(WAVEFORMATEXTENSIBLE const& wave_format){
57 auto r = audio_stream_format{};
58
59 hi_check(wave_format.Format.wBitsPerSample % 8 == 0, "wBitsPerSample is not multiple of 8");
60 hi_check(wave_format.Format.wBitsPerSample > 0, "wBitsPerSample is 0");
61 hi_check(wave_format.Format.wBitsPerSample <= 32, "wBitsPerSample is more than 32");
62 hi_check(wave_format.Samples.wValidBitsPerSample > 0, "wValidBitsPerSample is 0");
63 hi_check(
64 wave_format.Samples.wValidBitsPerSample <= wave_format.Format.wBitsPerSample, "wValidBitsPerSample > wBitsPerSample");
65 hi_check(wave_format.Format.nSamplesPerSec > 0, "nSamplesPerSec is zero");
66 hi_check(wave_format.Format.nSamplesPerSec <= std::numeric_limits<uint32_t>::max(), "nSamplesPerSec is to high");
67
68 if (wave_format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
69 hi_check(wave_format.Format.wBitsPerSample == 32, "wBitsPerSample is not 32");
70 r.format = pcm_format{true, std::endian::native, true, 4, 8, 23};
71
72 } else if (wave_format.SubFormat == KSDATAFORMAT_SUBTYPE_PCM) {
73 auto const num_bytes = narrow_cast<uint8_t>(wave_format.Format.wBitsPerSample / 8);
74 auto const num_minor_bits = narrow_cast<uint8_t>(wave_format.Samples.wValidBitsPerSample - 1);
75 r.format = pcm_format{false, std::endian::native, true, num_bytes, 0, num_minor_bits};
76 } else {
77 throw parse_error("Unknown SubFormat");
78 }
79
80 hi_check(wave_format.Format.nChannels > 0, "nChannels is zero");
81 r.num_channels = wave_format.Format.nChannels;
82
83 r.speaker_mapping = speaker_mapping_from_win32(wave_format.dwChannelMask);
84 hi_check(popcount(r.speaker_mapping) == 0 or popcount(r.speaker_mapping) == r.num_channels, "nChannels is zero");
85
86 r.sample_rate = narrow_cast<uint32_t>(wave_format.Format.nSamplesPerSec);
87 return r;
88}
89
90hi_export [[nodiscard]] inline audio_stream_format audio_stream_format_from_win32(WAVEFORMATEX const& wave_format){
91 auto r = audio_stream_format{};
92 hi_check(wave_format.wBitsPerSample > 0, "wBitsPerSample is zero");
93 hi_check(wave_format.wBitsPerSample % 8 == 0, "wBitsPerSample is not multiple of 8");
94 hi_check(wave_format.wBitsPerSample <= 32, "wBitsPerSample greater than 32");
95 hi_check(wave_format.nSamplesPerSec > 0, "nSamplesPerSec is zero");
96 hi_check(wave_format.nSamplesPerSec <= std::numeric_limits<uint32_t>::max(), "nSamplesPerSec is to high");
97 hi_check(wave_format.nChannels > 0, "nChannels is zero");
98
99 if (wave_format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
100 if (wave_format.cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) {
101 throw parse_error(std::format("WAVEFORMATEXTENSIBLE has incorrect size {}", wave_format.cbSize));
102 }
103 return audio_stream_format_from_win32(*std::launder(reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(&wave_format)));
104
105 } else if (wave_format.wFormatTag == WAVE_FORMAT_PCM) {
106 hi_check(wave_format.wBitsPerSample == 32, "wBitsPerSample is not 32");
107 r.format = pcm_format{true, std::endian::native, true, 4, 8, 23};
108
109 } else if (wave_format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
110 auto const num_bytes = narrow_cast<uint8_t>(wave_format.wBitsPerSample / 8);
111 auto const num_minor_bits = narrow_cast<uint8_t>(wave_format.wBitsPerSample);
112 r.format = pcm_format{false, std::endian::native, true, num_bytes, 0, num_minor_bits};
113
114 } else {
115 throw parse_error(std::format("Unsupported wFormatTag {}", wave_format.wFormatTag));
116 }
117
118 r.sample_rate = narrow_cast<uint32_t>(wave_format.nSamplesPerSec);
119
120 hi_check(wave_format.nChannels > 0, "nChannels is zero");
121 r.num_channels = wave_format.nChannels;
122
123 r.speaker_mapping = speaker_mapping::none;
124 return r;
125}
126
127}} // namespace hi::inline v1
Rules for working with win32 headers.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20