gb_emulator / gb_emulator / include / gb_emulator / gb_sound_wasapi_renderer.h

/*  Copyright © 2011 Chris Spencer <>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <>.  */

#ifndef GB_SOUND_WASAPI_RENDERER_H_CAEF2BE0_1839_11E1_8753_0002A5D5C51B
#define GB_SOUND_WASAPI_RENDERER_H_CAEF2BE0_1839_11E1_8753_0002A5D5C51B

#include <stdint.h>

#include <boost/circular_buffer.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

#include <atlbase.h>
#include <atlcom.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>

//! Audio renderer for the GbSoundWasapi driver.
class GbSoundWasapiRenderer: public IAudioSessionEvents, public IMMNotificationClient
	//! Default constructor.

	//! Initialises the renderer.
	 * \param device The audio endpoint to render to.
	 * \param sampleRateCallback The callback function to be called whenever the rendering sample
	 * rate changes. This will be called during initialisation as well as any other time there is a
	 * stream switch.
	 * \param extrapolateCallback The callback function to be called whenever the renderer would
	 * like the sound source to extrapolate the audio, instead of underflowing and skipping.
	void initialise(ATL::CComPtr<IMMDevice> device,
		boost::function<void (unsigned sampleRate)> sampleRateCallback,
		boost::function<void (unsigned frames)> extrapolateCallback);

	//! Checks for and handles any events then returns.
	void pollEvents();

	//! Sets the callback function to be called at the given frequency based on the audio clock.
	void setTimerCallback(double frequency, boost::function<void ()> callback);

	//! Pushes the given sample into the internal buffer to be played back.
	void pushSample(double left, double right);

	// IAudioSessionEvents

	STDMETHOD(OnChannelVolumeChanged)(DWORD ChannelCount, float *NewChannelVolumes,
		DWORD ChangedChannel, LPCGUID EventContext);
	STDMETHOD(OnDisplayNameChanged)(const wchar_t *NewDisplayName, const GUID *EventContext);
	STDMETHOD(OnGroupingParamChanged)(const GUID *NewGroupingParam, const GUID *EventContext);
	STDMETHOD(OnIconPathChanged)(const wchar_t *NewIconPath, const GUID *EventContext);
	STDMETHOD(OnSessionDisconnected)(AudioSessionDisconnectReason DisconnectReason);
	STDMETHOD(OnSimpleVolumeChanged)(float NewVolume, BOOL NewMute, const GUID *EventContext);
	STDMETHOD(OnStateChanged)(AudioSessionState NewState);

	// IMMNotificationClient

	STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, ERole role,
		const wchar_t *pwstrDefaultDevice);
	STDMETHOD(OnDeviceAdded)(const wchar_t *pwstrDeviceId);
	STDMETHOD(OnDeviceRemoved)(const wchar_t *pwstrDeviceId);
	STDMETHOD(OnDeviceStateChanged)(const wchar_t *pwstrDeviceId, DWORD dwNewState);
	STDMETHOD(OnPropertyValueChanged)(const wchar_t *pwstrDeviceId, const PROPERTYKEY key);

	// IUnknown

	STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
	STDMETHOD_(ULONG, Release)();

	// avrt.dll functions
	typedef HANDLE (WINAPI *AvSetMmThreadCharacteristicsW_t)(const wchar_t *TaskName,
		DWORD *TaskIndex);
	typedef BOOL (WINAPI *AvRevertMmThreadCharacteristics_t)(HANDLE AvrtHandle);

	// Sample render formats
	enum RenderFormat

	// Handle to avrt.dll, if available
	boost::shared_ptr<HINSTANCE__> avrt_;
	AvSetMmThreadCharacteristicsW_t AvSetMmThreadCharacteristicsW_;
	AvRevertMmThreadCharacteristics_t AvRevertMmThreadCharacteristics_;

	// The COM reference count
	unsigned long refCount_;

	// The audio endpoint
	ATL::CComPtr<IMMDeviceEnumerator> devices_;
	ATL::CComPtr<IMMDevice> device_;
	ATL::CComPtr<IAudioClient> audioClient_;
	ATL::CComPtr<IAudioRenderClient> renderClient_;
	ATL::CComPtr<IAudioSessionControl> audioSessionControl_;

	// The current render format type
	boost::shared_ptr<WAVEFORMATEX> mixFormat_;
	boost::function<void (unsigned sampleRate)> sampleRateCallback_;
	boost::function<void (unsigned frames)> extrapolateCallback_;
	RenderFormat renderFormat_;
	uint32_t samplesPerPeriod_;
	uint32_t bufferSize_;

	// Flag indicating if we are currently in the process of switching the render stream
	bool switchingStream_;

	// Audio event HANDLEs
	boost::shared_ptr<void> audioSamplesReadyEvent_;
	boost::shared_ptr<void> streamSwitchEvent_;
	boost::shared_ptr<void> streamSwitchCompleteEvent_;

	// MMCSS handle
	boost::shared_ptr<void> mmcssHandle_;

	// Sound buffers. Only one of these is used at any time based on the hardware sample format
	boost::circular_buffer<int16_t> soundBufInt16_;
	boost::circular_buffer<float> soundBufFloat_;

	// Sound timer callback
	double callbackFrequency_;
	double callbackSampleRate_;
	double callbackCountdown_;
	boost::function<void ()> timerCallback_;

	// Loads the mix format required by the hardware
	void loadFormat();

	// Initialises the audio client
	void initialiseAudioClient();

	// Handles a stream switch event
	void handleStreamSwitch();

	// Sends a period's worth of samples to the audio device
	void sendSamples();
	// Disabled operations
	GbSoundWasapiRenderer(const GbSoundWasapiRenderer &);
	GbSoundWasapiRenderer & operator=(const GbSoundWasapiRenderer &);