00001 #include <iostream>
00002
00003
00004 using namespace std;
00005
00006 #include "mythcontext.h"
00007 #include "audiooutputwin.h"
00008
00009 #include <windows.h>
00010 #include <mmsystem.h>
00011
00012
00013 const uint AudioOutputWin::kPacketCnt = 16;
00014
00015 class AudioOutputWinPrivate
00016 {
00017 public:
00018 AudioOutputWinPrivate() :
00019 m_WaveHdrs(NULL), m_hEvent(NULL)
00020 {
00021 m_WaveHdrs = new WAVEHDR[AudioOutputWin::kPacketCnt];
00022 memset(m_WaveHdrs, 0, sizeof(WAVEHDR) * AudioOutputWin::kPacketCnt);
00023 m_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
00024 }
00025
00026 ~AudioOutputWinPrivate()
00027 {
00028 if (m_WaveHdrs)
00029 {
00030 delete[] m_WaveHdrs;
00031 m_WaveHdrs = NULL;
00032 }
00033 if (m_hEvent)
00034 {
00035 CloseHandle(m_hEvent);
00036 m_hEvent = NULL;
00037 }
00038 }
00039
00040 void Close(void)
00041 {
00042 if (m_hWaveOut)
00043 {
00044 waveOutReset(m_hWaveOut);
00045 waveOutClose(m_hWaveOut);
00046 m_hWaveOut = NULL;
00047 }
00048 }
00049
00050 static void CALLBACK waveOutProc(
00051 HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1,
00052 DWORD dwParam2);
00053
00054 public:
00055 HWAVEOUT m_hWaveOut;
00056 WAVEHDR *m_WaveHdrs;
00057 HANDLE m_hEvent;
00058 };
00059
00060 void CALLBACK AudioOutputWinPrivate::waveOutProc(
00061 HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
00062 {
00063 if (uMsg == WOM_DONE)
00064 {
00065 InterlockedDecrement(&((AudioOutputWin*)dwInstance)->m_nPkts);
00066 if (((AudioOutputWin*)dwInstance)->m_nPkts <
00067 (int)AudioOutputWin::kPacketCnt)
00068 {
00069 SetEvent(((AudioOutputWin*)dwInstance)->m_priv->m_hEvent);
00070 }
00071 }
00072 }
00073
00074 AudioOutputWin::AudioOutputWin(
00075 QString laudio_main_device, QString laudio_passthru_device,
00076 int laudio_bits, int laudio_channels,
00077 int laudio_samplerate, AudioOutputSource lsource,
00078 bool lset_initial_vol, bool laudio_passthru) :
00079 AudioOutputBase(laudio_main_device, laudio_passthru_device,
00080 laudio_bits, laudio_channels,
00081 laudio_samplerate, lsource,
00082 lset_initial_vol, laudio_passthru),
00083 m_priv(new AudioOutputWinPrivate()),
00084 m_nPkts(0),
00085 m_CurrentPkt(0),
00086 m_OutPkts(NULL)
00087 {
00088 Reconfigure(laudio_bits, laudio_channels,
00089 laudio_samplerate, laudio_passthru);
00090
00091 m_OutPkts = (unsigned char**) calloc(kPacketCnt, sizeof(unsigned char*));
00092 }
00093
00094 AudioOutputWin::~AudioOutputWin()
00095 {
00096 KillAudio();
00097
00098 if (m_priv)
00099 {
00100 delete m_priv;
00101 m_priv = NULL;
00102 }
00103
00104 if (m_OutPkts)
00105 {
00106 for (uint i = 0; i < kPacketCnt; i++)
00107 {
00108 if (m_OutPkts[i])
00109 free(m_OutPkts[i]);
00110 }
00111 free(m_OutPkts);
00112 m_OutPkts = NULL;
00113 }
00114 }
00115
00116 bool AudioOutputWin::OpenDevice(void)
00117 {
00118 CloseDevice();
00119
00120 SetBlocking(true);
00121 fragment_size = (AUDIOOUTPUT_TELEPHONY == source) ? 320 : 6144;
00122
00123 WAVEFORMATEX wf;
00124 wf.wFormatTag = WAVE_FORMAT_PCM;
00125 wf.nChannels = audio_channels;
00126 wf.nSamplesPerSec = audio_samplerate;
00127 wf.wBitsPerSample = audio_bits;
00128 wf.nBlockAlign = wf.wBitsPerSample / 8 * wf.nChannels;
00129 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
00130 wf.cbSize = 0;
00131
00132 MMRESULT mmr = waveOutOpen(
00133 &m_priv->m_hWaveOut,
00134 WAVE_MAPPER,
00135 &wf,
00136 (DWORD) AudioOutputWinPrivate::waveOutProc,
00137 (DWORD) this,
00138 CALLBACK_FUNCTION);
00139
00140 if (mmr == WAVERR_BADFORMAT)
00141 {
00142 Error(QString("Unable to set audio output parameters %1")
00143 .arg(wf.nSamplesPerSec));
00144 return false;
00145 }
00146
00147 return true;
00148 }
00149
00150 void AudioOutputWin::CloseDevice(void)
00151 {
00152 m_priv->Close();
00153 }
00154
00155 void AudioOutputWin::WriteAudio(unsigned char * buffer, int size)
00156 {
00157 if (size == 0)
00158 return;
00159
00160 if (InterlockedIncrement(&m_nPkts) >= kPacketCnt)
00161 {
00162 while (m_nPkts >= kPacketCnt)
00163 WaitForSingleObject(m_priv->m_hEvent, INFINITE);
00164 }
00165
00166 if (m_CurrentPkt >= kPacketCnt)
00167 m_CurrentPkt = 0;
00168
00169 WAVEHDR *wh = &m_priv->m_WaveHdrs[m_CurrentPkt];
00170 if (wh->dwFlags & WHDR_PREPARED)
00171 waveOutUnprepareHeader(m_priv->m_hWaveOut, wh, sizeof(WAVEHDR));
00172
00173 m_OutPkts[m_CurrentPkt] = (unsigned char*)
00174 realloc(m_OutPkts[m_CurrentPkt], size);
00175
00176 memcpy(m_OutPkts[m_CurrentPkt], buffer, size);
00177
00178 memset(wh, 0, sizeof(WAVEHDR));
00179 wh->lpData = (LPSTR)m_OutPkts[m_CurrentPkt];
00180 wh->dwBufferLength = size;
00181
00182 if (MMSYSERR_NOERROR != waveOutPrepareHeader(
00183 m_priv->m_hWaveOut, wh, sizeof(WAVEHDR)))
00184 {
00185 VERBOSE(VB_IMPORTANT, "WriteAudio: failed to prepare header");
00186 }
00187 else if (MMSYSERR_NOERROR != waveOutWrite(
00188 m_priv->m_hWaveOut, wh, sizeof(WAVEHDR)))
00189 {
00190 VERBOSE(VB_IMPORTANT, "WriteAudio: failed to write packet");
00191 }
00192
00193 m_CurrentPkt++;
00194 }
00195
00196 int AudioOutputWin::getSpaceOnSoundcard(void)
00197 {
00198 return (kPacketCnt - m_nPkts) * 1536 * 4;
00199 }
00200
00201 int AudioOutputWin::getBufferedOnSoundcard(void)
00202 {
00203 return m_nPkts * 1536 * 4;
00204 }
00205
00206 int AudioOutputWin::GetVolumeChannel(int channel)
00207 {
00208 DWORD dwVolume = 0xffffffff;
00209 int Volume = 100;
00210 if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
00211 {
00212 Volume = (channel == 0) ?
00213 (LOWORD(dwVolume) / (0xffff / 100)) :
00214 (HIWORD(dwVolume) / (0xffff / 100));
00215 }
00216
00217 VERBOSE(VB_AUDIO, "GetVolume(" << channel << ") "
00218 << Volume << "(" << dwVolume << ")");
00219
00220 return Volume;
00221 }
00222
00223 void AudioOutputWin::SetVolumeChannel(int channel, int volume)
00224 {
00225 if (channel > 1)
00226 {
00227 Error(QString("Error setting channel: %1. "
00228 "Only stereo volume supported").arg(channel));
00229 return;
00230 }
00231
00232 DWORD dwVolume = 0xffffffff;
00233 if (MMSYSERR_NOERROR == waveOutGetVolume((HWAVEOUT)WAVE_MAPPER, &dwVolume))
00234 {
00235 if (channel == 0)
00236 dwVolume = dwVolume & 0xffff0000 | volume * (0xffff / 100);
00237 else
00238 dwVolume = dwVolume & 0xffff | ((volume * (0xffff / 100)) << 16);
00239 }
00240 else
00241 {
00242 dwVolume = volume * (0xffff / 100);
00243 dwVolume |= (dwVolume << 16);
00244 }
00245
00246 VERBOSE(VB_AUDIO, "SetVolume(" << channel << ") "
00247 << volume << "(" << dwVolume << ")");
00248
00249 waveOutSetVolume((HWAVEOUT)WAVE_MAPPER, dwVolume);
00250 }