MythTV  master
audiooutputwin.cpp
Go to the documentation of this file.
1 #include <iostream>
2 
3 using namespace std;
4 
5 #include "mythlogging.h"
6 #include "audiooutputwin.h"
7 
8 #include <windows.h>
9 #include <mmsystem.h>
10 #include <objbase.h> // For DEFINE_GUID
11 
12 #define LOC QString("AOWin: ")
13 
14 #ifndef WAVE_FORMAT_IEEE_FLOAT
15 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
16 #endif
17 
18 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
19 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
20 #endif
21 
22 #ifndef WAVE_FORMAT_EXTENSIBLE
23 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
24 #endif
25 
26 #ifndef _WAVEFORMATEXTENSIBLE_
27 struct WAVEFORMATEXTENSIBLE {
28  WAVEFORMATEX Format;
29  union {
30  WORD wValidBitsPerSample; // bits of precision
31  WORD wSamplesPerBlock; // valid if wBitsPerSample==0
32  WORD wReserved; // If neither applies, set to zero
33  } Samples;
34  DWORD dwChannelMask; // which channels are present in stream
35  GUID SubFormat;
36 };
38 #endif
39 
41 
42 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT,
43  0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
44 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM,
45  0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF,
47  0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
48 
50 {
51  public:
53  {
54  m_WaveHdrs = new WAVEHDR[AudioOutputWin::kPacketCnt];
55  memset(m_WaveHdrs, 0, sizeof(WAVEHDR) * AudioOutputWin::kPacketCnt);
56  m_hEvent = CreateEvent(nullptr, FALSE, TRUE, nullptr);
57  }
58 
60  {
61  if (m_WaveHdrs)
62  {
63  delete[] m_WaveHdrs;
64  m_WaveHdrs = nullptr;
65  }
66  if (m_hEvent)
67  {
68  CloseHandle(m_hEvent);
69  m_hEvent = nullptr;
70  }
71  }
72 
73  void Close(void)
74  {
75  if (m_hWaveOut)
76  {
77  waveOutReset(m_hWaveOut);
78  waveOutClose(m_hWaveOut);
79  m_hWaveOut = nullptr;
80  }
81  }
82 
83  static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
84  DWORD dwParam1, DWORD dwParam2);
85 
86  private:
87  AudioOutputWinPrivate(const AudioOutputWinPrivate &) = delete; // not copyable
88  AudioOutputWinPrivate &operator=(const AudioOutputWinPrivate &) = delete; // not copyable
89 
90  public:
91  HWAVEOUT m_hWaveOut {nullptr};
92  WAVEHDR *m_WaveHdrs {nullptr};
93  HANDLE m_hEvent {nullptr};
94 };
95 
96 void CALLBACK AudioOutputWinPrivate::waveOutProc(HWAVEOUT hwo, UINT uMsg,
97  DWORD dwInstance,
98  DWORD dwParam1, 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.use_passthru)
115 {
116  InitSettings(settings);
117  if (settings.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
178  fragment_size = 50 * output_bytes_per_frame * samplerate / 1000;
179  // DirectSound buffer holds 4 fragments = 200ms worth of samples
180  soundcard_buffer_size = kPacketCnt * fragment_size;
181 
182  VBAUDIO(QString("Buffering %1 fragments of %2 bytes each, total: %3 bytes")
183  .arg(kPacketCnt).arg(fragment_size).arg(soundcard_buffer_size));
184 
185  m_UseSPDIF = passthru || enc;
186 
188  wf.Format.nChannels = channels;
189  wf.Format.nSamplesPerSec = samplerate;
190  wf.Format.nBlockAlign = output_bytes_per_frame;
191  wf.Format.nAvgBytesPerSec = samplerate * output_bytes_per_frame;
192  wf.Format.wBitsPerSample = (output_bytes_per_frame << 3) / channels;
194  AudioOutputSettings::FormatToBits(output_format);
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 (output_format == 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")
213  .arg(wf.Samples.wValidBitsPerSample).arg(channels)
214  .arg(samplerate).arg(m_UseSPDIF ? "data" : "PCM"));
215 
216  /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
217  if (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  (DWORD)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 * fragment_size;
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 }
void InitSettings(const AudioSettings &settings)
virtual ~AudioOutputWin()
void Error(const QString &msg)
AudioOutputWin(const AudioSettings &settings)
AudioOutputWinPrivate * m_priv
void setPassthrough(int val)
union WAVEFORMATEXTENSIBLE::@2 Samples
DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)
static int FormatToBits(AudioFormat format)
#define VBERROR(str)
unsigned char ** m_OutPkts
static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
AudioOutputSettings * GetOutputSettings(bool digital) override
void CloseDevice(void) override
bool OpenDevice(void) override
unsigned int uint
Definition: compat.h:140
#define WAVE_FORMAT_IEEE_FLOAT
void SetVolumeChannel(int channel, int volume) override
static const uint kPacketCnt
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void WriteAudio(unsigned char *aubuf, int size) override
void AddSupportedRate(int rate)
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
#define WAVE_FORMAT_EXTENSIBLE
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
int GetVolumeChannel(int channel) const override
void AddSupportedFormat(AudioFormat format)
void AddSupportedChannels(int channels)
#define VBAUDIO(str)
#define WAVE_FORMAT_DOLBY_AC3_SPDIF