00001 #include <iostream>
00002
00003
00004 using namespace std;
00005
00006 #include "mythcontext.h"
00007 #include "audiooutputdx.h"
00008
00009 #include <windows.h>
00010 #include <mmsystem.h>
00011 #include <dsound.h>
00012
00013 #define FRAMES_NUM 65536
00014
00015
00016
00017
00018
00019
00020 #include <initguid.h>
00021 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
00022
00023
00024
00025
00026 #ifndef WAVE_FORMAT_IEEE_FLOAT
00027 # define WAVE_FORMAT_IEEE_FLOAT 0x0003
00028 #endif
00029
00030 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
00031 # define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
00032 #endif
00033
00034 #ifndef WAVE_FORMAT_EXTENSIBLE
00035 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
00036 #endif
00037
00038 #ifndef SPEAKER_FRONT_LEFT
00039 # define SPEAKER_FRONT_LEFT 0x1
00040 # define SPEAKER_FRONT_RIGHT 0x2
00041 # define SPEAKER_FRONT_CENTER 0x4
00042 # define SPEAKER_LOW_FREQUENCY 0x8
00043 # define SPEAKER_BACK_LEFT 0x10
00044 # define SPEAKER_BACK_RIGHT 0x20
00045 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
00046 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
00047 # define SPEAKER_BACK_CENTER 0x100
00048 # define SPEAKER_SIDE_LEFT 0x200
00049 # define SPEAKER_SIDE_RIGHT 0x400
00050 # define SPEAKER_TOP_CENTER 0x800
00051 # define SPEAKER_TOP_FRONT_LEFT 0x1000
00052 # define SPEAKER_TOP_FRONT_CENTER 0x2000
00053 # define SPEAKER_TOP_FRONT_RIGHT 0x4000
00054 # define SPEAKER_TOP_BACK_LEFT 0x8000
00055 # define SPEAKER_TOP_BACK_CENTER 0x10000
00056 # define SPEAKER_TOP_BACK_RIGHT 0x20000
00057 # define SPEAKER_RESERVED 0x80000000
00058 #endif
00059
00060 #ifndef DSSPEAKER_HEADPHONE
00061 # define DSSPEAKER_HEADPHONE 0x00000001
00062 #endif
00063 #ifndef DSSPEAKER_MONO
00064 # define DSSPEAKER_MONO 0x00000002
00065 #endif
00066 #ifndef DSSPEAKER_QUAD
00067 # define DSSPEAKER_QUAD 0x00000003
00068 #endif
00069 #ifndef DSSPEAKER_STEREO
00070 # define DSSPEAKER_STEREO 0x00000004
00071 #endif
00072 #ifndef DSSPEAKER_SURROUND
00073 # define DSSPEAKER_SURROUND 0x00000005
00074 #endif
00075 #ifndef DSSPEAKER_5POINT1
00076 # define DSSPEAKER_5POINT1 0x00000006
00077 #endif
00078
00079 #ifndef _WAVEFORMATEXTENSIBLE_
00080 typedef struct {
00081 WAVEFORMATEX Format;
00082 union {
00083 WORD wValidBitsPerSample;
00084 WORD wSamplesPerBlock;
00085 WORD wReserved;
00086 } Samples;
00087 DWORD dwChannelMask;
00088
00089 GUID SubFormat;
00090 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
00091 #endif
00092
00093 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
00094 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
00095 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
00096
00097
00098 AudioOutputDX::AudioOutputDX(
00099 QString laudio_main_device, QString laudio_passthru_device,
00100 int laudio_bits, int laudio_channels,
00101 int laudio_samplerate, AudioOutputSource lsource,
00102 bool lset_initial_vol, bool laudio_passthru) :
00103 AudioOutputBase(laudio_main_device, laudio_passthru_device,
00104 laudio_bits, laudio_channels,
00105 laudio_samplerate, lsource,
00106 lset_initial_vol, laudio_passthru),
00107 dsound_dll(NULL),
00108 dsobject(NULL),
00109 dsbuffer(NULL),
00110 write_cursor(0),
00111 buffer_size(0),
00112 blocking(false),
00113 awaiting_data(false),
00114 effdsp(0),
00115 audio_bytes_per_sample(0),
00116 audio_bits(0),
00117 audio_channels(0),
00118 audbuf_timecode(0),
00119 can_hw_pause(false),
00120 paused(false)
00121 {
00122 InitDirectSound();
00123
00124 Reconfigure(laudio_bits, laudio_channels,
00125 laudio_samplerate, laudio_passthru);
00126 }
00127
00128 void AudioOutputDX::SetBlocking(bool blocking)
00129 {
00130
00131 }
00132
00133 void AudioOutputDX::Reconfigure(int audio_bits,
00134 int audio_channels,
00135 int audio_samplerate,
00136 bool audio_passthru)
00137 {
00138 if (dsbuffer)
00139 DestroyDSBuffer();
00140
00141 CreateDSBuffer(audio_bits, audio_channels, audio_samplerate, false);
00142
00143 awaiting_data = true;
00144 paused = true;
00145
00146 effdsp = audio_samplerate;
00147 this->audio_bits = audio_bits;
00148 this->audio_channels = audio_channels;
00149 this->audio_passthru = audio_passthru;
00150 }
00151
00152 AudioOutputDX::~AudioOutputDX()
00153 {
00154 DestroyDSBuffer();
00155
00156
00157 if (dsobject)
00158 IDirectSound_Release(dsobject);
00159
00160
00161 if (dsound_dll)
00162 FreeLibrary(dsound_dll);
00163 }
00164
00165 void AudioOutputDX::Reset(void)
00166 {
00167 audbuf_timecode = 0;
00168 awaiting_data = true;
00169 write_cursor = 0;
00170
00171 if (dsbuffer)
00172 {
00173 IDirectSoundBuffer_Stop(dsbuffer);
00174 IDirectSoundBuffer_SetCurrentPosition(dsbuffer, 0);
00175 }
00176
00177 }
00178
00179 bool AudioOutputDX::AddSamples(char *buffer, int frames, long long timecode)
00180 {
00181 FillBuffer(frames, buffer);
00182
00183 HRESULT dsresult;
00184
00185 if (awaiting_data)
00186 {
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 awaiting_data = false;
00199 }
00200
00201
00202
00203
00204
00205 if (timecode < 0)
00206 timecode = audbuf_timecode;
00207
00208
00209
00210 audbuf_timecode = timecode + (int)((frames*1000.0) / effdsp);
00211
00212 return true;
00213 }
00214
00215
00216 bool AudioOutputDX::AddSamples(char *buffers[], int frames, long long timecode)
00217 {
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266 return true;
00267 }
00268
00269 void AudioOutputDX::SetTimecode(long long timecode)
00270 {
00271 audbuf_timecode = timecode;
00272 }
00273
00274 void AudioOutputDX::SetEffDsp(int dsprate)
00275 {
00276 VERBOSE(VB_IMPORTANT, "setting dsprate = " << dsprate);
00277
00278 HRESULT dsresult;
00279
00280 dsresult = IDirectSoundBuffer_SetFrequency(dsbuffer, dsprate / 100);
00281
00282 if (dsresult != DS_OK)
00283 {
00284 VERBOSE(VB_IMPORTANT, "cannot set frequency");
00285 }
00286
00287 effdsp = dsprate / 100;
00288 }
00289
00290 bool AudioOutputDX::GetPause(void)
00291 {
00292 return paused;
00293 }
00294
00295 void AudioOutputDX::Pause(bool pause)
00296 {
00297 HRESULT dsresult;
00298
00299 VERBOSE(VB_IMPORTANT, "pause: " << pause);
00300
00301 if (pause == paused)
00302 return;
00303
00304 if (paused)
00305 {
00306 VERBOSE(VB_IMPORTANT, "unpausing");
00307
00308 dsresult = IDirectSoundBuffer_Play(dsbuffer, 0, 0, DSBPLAY_LOOPING);
00309 if (dsresult == DSERR_BUFFERLOST)
00310 {
00311 IDirectSoundBuffer_Restore(dsbuffer);
00312 dsresult = IDirectSoundBuffer_Play(dsbuffer, 0, 0, DSBPLAY_LOOPING );
00313 }
00314 if (dsresult != DS_OK)
00315 {
00316 VERBOSE(VB_IMPORTANT, "cannot start playing buffer" );
00317 }
00318 }
00319 else
00320 {
00321 VERBOSE(VB_IMPORTANT, "pausing");
00322
00323 IDirectSoundBuffer_Stop(dsbuffer);
00324 }
00325
00326 paused = pause;
00327 }
00328
00329 int AudioOutputDX::GetAudiotime(void)
00330 {
00331 DWORD play_pos;
00332 HRESULT dsresult;
00333
00334 dsresult = IDirectSoundBuffer_GetCurrentPosition(dsbuffer, &play_pos, NULL);
00335
00336 if (dsresult != DS_OK)
00337 {
00338 VERBOSE(VB_IMPORTANT, "cannot get current buffer positions");
00339 return -1;
00340 }
00341
00342 int frames = (write_cursor - play_pos) / audio_bytes_per_sample;
00343
00344 if (frames < 0)
00345 frames += buffer_size;
00346
00347 return audbuf_timecode - (int)((frames*1000.0) / effdsp);
00348 }
00349
00350
00351 typedef HRESULT (WINAPI *LPFNDSC) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
00352
00353
00354
00355
00356 int AudioOutputDX::InitDirectSound()
00357 {
00358
00359 LPFNDSC OurDirectSoundCreate;
00360
00361 VERBOSE(VB_IMPORTANT, "initialising DirectSound");
00362
00363 dsound_dll = LoadLibrary("DSOUND.DLL");
00364 if (dsound_dll == NULL )
00365 {
00366 VERBOSE(VB_IMPORTANT, "cannot open DSOUND.DLL" );
00367 goto error;
00368 }
00369
00370 OurDirectSoundCreate = (LPFNDSC)
00371 GetProcAddress(dsound_dll, "DirectSoundCreate");
00372 if (OurDirectSoundCreate == NULL)
00373 {
00374 VERBOSE(VB_IMPORTANT, "GetProcAddress FAILED" );
00375 goto error;
00376 }
00377
00378 VERBOSE(VB_IMPORTANT, "create DS object");
00379
00380
00381 if FAILED(OurDirectSoundCreate(NULL, &dsobject, NULL))
00382 {
00383 VERBOSE(VB_IMPORTANT, "cannot create a direct sound device" );
00384 goto error;
00385 }
00386
00387 VERBOSE(VB_IMPORTANT, "setting cooperative level");
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399 if (IDirectSound_SetCooperativeLevel(dsobject,
00400 GetDesktopWindow(),
00401 DSSCL_EXCLUSIVE))
00402 {
00403 VERBOSE(VB_IMPORTANT, "cannot set direct sound cooperative level" );
00404 }
00405
00406 VERBOSE(VB_IMPORTANT, "creating notificatoin events");
00407
00408
00409 for (int i = 0; i < 4; i++)
00410 notif[i].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
00411
00412 VERBOSE(VB_IMPORTANT, "initialised DirectSound");
00413
00414 return 0;
00415
00416 error:
00417 dsobject = NULL;
00418 if (dsound_dll)
00419 {
00420 FreeLibrary(dsound_dll);
00421 dsound_dll = NULL;
00422 }
00423 return -1;
00424
00425 }
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440 int AudioOutputDX::CreateDSBuffer(int audio_bits, int audio_channels, int audio_samplerate, bool b_probe)
00441 {
00442 WAVEFORMATEXTENSIBLE waveformat;
00443 DSBUFFERDESC dsbdesc;
00444 unsigned int i;
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 waveformat.Format.wBitsPerSample = audio_bits;
00477 waveformat.Samples.wValidBitsPerSample =
00478 waveformat.Format.wBitsPerSample;
00479 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
00480 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
00481
00482
00483
00484 waveformat.Format.nChannels = audio_channels;
00485 waveformat.Format.nSamplesPerSec = audio_samplerate;
00486 waveformat.Format.nBlockAlign =
00487 waveformat.Format.wBitsPerSample / 8 * audio_channels;
00488 waveformat.Format.nAvgBytesPerSec =
00489 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
00490
00491 audio_bytes_per_sample = waveformat.Format.wBitsPerSample / 8 * audio_channels;
00492
00493 VERBOSE(VB_IMPORTANT, "New format: " << audio_bits << "bits, " << audio_channels << "ch, " << audio_samplerate << "Hz");
00494
00495
00496 if (audio_channels <= 2)
00497 {
00498 waveformat.Format.cbSize = 0;
00499 }
00500 else
00501 {
00502 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
00503 waveformat.Format.cbSize =
00504 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
00505 }
00506
00507
00508
00509 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
00510 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
00511 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
00512 | DSBCAPS_CTRLPOSITIONNOTIFY
00513 | DSBCAPS_GLOBALFOCUS
00514 | DSBCAPS_LOCHARDWARE;
00515 dsbdesc.dwBufferBytes = FRAMES_NUM * audio_bits/8 * audio_channels;
00516 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
00517
00518 if FAILED(IDirectSound_CreateSoundBuffer(dsobject, &dsbdesc, &dsbuffer, NULL))
00519 {
00520
00521 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
00522 if FAILED(IDirectSound_CreateSoundBuffer(dsobject, &dsbdesc, &dsbuffer, NULL))
00523 {
00524 return -1;
00525 }
00526 if ( !b_probe ) VERBOSE(VB_IMPORTANT, "couldn't use hardware sound buffer" );
00527 }
00528
00529 write_cursor = 0;
00530 buffer_size = FRAMES_NUM * audio_bits/8 * audio_channels;
00531
00532
00533 if ( b_probe )
00534 {
00535 IDirectSoundBuffer_Release(dsbuffer);
00536 dsbuffer = NULL;
00537 return 0;
00538 }
00539
00540
00541
00542 for (i = 0; i < 4; i++)
00543 {
00544 notif[i].dwOffset = buffer_size / 4 * i;
00545 }
00546
00547
00548 if FAILED(IDirectSoundBuffer_QueryInterface(dsbuffer, IID_IDirectSoundNotify, (LPVOID*) &dsnotify))
00549 {
00550 VERBOSE(VB_IMPORTANT, "cannot get IDirectSoundNotify interface" );
00551 goto error;
00552 }
00553
00554 if FAILED(IDirectSoundNotify_SetNotificationPositions(dsnotify, 4, notif))
00555 {
00556 VERBOSE(VB_IMPORTANT, "cannot set position notification" );
00557 goto error;
00558 }
00559
00560
00561
00562
00563 VERBOSE(VB_IMPORTANT, "created DirectSound buffer");
00564
00565 return 0;
00566
00567 error:
00568 DestroyDSBuffer();
00569 return -1;
00570 }
00571
00572
00573
00574
00575
00576
00577
00578 void AudioOutputDX::DestroyDSBuffer()
00579 {
00580 if (dsnotify)
00581 {
00582 IDirectSoundNotify_Release(dsnotify);
00583 dsnotify = NULL;
00584 }
00585
00586 VERBOSE(VB_IMPORTANT, "destroying DirectSound buffer");
00587
00588 if (dsbuffer)
00589 {
00590 IDirectSoundBuffer_Stop(dsbuffer);
00591 IDirectSoundBuffer_Release(dsbuffer);
00592 dsbuffer = NULL;
00593 }
00594 }
00595
00596
00597
00598
00599
00600
00601 int AudioOutputDX::FillBuffer(int frames, char *buffer)
00602 {
00603 DWORD play_pos, write_pos, end_write;
00604 void *p_write_position, *p_wrap_around;
00605 DWORD l_bytes1, l_bytes2;
00606 HRESULT dsresult;
00607
00608 if (!awaiting_data && !paused)
00609 {
00610
00611
00612
00613 dsresult = IDirectSoundBuffer_GetCurrentPosition(dsbuffer, &play_pos, &write_pos);
00614
00615 if (dsresult != DS_OK)
00616 {
00617 VERBOSE(VB_IMPORTANT, "cannot get current buffer positions");
00618 return -1;
00619 }
00620
00621 end_write = write_cursor + (frames + FRAMES_NUM/4) * audio_bytes_per_sample;
00622
00623 while (!(((play_pos < write_pos) && (write_cursor >= write_pos || write_cursor < play_pos) &&
00624 (end_write >= write_pos || end_write < play_pos)) ||
00625 ((play_pos < write_pos) && (write_cursor >= write_pos && write_cursor < play_pos) &&
00626 (end_write >= write_pos && end_write < play_pos))))
00627 {
00628
00629
00630 HANDLE notification_events[4];
00631
00632 for(int i = 0; i < 4; i++)
00633 notification_events[i] = notif[i].hEventNotify;
00634
00635 WaitForMultipleObjects(4, notification_events, 0, INFINITE );
00636
00637
00638
00639 dsresult = IDirectSoundBuffer_GetCurrentPosition(dsbuffer, &play_pos, &write_pos);
00640
00641 if (dsresult != DS_OK)
00642 {
00643 VERBOSE(VB_IMPORTANT, "cannot get current buffer positions");
00644 return -1;
00645 }
00646
00647 end_write = write_cursor + (frames + FRAMES_NUM/4) * audio_bytes_per_sample;
00648 }
00649 }
00650
00651
00652
00653
00654
00655 dsresult = IDirectSoundBuffer_Lock(
00656 dsbuffer,
00657 write_cursor,
00658 frames * audio_bytes_per_sample,
00659 &p_write_position,
00660 &l_bytes1,
00661 &p_wrap_around,
00662 &l_bytes2,
00663 0);
00664 if (dsresult == DSERR_BUFFERLOST)
00665 {
00666 IDirectSoundBuffer_Restore(dsbuffer);
00667 dsresult = IDirectSoundBuffer_Lock(
00668 dsbuffer,
00669 write_cursor,
00670 frames * audio_bytes_per_sample,
00671 &p_write_position,
00672 &l_bytes1,
00673 &p_wrap_around,
00674 &l_bytes2,
00675 0);
00676 }
00677 if (dsresult != DS_OK)
00678 {
00679 VERBOSE(VB_IMPORTANT, "cannot lock buffer");
00680 return -1;
00681 }
00682
00683 if (buffer == NULL)
00684 {
00685 VERBOSE(VB_IMPORTANT, "Writing null bytes");
00686
00687 memset(p_write_position, 0, l_bytes1);
00688 if (p_wrap_around)
00689 memset(p_wrap_around, 0, l_bytes2);
00690
00691 write_cursor += l_bytes1 + l_bytes2;
00692
00693 while (write_cursor >= buffer_size)
00694 write_cursor -= buffer_size;
00695 }
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 else
00714 {
00715
00716
00717 memcpy(p_write_position, buffer, l_bytes1);
00718
00719 if (p_wrap_around) {
00720 memcpy(p_wrap_around, buffer + l_bytes1, l_bytes2);
00721
00722 }
00723
00724 write_cursor += l_bytes1 + l_bytes2;
00725
00726 while (write_cursor >= buffer_size)
00727 write_cursor -= buffer_size;
00728 }
00729
00730
00731
00732
00733 IDirectSoundBuffer_Unlock(dsbuffer, p_write_position, l_bytes1,
00734 p_wrap_around, l_bytes2 );
00735
00736
00737
00738
00739 return 0;
00740 }
00741
00742
00743 void AudioOutputDX::Drain()
00744 {
00745
00746
00747 }
00748
00749 int AudioOutputDX::GetVolumeChannel(int channel)
00750 {
00751
00752 return 100;
00753 }
00754 void AudioOutputDX::SetVolumeChannel(int channel, int volume)
00755 {
00756
00757 }
00758