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_
25 struct 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
31  } Samples;
32  DWORD dwChannelMask; // which channels are present in stream
33  GUID SubFormat;
34 };
36 #endif
37 
39 
40 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT,
41  0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
42 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM,
43  0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
44 DEFINE_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 
94 void 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 {
145  AudioOutputSettings *settings = new AudioOutputSettings();
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);
154  settings->AddSupportedFormat(FORMAT_S16);
155 #if 0 // 24-bit integer is not supported
156  settings->AddSupportedFormat(FORMAT_S24);
157 #endif
158 #if 0 // 32-bit integer (OGG) is not supported on all platforms.
159  settings->AddSupportedFormat(FORMAT_S32);
160 #endif
161 #if 0 // 32-bit floating point (AC3) is not supported on all platforms.
162  settings->AddSupportedFormat(FORMAT_FLT);
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  VBAUDIO(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  VBAUDIO(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 
246 void 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 
257  if (m_CurrentPkt >= kPacketCnt)
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  VBERROR("WriteAudio: failed to prepare header");
276  else if (MMSYSERR_NOERROR != waveOutWrite(m_priv->m_hWaveOut, wh,
277  sizeof(WAVEHDR)))
278  VBERROR("WriteAudio: failed to write packet");
279 
280  m_CurrentPkt++;
281 }
282 
284 {
285  return m_nPkts * m_fragmentSize;
286 }
287 
288 int AudioOutputWin::GetVolumeChannel(int channel) const
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 
305 void AudioOutputWin::SetVolumeChannel(int channel, int volume)
306 {
307  if (channel > 1)
308  VBERROR("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  VBAUDIO(QString("SetVolume(%1) %2(%3)")
325  .arg(channel).arg(volume).arg(dwVolume));
326 
327  waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
328 }
FORMAT_U8
@ FORMAT_U8
Definition: audiooutputsettings.h:26
AudioOutputBase::m_enc
bool m_enc
Definition: audiooutputbase.h:190
WAVEFORMATEXTENSIBLE::SubFormat
GUID SubFormat
Definition: audiooutputdx.cpp:39
AudioOutputSettings::GetNextRate
int GetNextRate()
Definition: audiooutputsettings.cpp:67
audiooutputwin.h
AudioOutputWinPrivate::waveOutProc
static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
Definition: audiooutputwin.cpp:94
WAVEFORMATEXTENSIBLE::Samples
union WAVEFORMATEXTENSIBLE::@2 Samples
AudioOutputSettings::setPassthrough
void setPassthrough(int val)
Definition: audiooutputsettings.h:76
VBAUDIO
#define VBAUDIO(str)
Definition: audiooutputbase.h:20
AudioOutputWin::~AudioOutputWin
virtual ~AudioOutputWin()
Definition: audiooutputwin.cpp:122
AudioOutputWinPrivate::m_hWaveOut
HWAVEOUT m_hWaveOut
Definition: audiooutputwin.cpp:89
FORMAT_S16
@ FORMAT_S16
Definition: audiooutputsettings.h:27
AudioOutputSettings::AddSupportedFormat
void AddSupportedFormat(AudioFormat format)
Definition: audiooutputsettings.cpp:127
AudioOutput::Error
void Error(const QString &msg)
Definition: audiooutput.cpp:272
AudioOutputWin::CloseDevice
void CloseDevice(void) override
Definition: audiooutputwin.cpp:241
AudioOutputWin::m_UseSPDIF
bool m_UseSPDIF
Definition: audiooutputwin.h:32
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:867
AudioOutputSettings::FormatToBits
static int FormatToBits(AudioFormat format)
Definition: audiooutputsettings.cpp:150
AudioOutputWin::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool digital) override
Definition: audiooutputwin.cpp:143
WAVE_FORMAT_DOLBY_AC3_SPDIF
#define WAVE_FORMAT_DOLBY_AC3_SPDIF
Definition: audiooutputwin.cpp:17
VBERROR
#define VBERROR(str)
Definition: audiooutputbase.h:23
AudioOutputWin::GetBufferedOnSoundcard
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
Definition: audiooutputwin.cpp:283
AudioOutputWinPrivate::Close
void Close(void)
Definition: audiooutputwin.cpp:71
AudioOutputWin
Definition: audiooutputwin.h:9
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
AudioOutputWin::m_OutPkts
unsigned char ** m_OutPkts
Definition: audiooutputwin.h:31
AudioOutputBase::m_sampleRate
int m_sampleRate
Definition: audiooutputbase.h:180
AudioOutputSettings::AddSupportedChannels
void AddSupportedChannels(int channels)
Definition: audiooutputsettings.cpp:237
AudioOutputWin::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputwin.cpp:288
AudioSettings
Definition: audiosettings.h:28
AudioOutputBase::m_soundcardBufferSize
long m_soundcardBufferSize
Definition: audiooutputbase.h:183
AudioOutputBase
Definition: audiooutputbase.h:51
AudioSettings::m_init
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
mythlogging.h
FORMAT_S24
@ FORMAT_S24
Definition: audiooutputsettings.h:29
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:31
AudioOutputBase::Reconfigure
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
Definition: audiooutputbase.cpp:469
AudioOutputWinPrivate::operator=
AudioOutputWinPrivate & operator=(const AudioOutputWinPrivate &)=delete
AudioOutputSettings::AddSupportedRate
void AddSupportedRate(int rate)
Definition: audiooutputsettings.cpp:78
FORMAT_S32
@ FORMAT_S32
Definition: audiooutputsettings.h:30
uint
unsigned int uint
Definition: compat.h:81
AudioOutputBase::m_passthru
bool m_passthru
Definition: audiooutputbase.h:189
WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT
Definition: audiooutputwin.cpp:13
AudioOutputBase::m_outputFormat
AudioFormat m_outputFormat
Definition: audiooutputbase.h:179
AudioOutputWin::AudioOutputWin
AudioOutputWin(const AudioSettings &settings)
Definition: audiooutputwin.cpp:111
WAVEFORMATEXTENSIBLE::dwChannelMask
DWORD dwChannelMask
Definition: audiooutputdx.cpp:38
AudioOutputWin::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputwin.cpp:174
AudioOutputWinPrivate
Definition: audiooutputwin.cpp:47
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:174
AudioOutputWin::m_nPkts
long m_nPkts
Definition: audiooutputwin.h:29
AudioOutputWinPrivate::~AudioOutputWinPrivate
~AudioOutputWinPrivate()
Definition: audiooutputwin.cpp:57
AudioOutputBase::m_outputBytesPerFrame
int m_outputBytesPerFrame
Definition: audiooutputbase.h:177
AudioOutputWin::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputwin.cpp:305
AudioOutputSettings
Definition: audiooutputsettings.h:48
AudioOutputWinPrivate::m_hEvent
HANDLE m_hEvent
Definition: audiooutputwin.cpp:91
WAVEFORMATEXTENSIBLE::wValidBitsPerSample
WORD wValidBitsPerSample
Definition: audiooutputdx.cpp:34
AudioOutputBase::InitSettings
void InitSettings(const AudioSettings &settings)
Definition: audiooutputbase.cpp:120
AudioOutputWin::m_priv
AudioOutputWinPrivate * m_priv
Definition: audiooutputwin.h:28
AudioOutputWinPrivate::m_WaveHdrs
WAVEHDR * m_WaveHdrs
Definition: audiooutputwin.cpp:90
AudioOutputBase::m_fragmentSize
int m_fragmentSize
Definition: audiooutputbase.h:182
AudioOutputWin::m_CurrentPkt
uint m_CurrentPkt
Definition: audiooutputwin.h:30
WAVEFORMATEXTENSIBLE::wSamplesPerBlock
WORD wSamplesPerBlock
Definition: audiooutputdx.cpp:35
AudioOutputWin::WriteAudio
void WriteAudio(unsigned char *aubuf, int size) override
Definition: audiooutputwin.cpp:246
WAVE_FORMAT_EXTENSIBLE
#define WAVE_FORMAT_EXTENSIBLE
Definition: audiooutputwin.cpp:21
WAVEFORMATEXTENSIBLE
Definition: audiooutputdx.cpp:31
AudioOutputWinPrivate::AudioOutputWinPrivate
AudioOutputWinPrivate()
Definition: audiooutputwin.cpp:50
AudioOutputWin::kPacketCnt
static const uint kPacketCnt
Definition: audiooutputwin.h:34
WAVEFORMATEXTENSIBLE::wReserved
WORD wReserved
Definition: audiooutputdx.cpp:36
DEFINE_GUID
DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)
WAVEFORMATEXTENSIBLE::Format
WAVEFORMATEX Format
Definition: audiooutputdx.cpp:32