void do_register(XAudio2_Output * p_instance) { if ( InterlockedIncrement( ®istered ) == 1 ) { pEnumerator = NULL; HRESULT hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IMMDeviceEnumerator ), ( void** ) &pEnumerator ); if ( SUCCEEDED( hr ) ) { pEnumerator->RegisterEndpointNotificationCallback( this ); } } EnterCriticalSection( &lock ); instances.push_back( p_instance ); LeaveCriticalSection( &lock ); }
HRESULT register_notification_callback(IMMNotificationClient *pClient) { HRESULT hr = S_OK; IMMDeviceEnumerator *pMMDeviceEnumerator; // activate a device enumerator hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator ); if (FAILED(hr)) { printf("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x\n", hr); return hr; } // get the default render endpoint hr = pMMDeviceEnumerator->RegisterEndpointNotificationCallback(pClient); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { printf("IMMDeviceEnumerator::RegisterEndpointNotificationCallback failed: hr = 0x%08x\n", hr); return hr; } return S_OK; }
//----------------------------------------------------------------------------- // Name: WinMain() // Desc: The application's entry point //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT ) { // set up some xbmc specific relationships XBMC::Context context; //this can't be set from CAdvancedSettings::Initialize() because it will overwrite //the loglevel set with the --debug flag #ifdef _DEBUG g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG; g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG; #else g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL; g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL; #endif CLog::SetLogLevel(g_advancedSettings.m_logLevel); // Initializes CreateMiniDump to handle exceptions. win32_exception::set_version(g_infoManager.GetVersion()); SetUnhandledExceptionFilter( CreateMiniDump ); // check if Kodi is already running std::string appName = CSysInfo::GetAppName(); CreateMutex(NULL, FALSE, (appName + " Media Center").c_str()); if(GetLastError() == ERROR_ALREADY_EXISTS) { HWND m_hwnd = FindWindow(appName.c_str(), appName.c_str()); if(m_hwnd != NULL) { // switch to the running instance ShowWindow(m_hwnd,SW_RESTORE); SetForegroundWindow(m_hwnd); } return 0; } #ifndef HAS_DX if(CWIN32Util::GetDesktopColorDepth() < 32) { //FIXME: replace it by a SDL window for all ports MessageBox(NULL, "Desktop Color Depth isn't 32Bit", (appName + ": Fatal Error").c_str(), MB_OK|MB_ICONERROR); return 0; } #endif if((g_cpuInfo.GetCPUFeatures() & CPU_FEATURE_SSE2) == 0) { MessageBox(NULL, "No SSE2 support detected", (appName + ": Fatal Error").c_str(), MB_OK|MB_ICONERROR); return 0; } //Initialize COM CoInitializeEx(NULL, COINIT_MULTITHREADED); // Handle numeric values using the default/POSIX standard setlocale(LC_NUMERIC, "C"); // If the command line passed to WinMain, commandLine, is not "" we need // to process the command line arguments. // Note that commandLine does not include the program name and can be // equal to "" if no arguments were supplied. By contrast GetCommandLineW() // does include the program name and is never equal to "". g_advancedSettings.Initialize(); if (strlen(commandLine) != 0) { int argc; LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc); std::vector<std::string> strargvA; strargvA.resize(argc); const char** argv = (const char**) LocalAlloc(LMEM_FIXED, argc*sizeof(char*)); if (!argv) return 20; for (int i = 0; i < argc; i++) { g_charsetConverter.wToUTF8(argvW[i], strargvA[i]); argv[i] = strargvA[i].c_str(); } // Parse the arguments CAppParamParser appParamParser; appParamParser.Parse(argv, argc); // Clean up the storage we've used LocalFree(argvW); LocalFree(argv); } // Initialise Winsock WSADATA wd; WSAStartup(MAKEWORD(2,2), &wd); // use 1 ms timer precision - like SDL initialization used to do timeBeginPeriod(1); #ifdef XBMC_TRACK_EXCEPTIONS try { #endif // Create and run the app if(!g_application.Create()) { MessageBox(NULL, "ERROR: Unable to create application. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #ifdef XBMC_TRACK_EXCEPTIONS } catch (const XbmcCommons::UncheckedException &e) { e.LogThrowMessage("CApplication::Create()"); MessageBox(NULL, "ERROR: Unable to create application. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } catch (...) { CLog::Log(LOGERROR, "exception in CApplication::Create()"); MessageBox(NULL, "ERROR: Unable to create application. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #endif #ifndef _DEBUG // we don't want to see the "no disc in drive" windows message box SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); #endif #ifdef XBMC_TRACK_EXCEPTIONS try { #endif if (!g_application.CreateGUI()) { MessageBox(NULL, "ERROR: Unable to create GUI. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #ifdef XBMC_TRACK_EXCEPTIONS } catch (const XbmcCommons::UncheckedException &e) { e.LogThrowMessage("CApplication::CreateGUI()"); MessageBox(NULL, "ERROR: Unable to create GUI. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } catch (...) { CLog::Log(LOGERROR, "exception in CApplication::CreateGUI()"); MessageBox(NULL, "ERROR: Unable to create GUI. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #endif #ifdef XBMC_TRACK_EXCEPTIONS try { #endif if (!g_application.Initialize()) { MessageBox(NULL, "ERROR: Unable to Initialize. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #ifdef XBMC_TRACK_EXCEPTIONS } catch (const XbmcCommons::UncheckedException &e) { e.LogThrowMessage("CApplication::Initialize()"); MessageBox(NULL, "ERROR: Unable to Initialize. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } catch (...) { CLog::Log(LOGERROR, "exception in CApplication::Initialize()"); MessageBox(NULL, "ERROR: Unable to Initialize. Exiting.", (appName + ": Error").c_str(), MB_OK|MB_ICONERROR); return 1; } #endif IMMDeviceEnumerator *pEnumerator = NULL; CMMNotificationClient cMMNC; HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); if(SUCCEEDED(hr)) { pEnumerator->RegisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } g_application.Run(); // clear previously set timer resolution timeEndPeriod(1); // the end hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); if(SUCCEEDED(hr)) { pEnumerator->UnregisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } WSACleanup(); CoUninitialize(); return 0; }
extern "C" int XBMC_Run(bool renderGUI) { int status = -1; if (!g_advancedSettings.Initialized()) { #ifdef _DEBUG g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG; g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG; #else g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL; g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL; #endif g_advancedSettings.Initialize(); } if (!g_application.Create()) { CMessagePrinter::DisplayError("ERROR: Unable to create application. Exiting"); return status; } #if defined(HAVE_BREAKPAD) // Must have our TEMP dir fixed first std::string tempPath = CSpecialProtocol::TranslatePath("special://temp/"); google_breakpad::MinidumpDescriptor descriptor(tempPath.c_str()); google_breakpad::ExceptionHandler eh(descriptor, NULL, NULL, NULL, true, -1); #endif #ifdef TARGET_RASPBERRY_PI if(!g_RBP.Initialize()) return false; g_RBP.LogFirmwareVerison(); #endif if (renderGUI && !g_application.CreateGUI()) { CMessagePrinter::DisplayError("ERROR: Unable to create GUI. Exiting"); return status; } if (!g_application.Initialize()) { CMessagePrinter::DisplayError("ERROR: Unable to Initialize. Exiting"); return status; } #ifdef TARGET_WINDOWS IMMDeviceEnumerator *pEnumerator = nullptr; CMMNotificationClient cMMNC; HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator)); if (SUCCEEDED(hr)) { pEnumerator->RegisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } #endif try { status = g_application.Run(); } #ifdef TARGET_WINDOWS catch (const XbmcCommons::UncheckedException &e) { e.LogThrowMessage("CApplication::Create()"); CMessagePrinter::DisplayError("ERROR: Exception caught on main loop. Exiting"); status = -1; } #endif catch(...) { CMessagePrinter::DisplayError("ERROR: Exception caught on main loop. Exiting"); status = -1; } #ifdef TARGET_WINDOWS // the end hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator)); if (SUCCEEDED(hr)) { pEnumerator->UnregisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } #endif #ifdef TARGET_RASPBERRY_PI g_RBP.Deinitialize(); #endif return status; }
extern "C" int XBMC_Run(bool renderGUI) { int status = -1; if (!g_advancedSettings.Initialized()) { #ifdef _DEBUG g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG; g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG; #else g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL; g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL; #endif g_advancedSettings.Initialize(); } if (!g_application.Create()) { CMessagePrinter::DisplayError("ERROR: Unable to create application. Exiting"); return status; } #ifdef TARGET_RASPBERRY_PI if(!g_RBP.Initialize()) return false; g_RBP.LogFirmwareVerison(); #endif if (renderGUI && !g_application.CreateGUI()) { CMessagePrinter::DisplayError("ERROR: Unable to create GUI. Exiting"); return status; } if (!g_application.Initialize()) { CMessagePrinter::DisplayError("ERROR: Unable to Initialize. Exiting"); return status; } #ifdef TARGET_WINDOWS IMMDeviceEnumerator *pEnumerator = nullptr; CMMNotificationClient cMMNC; HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator)); if (SUCCEEDED(hr)) { pEnumerator->RegisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } #endif try { status = g_application.Run(); } #ifdef TARGET_WINDOWS catch (const XbmcCommons::UncheckedException &e) { e.LogThrowMessage("CApplication::Create()"); CMessagePrinter::DisplayError("ERROR: Exception caught on main loop. Exiting"); status = -1; } #endif catch(...) { CMessagePrinter::DisplayError("ERROR: Exception caught on main loop. Exiting"); status = -1; } #ifdef TARGET_WINDOWS // the end hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&pEnumerator)); if (SUCCEEDED(hr)) { pEnumerator->UnregisterEndpointNotificationCallback(&cMMNC); SAFE_RELEASE(pEnumerator); } #endif #ifdef TARGET_RASPBERRY_PI g_RBP.Deinitialize(); #endif return status; }
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; IMMDevice *device = NULL; CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_device->device_name == "Default") { hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } else { IMMDeviceCollection *devices = NULL; hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = NULL; bool found = false; UINT count = 0; hr = devices->GetCount(&count); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); for (ULONG i = 0; i < count && !found; i++) { IMMDevice *device = NULL; hr = devices->Item(i, &device); ERR_BREAK(hr != S_OK); IPropertyStore *props = NULL; hr = device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); PROPVARIANT propvar; PropVariantInit(&propvar); hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); ERR_BREAK(hr != S_OK); if (p_device->device_name == String(propvar.pwszVal)) { hr = device->GetId(&strId); ERR_BREAK(hr != S_OK); found = true; } PropVariantClear(&propvar); props->Release(); device->Release(); } if (found) { hr = enumerator->GetDevice(strId, &device); } if (strId) { CoTaskMemFree(strId); } if (device == NULL) { hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } } if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); SAFE_RELEASE(enumerator) if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&p_device->audio_client); SAFE_RELEASE(device) if (reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along p_device->channels = pwfex->nChannels; p_device->format_tag = pwfex->wFormatTag; p_device->bits_per_sample = pwfex->wBitsPerSample; p_device->frame_size = (p_device->bits_per_sample / 8) * p_device->channels; if (p_device->format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { p_device->format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { p_device->format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { if (p_device->format_tag != WAVE_FORMAT_PCM && p_device->format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } DWORD streamflags = 0; if (mix_rate != pwfex->nSamplesPerSec) { streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_capture) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); } else { hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); } ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Free memory CoTaskMemFree(pwfex); SAFE_RELEASE(device) return OK; }
Error AudioDriverWASAPI::init_device(bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; IMMDevice *device = NULL; CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audio_client); if (reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along wasapi_channels = pwfex->nChannels; mix_rate = pwfex->nSamplesPerSec; format_tag = pwfex->wFormatTag; bits_per_sample = pwfex->wBitsPerSample; switch (wasapi_channels) { case 2: // Stereo //case 6: // Surround 5.1 //case 8: // Surround 7.1 channels = wasapi_channels; break; default: WARN_PRINTS("WASAPI: Unsupported number of channels (" + itos(wasapi_channels) + ")"); channels = 2; } if (format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { if (format_tag != WAVE_FORMAT_PCM && format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); event = CreateEvent(NULL, FALSE, FALSE, NULL); ERR_FAIL_COND_V(event == NULL, ERR_CANT_OPEN); hr = audio_client->SetEventHandle(event); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = audio_client->GetService(IID_IAudioRenderClient, (void **)&render_client); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); UINT32 max_frames; hr = audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size buffer_frames = max_frames; // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); if (OS::get_singleton()->is_stdout_verbose()) { print_line("audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } return OK; }