MythTV master
audiooutputwin.cpp
Go to the documentation of this file.
1#include <iostream>
2
4#include "audiooutputwin.h"
5
6#include <windows.h>
7#include <mmsystem.h>
8#include <objbase.h> // For DEFINE_GUID
9
10#define LOC QString("AOWin: ")
11
12#ifndef WAVE_FORMAT_IEEE_FLOAT
13#define WAVE_FORMAT_IEEE_FLOAT 0x0003
14#endif
15
16#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
17#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
18#endif
19
20#ifndef WAVE_FORMAT_EXTENSIBLE
21#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
22#endif
23
24#ifndef _WAVEFORMATEXTENSIBLE_
26 WAVEFORMATEX Format;
27 union {
28 WORD wValidBitsPerSample; // bits of precision
29 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
30 WORD wReserved; // If neither applies, set to zero
32 DWORD dwChannelMask; // which channels are present in stream
33 GUID SubFormat;
34};
36#endif
37
39
40DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT,
41 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
42DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM,
43 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
44DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF,
45 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46
48{
49 public:
51 {
53 memset(m_WaveHdrs, 0, sizeof(WAVEHDR) * AudioOutputWin::kPacketCnt);
54 m_hEvent = CreateEvent(nullptr, FALSE, TRUE, nullptr);
55 }
56
58 {
59 if (m_WaveHdrs)
60 {
61 delete[] m_WaveHdrs;
62 m_WaveHdrs = nullptr;
63 }
64 if (m_hEvent)
65 {
66 CloseHandle(m_hEvent);
67 m_hEvent = nullptr;
68 }
69 }
70
71 void Close(void)
72 {
73 if (m_hWaveOut)
74 {
75 waveOutReset(m_hWaveOut);
76 waveOutClose(m_hWaveOut);
77 m_hWaveOut = nullptr;
78 }
79 }
80
81 static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
82 DWORD dwParam1, DWORD dwParam2);
83
84 private:
85 AudioOutputWinPrivate(const AudioOutputWinPrivate &) = delete; // not copyable
86 AudioOutputWinPrivate &operator=(const AudioOutputWinPrivate &) = delete; // not copyable
87
88 public:
89 HWAVEOUT m_hWaveOut {nullptr};
90 WAVEHDR *m_WaveHdrs {nullptr};
91 HANDLE m_hEvent {nullptr};
92};
93
94void CALLBACK AudioOutputWinPrivate::waveOutProc([[maybe_unused]] HWAVEOUT hwo,
95 UINT uMsg,
96 DWORD dwInstance,
97 [[maybe_unused]] DWORD dwParam1,
98 [[maybe_unused]] DWORD dwParam2)
99{
100 if (uMsg != WOM_DONE)
101 return;
102
103 AudioOutputWin *instance = reinterpret_cast<AudioOutputWin*>(dwInstance);
104 InterlockedDecrement(&instance->m_nPkts);
105 if (instance->m_nPkts < (int)AudioOutputWin::kPacketCnt)
106 {
107 SetEvent(instance->m_priv->m_hEvent);
108 }
109}
110
112 AudioOutputBase(settings),
113 m_priv(new AudioOutputWinPrivate()),
114 m_UseSPDIF(settings.m_usePassthru)
115{
116 InitSettings(settings);
117 if (settings.m_init)
118 Reconfigure(settings);
119 m_OutPkts = (unsigned char**) calloc(kPacketCnt, sizeof(unsigned char*));
120}
121
123{
124 KillAudio();
125
126 if (m_priv)
127 {
128 delete m_priv;
129 m_priv = nullptr;
130 }
131
132 if (m_OutPkts)
133 {
134 for (uint i = 0; i < kPacketCnt; i++)
135 if (m_OutPkts[i])
136 free(m_OutPkts[i]);
137
138 free(m_OutPkts);
139 m_OutPkts = nullptr;
140 }
141}
142
144{
146
147 // We use WAVE_MAPPER to find a compatible device, so just claim support
148 // for all of the standard rates
149 while (DWORD rate = (DWORD)settings->GetNextRate())
150 settings->AddSupportedRate(rate);
151
152 // Support all standard formats
153 settings->AddSupportedFormat(FORMAT_U8);
155#if 0 // 24-bit integer is not supported
157#endif
158#if 0 // 32-bit integer (OGG) is not supported on all platforms.
160#endif
161#if 0 // 32-bit floating point (AC3) is not supported on all platforms.
163#endif
164
165 // Guess that we can do up to 5.1
166 for (uint i = 2; i < 7; i++)
167 settings->AddSupportedChannels(i);
168
169 settings->setPassthrough(0); //Maybe passthrough
170
171 return settings;
172}
173
175{
176 CloseDevice();
177 // fragments are 50ms worth of samples
179 // DirectSound buffer holds 4 fragments = 200ms worth of samples
181
182 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Buffering %1 fragments of %2 bytes each, total: %3 bytes")
184
186
188 wf.Format.nChannels = m_channels;
189 wf.Format.nSamplesPerSec = m_sampleRate;
190 wf.Format.nBlockAlign = m_outputBytesPerFrame;
191 wf.Format.nAvgBytesPerSec = m_sampleRate * m_outputBytesPerFrame;
192 wf.Format.wBitsPerSample = (m_outputBytesPerFrame << 3) / m_channels;
195
196 if (m_UseSPDIF)
197 {
198 wf.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
199 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
200 }
201 else if (m_outputFormat == FORMAT_FLT)
202 {
203 wf.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
204 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
205 }
206 else
207 {
208 wf.Format.wFormatTag = WAVE_FORMAT_PCM;
209 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
210 }
211
212 LOG(VB_AUDIO, LOG_INFO, LOC + QString("New format: %1bits %2ch %3Hz %4")
214 .arg(m_sampleRate).arg(m_UseSPDIF ? "data" : "PCM"));
215
216 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
217 if (m_channels <= 2)
218 wf.Format.cbSize = 0;
219 else
220 {
221 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
222 wf.dwChannelMask = 0x003F; // 0x003F = 5.1 channels
223 wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
224 }
225
226 MMRESULT mmr = waveOutOpen(&m_priv->m_hWaveOut, WAVE_MAPPER,
227 (WAVEFORMATEX *)&wf,
229 (intptr_t)this, CALLBACK_FUNCTION);
230
231 if (mmr == WAVERR_BADFORMAT)
232 {
233 Error(QString("Unable to set audio output parameters %1")
234 .arg(wf.Format.nSamplesPerSec));
235 return false;
236 }
237
238 return true;
239}
240
242{
243 m_priv->Close();
244}
245
246void AudioOutputWin::WriteAudio(unsigned char * buffer, int size)
247{
248 if (!size)
249 return;
250
251 if (InterlockedIncrement(&m_nPkts) > (int)kPacketCnt)
252 {
253 while (m_nPkts > (int)kPacketCnt)
254 WaitForSingleObject(m_priv->m_hEvent, INFINITE);
255 }
256
258 m_CurrentPkt = 0;
259
260 WAVEHDR *wh = &m_priv->m_WaveHdrs[m_CurrentPkt];
261 if (wh->dwFlags & WHDR_PREPARED)
262 waveOutUnprepareHeader(m_priv->m_hWaveOut, wh, sizeof(WAVEHDR));
263
265 (unsigned char*)realloc(m_OutPkts[m_CurrentPkt], size);
266
267 memcpy(m_OutPkts[m_CurrentPkt], buffer, size);
268
269 memset(wh, 0, sizeof(WAVEHDR));
270 wh->lpData = (LPSTR)m_OutPkts[m_CurrentPkt];
271 wh->dwBufferLength = size;
272
273 if (MMSYSERR_NOERROR != waveOutPrepareHeader(m_priv->m_hWaveOut, wh,
274 sizeof(WAVEHDR)))
275 LOG(VB_GENERAL, LOG_ERR, LOC + "WriteAudio: failed to prepare header");
276 else if (MMSYSERR_NOERROR != waveOutWrite(m_priv->m_hWaveOut, wh,
277 sizeof(WAVEHDR)))
278 LOG(VB_GENERAL, LOG_ERR, LOC + "WriteAudio: failed to write packet");
279
280 m_CurrentPkt++;
281}
282
284{
285 return m_nPkts * m_fragmentSize;
286}
287
289{
290 DWORD dwVolume = 0xffffffff;
291 int Volume = 100;
292 if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
293 {
294 Volume = (channel == 0) ?
295 (LOWORD(dwVolume) / (0xffff / 100)) :
296 (HIWORD(dwVolume) / (0xffff / 100));
297 }
298
299 LOG(VB_AUDIO, LOG_INFO, QString("GetVolume(%1) %2 (%3)")
300 .arg(channel).arg(Volume).arg(dwVolume));
301
302 return Volume;
303}
304
305void AudioOutputWin::SetVolumeChannel(int channel, int volume)
306{
307 if (channel > 1)
308 LOG(VB_GENERAL, LOG_ERR, LOC + "Windows volume only supports stereo!");
309
310 DWORD dwVolume = 0xffffffff;
311 if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
312 {
313 if (channel == 0)
314 dwVolume = (dwVolume & 0xffff0000) | (volume * (0xffff / 100));
315 else
316 dwVolume = (dwVolume & 0xffff) | ((volume * (0xffff / 100)) << 16);
317 }
318 else
319 {
320 dwVolume = volume * (0xffff / 100);
321 dwVolume |= (dwVolume << 16);
322 }
323
324 LOG(VB_AUDIO, LOG_INFO, LOC + QString("SetVolume(%1) %2(%3)")
325 .arg(channel).arg(volume).arg(dwVolume));
326
327 waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
328}
@ FORMAT_U8
@ FORMAT_S32
@ FORMAT_S24
@ FORMAT_FLT
@ FORMAT_S16
DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)
#define LOC
#define WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_EXTENSIBLE
#define WAVE_FORMAT_DOLBY_AC3_SPDIF
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
AudioFormat m_outputFormat
void InitSettings(const AudioSettings &settings)
void AddSupportedRate(int rate)
void setPassthrough(int val)
static int FormatToBits(AudioFormat format)
void AddSupportedChannels(int channels)
void AddSupportedFormat(AudioFormat format)
AudioOutputWinPrivate & operator=(const AudioOutputWinPrivate &)=delete
static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
AudioOutputWinPrivate(const AudioOutputWinPrivate &)=delete
void SetVolumeChannel(int channel, int volume) override
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
AudioOutputWinPrivate * m_priv
static const uint kPacketCnt
bool OpenDevice(void) override
AudioOutputSettings * GetOutputSettings(bool digital) override
virtual ~AudioOutputWin()
int GetVolumeChannel(int channel) const override
unsigned char ** m_OutPkts
void CloseDevice(void) override
AudioOutputWin(const AudioSettings &settings)
void WriteAudio(unsigned char *aubuf, int size) override
void Error(const QString &msg)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
unsigned int uint
Definition: freesurround.h:24
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
union WAVEFORMATEXTENSIBLE::@2 Samples