1 #include "audio.h"
2 
3 #ifdef HAVE_XAUDIO2
4 
5 #include <xaudio2.h>
6 #include <math.h>
7 
8 struct AudioContext
9 {
10 	IXAudio2 *xaudio2;
11 	IXAudio2MasteringVoice *mastering_voice;
12 };
13 
14 struct AudioVoice
15 {
16 	AudioContext *context;
17 	IXAudio2SourceVoice *voice;
18 };
19 
20 struct AudioFilter
21 {
22 	AudioContext *context;
23 	XAUDIO2_FILTER_PARAMETERS params;
24 };
25 
xaudio_destroy_context(AudioContext * p_context)26 void xaudio_destroy_context(AudioContext *p_context)
27 {
28 	p_context->mastering_voice->DestroyVoice();
29 	p_context->xaudio2->Release();
30 	delete p_context;
31 }
32 
xaudio_create_voice(AudioContext * p_context,float * p_buffer,size_t p_buffer_size,int p_sample_rate,int p_num_channels)33 AudioVoice *xaudio_create_voice(AudioContext *p_context, float *p_buffer, size_t p_buffer_size, int p_sample_rate, int p_num_channels)
34 {
35 	// create a source voice
36 	WAVEFORMATEX waveFormat;
37 	waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
38 	waveFormat.nChannels = p_num_channels;
39 	waveFormat.nSamplesPerSec = p_sample_rate;
40 	waveFormat.nBlockAlign = p_num_channels * 4;
41 	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
42 	waveFormat.wBitsPerSample = 32;
43 	waveFormat.cbSize = 0;
44 
45 	IXAudio2SourceVoice *voice;
46 	XAUDIO2_SEND_DESCRIPTOR send;
47 	XAUDIO2_VOICE_SENDS sends;
48 	sends.SendCount = 1;
49 	sends.pSends = &send;
50 	send.Flags = XAUDIO2_SEND_USEFILTER;
51 	send.pOutputVoice = p_context->mastering_voice;
52 	HRESULT hr = p_context->xaudio2->CreateSourceVoice(&voice, &waveFormat, XAUDIO2_VOICE_USEFILTER, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, &sends);
53 
54 	if (FAILED(hr)) {
55 		return NULL;
56 	}
57 
58 	voice->SetVolume(0.0f);
59 
60 	// submit the array
61 	XAUDIO2_BUFFER buffer = { 0 };
62 	buffer.AudioBytes = 4 * p_buffer_size * p_num_channels;
63 	buffer.pAudioData = (byte *)p_buffer;
64 	buffer.Flags = XAUDIO2_END_OF_STREAM;
65 	buffer.PlayBegin = 0;
66 	buffer.PlayLength = 0;
67 	buffer.LoopBegin = 0;
68 	buffer.LoopLength = 0;
69 	buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
70 
71 	hr = voice->SubmitSourceBuffer(&buffer);
72 
73 	if (FAILED(hr)) {
74 		return NULL;
75 	}
76 
77 	// start the voice playing
78 	voice->Start();
79 
80 	// return a voice struct
81 	AudioVoice *result = new AudioVoice();
82 	result->context = p_context;
83 	result->voice = voice;
84 	return result;
85 }
86 
xaudio_voice_destroy(AudioVoice * p_voice)87 void xaudio_voice_destroy(AudioVoice *p_voice)
88 {
89 
90 }
91 
xaudio_voice_set_volume(AudioVoice * p_voice,float p_volume)92 void xaudio_voice_set_volume(AudioVoice *p_voice, float p_volume)
93 {
94 	p_voice->voice->SetVolume(p_volume);
95 }
96 
xaudio_voice_set_frequency(AudioVoice * p_voice,float p_frequency)97 void xaudio_voice_set_frequency(AudioVoice *p_voice, float p_frequency)
98 {
99 	p_voice->voice->SetFrequencyRatio(p_frequency);
100 }
101 
xaudio_create_filter(AudioContext * p_context)102 AudioFilter *xaudio_create_filter(AudioContext *p_context)
103 {
104 	AudioFilter *filter = new AudioFilter();
105 	filter->context = p_context;
106 	return filter;
107 }
108 
xaudio_filter_update(AudioFilter * p_filter,int p_type,float p_cutoff_frequency,float p_q)109 void xaudio_filter_update(AudioFilter *p_filter, int p_type, float p_cutoff_frequency, float p_q)
110 {
111 	if (p_type != -1)
112 	{
113 		p_filter->params.Type = XAUDIO2_FILTER_TYPE(p_type);
114 		p_filter->params.Frequency = (float) (2 * sin(PI * p_cutoff_frequency / 44100));
115 		p_filter->params.OneOverQ = (float)(1.0 / p_q);
116 	}
117 	else
118 	{
119 		// documentation of XAUDIO2_FILTER_PARAMETERS:
120 		// Setting XAUDIO2_FILTER_PARAMETERS with the following values is acoustically equivalent to the filter being fully bypassed.
121 		p_filter->params.Type = LowPassFilter;
122 		p_filter->params.Frequency = 1.0f;
123 		p_filter->params.OneOverQ = 1.0f;
124 	}
125 }
126 
xaudio_filter_apply(AudioFilter * p_filter,AudioVoice * p_voice)127 void xaudio_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice)
128 {
129 	p_voice->voice->SetFilterParameters(&p_filter->params, XAUDIO2_COMMIT_ALL);
130 }
131 
xaudio_output_filter_apply(AudioFilter * p_filter,AudioVoice * p_voice)132 void xaudio_output_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice)
133 {
134 	p_voice->voice->SetOutputFilterParameters(p_voice->context->mastering_voice, &p_filter->params, XAUDIO2_COMMIT_ALL);
135 }
136 
xaudio_create_context()137 AudioContext *xaudio_create_context()
138 {
139 	// setup function pointers
140 	audio_destroy_context = xaudio_destroy_context;
141 	audio_create_voice = xaudio_create_voice;
142 	audio_voice_destroy = xaudio_voice_destroy;
143 	audio_voice_set_volume = xaudio_voice_set_volume;
144 	audio_voice_set_frequency = xaudio_voice_set_frequency;
145 	audio_create_filter = xaudio_create_filter;
146 	audio_filter_update = xaudio_filter_update;
147 	audio_filter_apply = xaudio_filter_apply;
148 	audio_output_filter_apply = xaudio_output_filter_apply;
149 
150 	// create XAudio object
151 	IXAudio2 *xaudio2;
152 
153 	HRESULT hr = XAudio2Create(&xaudio2);
154 	if (FAILED(hr))
155 		return NULL;
156 
157 	// create a mastering voice
158 	IXAudio2MasteringVoice *mastering_voice;
159 
160 	hr = xaudio2->CreateMasteringVoice(&mastering_voice);
161 	if (FAILED(hr))
162 		return NULL;
163 
164 	// return a context object
165 	AudioContext *context = new AudioContext();
166 	context->xaudio2 = xaudio2;
167 	context->mastering_voice = mastering_voice;
168 
169 	return context;
170 }
171 
172 #endif // HAVE_XAUDIO2
173