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(HWAVEOUT hwo, UINT uMsg,
95  DWORD dwInstance,
96  DWORD dwParam1, DWORD dwParam2)
97 {
98  Q_UNUSED(hwo);
99  Q_UNUSED(dwParam1);
100  Q_UNUSED(dwParam2);
101 
102  if (uMsg != WOM_DONE)
103  return;
104 
105  AudioOutputWin *instance = reinterpret_cast<AudioOutputWin*>(dwInstance);
106  InterlockedDecrement(&instance->m_nPkts);
107  if (instance->m_nPkts < (int)AudioOutputWin::kPacketCnt)
108  {
109  SetEvent(instance->m_priv->m_hEvent);
110  }
111 }
112 
114  AudioOutputBase(settings),
115  m_priv(new AudioOutputWinPrivate()),
116  m_UseSPDIF(settings.m_usePassthru)
117 {
118  InitSettings(settings);
119  if (settings.m_init)
120  Reconfigure(settings);
121  m_OutPkts = (unsigned char**) calloc(kPacketCnt, sizeof(unsigned char*));
122 }
123 
125 {
126  KillAudio();
127 
128  if (m_priv)
129  {
130  delete m_priv;
131  m_priv = nullptr;
132  }
133 
134  if (m_OutPkts)
135  {
136  for (uint i = 0; i < kPacketCnt; i++)
137  if (m_OutPkts[i])
138  free(m_OutPkts[i]);
139 
140  free(m_OutPkts);
141  m_OutPkts = nullptr;
142  }
143 }
144 
146 {
147  AudioOutputSettings *settings = new AudioOutputSettings();
148 
149  // We use WAVE_MAPPER to find a compatible device, so just claim support
150  // for all of the standard rates
151  while (DWORD rate = (DWORD)settings->GetNextRate())
152  settings->AddSupportedRate(rate);
153 
154  // Support all standard formats
155  settings->AddSupportedFormat(FORMAT_U8);
156  settings->AddSupportedFormat(FORMAT_S16);
157 #if 0 // 24-bit integer is not supported
158  settings->AddSupportedFormat(FORMAT_S24);
159 #endif
160 #if 0 // 32-bit integer (OGG) is not supported on all platforms.
161  settings->AddSupportedFormat(FORMAT_S32);
162 #endif
163 #if 0 // 32-bit floating point (AC3) is not supported on all platforms.
164  settings->AddSupportedFormat(FORMAT_FLT);
165 #endif
166 
167  // Guess that we can do up to 5.1
168  for (uint i = 2; i < 7; i++)
169  settings->AddSupportedChannels(i);
170 
171  settings->setPassthrough(0); //Maybe passthrough
172 
173  return settings;
174 }
175 
177 {
178  CloseDevice();
179  // fragments are 50ms worth of samples
181  // DirectSound buffer holds 4 fragments = 200ms worth of samples
183 
184  VBAUDIO(QString("Buffering %1 fragments of %2 bytes each, total: %3 bytes")
186 
188 
190  wf.Format.nChannels = m_channels;
191  wf.Format.nSamplesPerSec = m_sampleRate;
192  wf.Format.nBlockAlign = m_outputBytesPerFrame;
193  wf.Format.nAvgBytesPerSec = m_sampleRate * m_outputBytesPerFrame;
194  wf.Format.wBitsPerSample = (m_outputBytesPerFrame << 3) / m_channels;
197 
198  if (m_UseSPDIF)
199  {
200  wf.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
201  wf.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
202  }
203  else if (m_outputFormat == FORMAT_FLT)
204  {
205  wf.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
206  wf.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
207  }
208  else
209  {
210  wf.Format.wFormatTag = WAVE_FORMAT_PCM;
211  wf.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
212  }
213 
214  VBAUDIO(QString("New format: %1bits %2ch %3Hz %4")
216  .arg(m_sampleRate).arg(m_UseSPDIF ? "data" : "PCM"));
217 
218  /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
219  if (m_channels <= 2)
220  wf.Format.cbSize = 0;
221  else
222  {
223  wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
224  wf.dwChannelMask = 0x003F; // 0x003F = 5.1 channels
225  wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
226  }
227 
228  MMRESULT mmr = waveOutOpen(&m_priv->m_hWaveOut, WAVE_MAPPER,
229  (WAVEFORMATEX *)&wf,
231  (intptr_t)this, CALLBACK_FUNCTION);
232 
233  if (mmr == WAVERR_BADFORMAT)
234  {
235  Error(QString("Unable to set audio output parameters %1")
236  .arg(wf.Format.nSamplesPerSec));
237  return false;
238  }
239 
240  return true;
241 }
242 
244 {
245  m_priv->Close();
246 }
247 
248 void AudioOutputWin::WriteAudio(unsigned char * buffer, int size)
249 {
250  if (!size)
251  return;
252 
253  if (InterlockedIncrement(&m_nPkts) > (int)kPacketCnt)
254  {
255  while (m_nPkts > (int)kPacketCnt)
256  WaitForSingleObject(m_priv->m_hEvent, INFINITE);
257  }
258 
259  if (m_CurrentPkt >= kPacketCnt)
260  m_CurrentPkt = 0;
261 
262  WAVEHDR *wh = &m_priv->m_WaveHdrs[m_CurrentPkt];
263  if (wh->dwFlags & WHDR_PREPARED)
264  waveOutUnprepareHeader(m_priv->m_hWaveOut, wh, sizeof(WAVEHDR));
265 
267  (unsigned char*)realloc(m_OutPkts[m_CurrentPkt], size);
268 
269  memcpy(m_OutPkts[m_CurrentPkt], buffer, size);
270 
271  memset(wh, 0, sizeof(WAVEHDR));
272  wh->lpData = (LPSTR)m_OutPkts[m_CurrentPkt];
273  wh->dwBufferLength = size;
274 
275  if (MMSYSERR_NOERROR != waveOutPrepareHeader(m_priv->m_hWaveOut, wh,
276  sizeof(WAVEHDR)))
277  VBERROR("WriteAudio: failed to prepare header");
278  else if (MMSYSERR_NOERROR != waveOutWrite(m_priv->m_hWaveOut, wh,
279  sizeof(WAVEHDR)))
280  VBERROR("WriteAudio: failed to write packet");
281 
282  m_CurrentPkt++;
283 }
284 
286 {
287  return m_nPkts * m_fragmentSize;
288 }
289 
290 int AudioOutputWin::GetVolumeChannel(int channel) const
291 {
292  DWORD dwVolume = 0xffffffff;
293  int Volume = 100;
294  if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
295  {
296  Volume = (channel == 0) ?
297  (LOWORD(dwVolume) / (0xffff / 100)) :
298  (HIWORD(dwVolume) / (0xffff / 100));
299  }
300 
301  LOG(VB_AUDIO, LOG_INFO, QString("GetVolume(%1) %2 (%3)")
302  .arg(channel).arg(Volume).arg(dwVolume));
303 
304  return Volume;
305 }
306 
307 void AudioOutputWin::SetVolumeChannel(int channel, int volume)
308 {
309  if (channel > 1)
310  VBERROR("Windows volume only supports stereo!");
311 
312  DWORD dwVolume = 0xffffffff;
313  if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
314  {
315  if (channel == 0)
316  dwVolume = (dwVolume & 0xffff0000) | (volume * (0xffff / 100));
317  else
318  dwVolume = (dwVolume & 0xffff) | ((volume * (0xffff / 100)) << 16);
319  }
320  else
321  {
322  dwVolume = volume * (0xffff / 100);
323  dwVolume |= (dwVolume << 16);
324  }
325 
326  VBAUDIO(QString("SetVolume(%1) %2(%3)")
327  .arg(channel).arg(volume).arg(dwVolume));
328 
329  waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
330 }
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:124
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:275
AudioOutputWin::CloseDevice
void CloseDevice(void) override
Definition: audiooutputwin.cpp:243
AudioOutputWin::m_UseSPDIF
bool m_UseSPDIF
Definition: audiooutputwin.h:32
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:868
AudioOutputSettings::FormatToBits
static int FormatToBits(AudioFormat format)
Definition: audiooutputsettings.cpp:150
AudioOutputWin::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool digital) override
Definition: audiooutputwin.cpp:145
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:285
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:290
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:474
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:79
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:113
WAVEFORMATEXTENSIBLE::dwChannelMask
DWORD dwChannelMask
Definition: audiooutputdx.cpp:38
AudioOutputWin::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputwin.cpp:176
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:307
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:125
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:248
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