src/audio/xaudio2/SDL_xaudio2.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 07 Jan 2012 00:57:24 -0500
changeset 6161 84e12f37b20c
parent 6138 4c64952a58fb
child 6352 a9bcd26e7105
permissions -rwxr-xr-x
Fixed bug 1362 - SDL Fails to compile with Visual C++ Express 2008 with Feb 2007 DirectX SDK

Pallav Nawani 2012-01-04 00:48:29 PST
Issue:
Attempted to compile SDL as a static library.

DirectX SDK: Feb 2007
OS: Win 7
Visual C++ Express 2008
Compliation Mode: Static (Changed project settings from dll to lib)

Error:
C1021: invalid preprocessor command 'warning'

It seems that the #warning directive does not exist in Vc++ Express 2008.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_AUDIO_DRIVER_XAUDIO2
    24 
    25 #include "../../core/windows/SDL_windows.h"
    26 #include "SDL_audio.h"
    27 #include "../SDL_audio_c.h"
    28 #include "../SDL_sysaudio.h"
    29 #include "SDL_assert.h"
    30 
    31 #include <dxsdkver.h> /* XAudio2 exists as of the March 2008 DirectX SDK */
    32 #if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284))
    33 #  pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.")
    34 #else
    35 #  define SDL_XAUDIO2_HAS_SDK 1
    36 #endif
    37 
    38 #ifdef SDL_XAUDIO2_HAS_SDK
    39 
    40 #define INITGUID 1
    41 #include <XAudio2.h>
    42 
    43 /* Hidden "this" pointer for the audio functions */
    44 #define _THIS	SDL_AudioDevice *this
    45 
    46 struct SDL_PrivateAudioData
    47 {
    48     IXAudio2 *ixa2;
    49     IXAudio2SourceVoice *source;
    50     IXAudio2MasteringVoice *mastering;
    51     HANDLE semaphore;
    52     Uint8 *mixbuf;
    53     int mixlen;
    54     Uint8 *nextbuf;
    55 };
    56 
    57 
    58 static __inline__ char *
    59 utf16_to_utf8(const WCHAR *S)
    60 {
    61     /* !!! FIXME: this should be UTF-16, not UCS-2! */
    62     return SDL_iconv_string("UTF-8", "UCS-2", (char *)(S),
    63                             (SDL_wcslen(S)+1)*sizeof(WCHAR));
    64 }
    65 
    66 static void
    67 XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
    68 {
    69     IXAudio2 *ixa2 = NULL;
    70     UINT32 devcount = 0;
    71     UINT32 i = 0;
    72     void *ptr = NULL;
    73 
    74     if (iscapture) {
    75         SDL_SetError("XAudio2: capture devices unsupported.");
    76         return;
    77     } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
    78         SDL_SetError("XAudio2: XAudio2Create() failed.");
    79         return;
    80     } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
    81         SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed.");
    82         IXAudio2_Release(ixa2);
    83         return;
    84     }
    85 
    86     for (i = 0; i < devcount; i++) {
    87         XAUDIO2_DEVICE_DETAILS details;
    88         if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
    89             char *str = utf16_to_utf8(details.DisplayName);
    90             if (str != NULL) {
    91                 addfn(str);
    92                 SDL_free(str);  /* addfn() made a copy of the string. */
    93             }
    94         }
    95     }
    96 
    97     IXAudio2_Release(ixa2);
    98 }
    99 
   100 static void STDMETHODCALLTYPE
   101 VoiceCBOnBufferEnd(THIS_ void *data)
   102 {
   103     /* Just signal the SDL audio thread and get out of XAudio2's way. */
   104     SDL_AudioDevice *this = (SDL_AudioDevice *) data;
   105     ReleaseSemaphore(this->hidden->semaphore, 1, NULL);
   106 }
   107 
   108 static void STDMETHODCALLTYPE
   109 VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error)
   110 {
   111     /* !!! FIXME: attempt to recover, or mark device disconnected. */
   112     SDL_assert(0 && "write me!");
   113 }
   114 
   115 /* no-op callbacks... */
   116 static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {}
   117 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {}
   118 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {}
   119 static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {}
   120 static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {}
   121 
   122 
   123 static Uint8 *
   124 XAUDIO2_GetDeviceBuf(_THIS)
   125 {
   126     return this->hidden->nextbuf;
   127 }
   128 
   129 static void
   130 XAUDIO2_PlayDevice(_THIS)
   131 {
   132     XAUDIO2_BUFFER buffer;
   133     Uint8 *mixbuf = this->hidden->mixbuf;
   134     Uint8 *nextbuf = this->hidden->nextbuf;
   135     const int mixlen = this->hidden->mixlen;
   136     IXAudio2SourceVoice *source = this->hidden->source;
   137     HRESULT result = S_OK;
   138 
   139     if (!this->enabled) { /* shutting down? */
   140         return;
   141     }
   142 
   143     /* Submit the next filled buffer */
   144     SDL_zero(buffer);
   145     buffer.AudioBytes = mixlen;
   146     buffer.pAudioData = nextbuf;
   147     buffer.pContext = this;
   148 
   149     if (nextbuf == mixbuf) {
   150         nextbuf += mixlen;
   151     } else {
   152         nextbuf = mixbuf;
   153     }
   154     this->hidden->nextbuf = nextbuf;
   155 
   156     result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL);
   157     if (result == XAUDIO2_E_DEVICE_INVALIDATED) {
   158         /* !!! FIXME: possibly disconnected or temporary lost. Recover? */
   159     }
   160 
   161     if (result != S_OK) {  /* uhoh, panic! */
   162         IXAudio2SourceVoice_FlushSourceBuffers(source);
   163         this->enabled = 0;
   164     }
   165 }
   166 
   167 static void
   168 XAUDIO2_WaitDevice(_THIS)
   169 {
   170     if (this->enabled) {
   171         WaitForSingleObject(this->hidden->semaphore, INFINITE);
   172     }
   173 }
   174 
   175 static void
   176 XAUDIO2_WaitDone(_THIS)
   177 {
   178     IXAudio2SourceVoice *source = this->hidden->source;
   179     XAUDIO2_VOICE_STATE state;
   180     SDL_assert(!this->enabled);  /* flag that stops playing. */
   181     IXAudio2SourceVoice_Discontinuity(source);
   182     IXAudio2SourceVoice_GetState(source, &state);
   183     while (state.BuffersQueued > 0) {
   184         WaitForSingleObject(this->hidden->semaphore, INFINITE);
   185         IXAudio2SourceVoice_GetState(source, &state);
   186     }
   187 }
   188 
   189 
   190 static void
   191 XAUDIO2_CloseDevice(_THIS)
   192 {
   193     if (this->hidden != NULL) {
   194         IXAudio2 *ixa2 = this->hidden->ixa2;
   195         IXAudio2SourceVoice *source = this->hidden->source;
   196         IXAudio2MasteringVoice *mastering = this->hidden->mastering;
   197 
   198         if (source != NULL) {
   199             IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
   200             IXAudio2SourceVoice_FlushSourceBuffers(source);
   201             IXAudio2SourceVoice_DestroyVoice(source);
   202         }
   203         if (ixa2 != NULL) {
   204             IXAudio2_StopEngine(ixa2);
   205         }
   206         if (mastering != NULL) {
   207             IXAudio2MasteringVoice_DestroyVoice(mastering);
   208         }
   209         if (ixa2 != NULL) {
   210             IXAudio2_Release(ixa2);
   211         }
   212         if (this->hidden->mixbuf != NULL) {
   213             SDL_free(this->hidden->mixbuf);
   214         }
   215         if (this->hidden->semaphore != NULL) {
   216             CloseHandle(this->hidden->semaphore);
   217         }
   218 
   219         SDL_free(this->hidden);
   220         this->hidden = NULL;
   221     }
   222 }
   223 
   224 static int
   225 XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture)
   226 {
   227     HRESULT result = S_OK;
   228     WAVEFORMATEX waveformat;
   229     int valid_format = 0;
   230     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   231     IXAudio2 *ixa2 = NULL;
   232     IXAudio2SourceVoice *source = NULL;
   233     UINT32 devId = 0;  /* 0 == system default device. */
   234 
   235 	static IXAudio2VoiceCallbackVtbl callbacks_vtable = {
   236 	    VoiceCBOnVoiceProcessPassStart,
   237         VoiceCBOnVoiceProcessPassEnd,
   238         VoiceCBOnStreamEnd,
   239         VoiceCBOnBufferStart,
   240         VoiceCBOnBufferEnd,
   241         VoiceCBOnLoopEnd,
   242         VoiceCBOnVoiceError
   243 	};
   244 
   245 	static IXAudio2VoiceCallback callbacks = { &callbacks_vtable };
   246 
   247     if (iscapture) {
   248         SDL_SetError("XAudio2: capture devices unsupported.");
   249         return 0;
   250     } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
   251         SDL_SetError("XAudio2: XAudio2Create() failed.");
   252         return 0;
   253     }
   254 
   255     if (devname != NULL) {
   256         UINT32 devcount = 0;
   257         UINT32 i = 0;
   258 
   259         if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
   260             IXAudio2_Release(ixa2);
   261             SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed.");
   262             return 0;
   263         }
   264         for (i = 0; i < devcount; i++) {
   265             XAUDIO2_DEVICE_DETAILS details;
   266             if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
   267                 char *str = utf16_to_utf8(details.DisplayName);
   268                 if (str != NULL) {
   269                     const int match = (SDL_strcmp(str, devname) == 0);
   270                     SDL_free(str);
   271                     if (match) {
   272                         devId = i;
   273                         break;
   274                     }
   275                 }
   276             }
   277         }
   278 
   279         if (i == devcount) {
   280             IXAudio2_Release(ixa2);
   281             SDL_SetError("XAudio2: Requested device not found.");
   282             return 0;
   283         }
   284     }
   285 
   286     /* Initialize all variables that we clean on shutdown */
   287     this->hidden = (struct SDL_PrivateAudioData *)
   288         SDL_malloc((sizeof *this->hidden));
   289     if (this->hidden == NULL) {
   290         IXAudio2_Release(ixa2);
   291         SDL_OutOfMemory();
   292         return 0;
   293     }
   294     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   295 
   296     this->hidden->ixa2 = ixa2;
   297     this->hidden->semaphore = CreateSemaphore(NULL, 1, 2, NULL);
   298     if (this->hidden->semaphore == NULL) {
   299         XAUDIO2_CloseDevice(this);
   300         SDL_SetError("XAudio2: CreateSemaphore() failed!");
   301         return 0;
   302     }
   303 
   304     while ((!valid_format) && (test_format)) {
   305         switch (test_format) {
   306         case AUDIO_U8:
   307         case AUDIO_S16:
   308         case AUDIO_S32:
   309         case AUDIO_F32:
   310             this->spec.format = test_format;
   311             valid_format = 1;
   312             break;
   313         }
   314         test_format = SDL_NextAudioFormat();
   315     }
   316 
   317     if (!valid_format) {
   318         XAUDIO2_CloseDevice(this);
   319         SDL_SetError("XAudio2: Unsupported audio format");
   320         return 0;
   321     }
   322 
   323     /* Update the fragment size as size in bytes */
   324     SDL_CalculateAudioSpec(&this->spec);
   325 
   326     /* We feed a Source, it feeds the Mastering, which feeds the device. */
   327     this->hidden->mixlen = this->spec.size;
   328     this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
   329     if (this->hidden->mixbuf == NULL) {
   330         XAUDIO2_CloseDevice(this);
   331         SDL_OutOfMemory();
   332         return 0;
   333     }
   334     this->hidden->nextbuf = this->hidden->mixbuf;
   335     SDL_memset(this->hidden->mixbuf, '\0', 2 * this->hidden->mixlen);
   336 
   337     /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
   338        Xbox360, this means 5.1 output, but on Windows, it means "figure out
   339        what the system has." It might be preferable to let XAudio2 blast
   340        stereo output to appropriate surround sound configurations
   341        instead of clamping to 2 channels, even though we'll configure the
   342        Source Voice for whatever number of channels you supply. */
   343     result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
   344                                            XAUDIO2_DEFAULT_CHANNELS,
   345                                            this->spec.freq, 0, devId, NULL);
   346     if (result != S_OK) {
   347         XAUDIO2_CloseDevice(this);
   348         SDL_SetError("XAudio2: Couldn't create mastering voice");
   349         return 0;
   350     }
   351 
   352     SDL_zero(waveformat);
   353     if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   354         waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   355     } else {
   356         waveformat.wFormatTag = WAVE_FORMAT_PCM;
   357     }
   358     waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   359     waveformat.nChannels = this->spec.channels;
   360     waveformat.nSamplesPerSec = this->spec.freq;
   361     waveformat.nBlockAlign =
   362         waveformat.nChannels * (waveformat.wBitsPerSample / 8);
   363     waveformat.nAvgBytesPerSec =
   364         waveformat.nSamplesPerSec * waveformat.nBlockAlign;
   365 
   366     result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
   367                                         XAUDIO2_VOICE_NOSRC |
   368                                         XAUDIO2_VOICE_NOPITCH,
   369                                         1.0f, &callbacks, NULL, NULL);
   370     if (result != S_OK) {
   371         XAUDIO2_CloseDevice(this);
   372         SDL_SetError("XAudio2: Couldn't create source voice");
   373         return 0;
   374     }
   375     this->hidden->source = source;
   376 
   377     /* Start everything playing! */
   378     result = IXAudio2_StartEngine(ixa2);
   379     if (result != S_OK) {
   380         XAUDIO2_CloseDevice(this);
   381         SDL_SetError("XAudio2: Couldn't start engine");
   382         return 0;
   383     }
   384 
   385     result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
   386     if (result != S_OK) {
   387         XAUDIO2_CloseDevice(this);
   388         SDL_SetError("XAudio2: Couldn't start source voice");
   389         return 0;
   390     }
   391 
   392     return 1; /* good to go. */
   393 }
   394 
   395 static void
   396 XAUDIO2_Deinitialize(void)
   397 {
   398     WIN_CoUninitialize();
   399 }
   400 
   401 #endif  /* SDL_XAUDIO2_HAS_SDK */
   402 
   403 
   404 static int
   405 XAUDIO2_Init(SDL_AudioDriverImpl * impl)
   406 {
   407 #ifndef SDL_XAUDIO2_HAS_SDK
   408     SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK).");
   409     return 0;  /* no XAudio2 support, ever. Update your SDK! */
   410 #else
   411     /* XAudio2Create() is a macro that uses COM; we don't load the .dll */
   412     IXAudio2 *ixa2 = NULL;
   413     if (FAILED(WIN_CoInitialize())) {
   414         SDL_SetError("XAudio2: CoInitialize() failed");
   415         return 0;
   416     }
   417 
   418     if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
   419         WIN_CoUninitialize();
   420         SDL_SetError("XAudio2: XAudio2Create() failed");
   421         return 0;  /* not available. */
   422     }
   423     IXAudio2_Release(ixa2);
   424 
   425     /* Set the function pointers */
   426     impl->DetectDevices = XAUDIO2_DetectDevices;
   427     impl->OpenDevice = XAUDIO2_OpenDevice;
   428     impl->PlayDevice = XAUDIO2_PlayDevice;
   429     impl->WaitDevice = XAUDIO2_WaitDevice;
   430     impl->WaitDone = XAUDIO2_WaitDone;
   431     impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
   432     impl->CloseDevice = XAUDIO2_CloseDevice;
   433     impl->Deinitialize = XAUDIO2_Deinitialize;
   434 
   435     return 1;   /* this audio target is available. */
   436 #endif
   437 }
   438 
   439 AudioBootStrap XAUDIO2_bootstrap = {
   440     "xaudio2", "XAudio2", XAUDIO2_Init, 0
   441 };
   442 
   443 #endif  /* SDL_AUDIO_DRIVER_XAUDIO2 */
   444 
   445 /* vi: set ts=4 sw=4 expandtab: */