1 /*
2 libgo2 - Support library for the ODROID-GO Advance
3 Copyright (C) 2020 OtherCrashOverride
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9 
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 #include "audio.h"
21 
22 #include <AL/al.h>
23 #include <AL/alc.h>
24 #include <AL/alext.h>
25 
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include <alsa/asoundlib.h>
33 #include <alsa/mixer.h>
34 
35 #define SOUND_SAMPLES_SIZE  (2048)
36 #define SOUND_CHANNEL_COUNT 2
37 
38 
39 typedef struct go2_audio
40 {
41     ALsizei frequency;
42     ALCdevice *device;
43     ALCcontext *context;
44     ALuint source;
45     bool isAudioInitialized;
46 } go2_audio_t;
47 
48 
go2_audio_create(int frequency)49 go2_audio_t* go2_audio_create(int frequency)
50 {
51     go2_audio_t* result = malloc(sizeof(*result));
52     if (!result)
53     {
54         printf("malloc failed.\n");
55         goto out;
56     }
57 
58     memset(result, 0, sizeof(*result));
59 
60 
61     result->frequency = frequency;
62 
63 	result->device = alcOpenDevice(NULL);
64 	if (!result->device)
65 	{
66 		printf("alcOpenDevice failed.\n");
67 		goto err_00;
68 	}
69 
70 	result->context = alcCreateContext(result->device, NULL);
71 	if (!alcMakeContextCurrent(result->context))
72 	{
73 		printf("alcMakeContextCurrent failed.\n");
74 		goto err_01;
75 	}
76 
77 	alGenSources((ALuint)1, &result->source);
78 
79 	alSourcef(result->source, AL_PITCH, 1);
80 	alSourcef(result->source, AL_GAIN, 1);
81 	alSource3f(result->source, AL_POSITION, 0, 0, 0);
82 	alSource3f(result->source, AL_VELOCITY, 0, 0, 0);
83 	alSourcei(result->source, AL_LOOPING, AL_FALSE);
84 
85 	//memset(audioBuffer, 0, AUDIOBUFFER_LENGTH * sizeof(short));
86 
87 	const int BUFFER_COUNT = 4;
88 	for (int i = 0; i < BUFFER_COUNT; ++i)
89 	{
90 		ALuint buffer;
91 		alGenBuffers((ALuint)1, &buffer);
92 		alBufferData(buffer, AL_FORMAT_STEREO16, NULL, 0, frequency);
93 		alSourceQueueBuffers(result->source, 1, &buffer);
94 	}
95 
96 	alSourcePlay(result->source);
97 
98     result->isAudioInitialized = true;
99 
100     // testing
101     //uint32_t vol = go2_audio_volume_get(result);
102     //printf("audio: vol=%d\n", vol);
103     //go2_audio_path_get(result);
104 
105 
106     return result;
107 
108 
109 err_01:
110     alcCloseDevice(result->device);
111 
112 err_00:
113     free(result);
114 
115 out:
116     return NULL;
117 }
118 
go2_audio_destroy(go2_audio_t * audio)119 void go2_audio_destroy(go2_audio_t* audio)
120 {
121     alDeleteSources(1, &audio->source);
122     alcDestroyContext(audio->context);
123     alcCloseDevice(audio->device);
124 
125     free(audio);
126 }
127 
go2_audio_submit(go2_audio_t * audio,const short * data,int frames)128 void go2_audio_submit(go2_audio_t* audio, const short* data, int frames)
129 {
130     if (!audio || !audio->isAudioInitialized) return;
131 
132 
133     if (!alcMakeContextCurrent(audio->context))
134     {
135         printf("alcMakeContextCurrent failed.\n");
136         return;
137     }
138 
139     ALint processed = 0;
140     while(!processed)
141     {
142         alGetSourceiv(audio->source, AL_BUFFERS_PROCESSED, &processed);
143 
144         if (!processed)
145         {
146             sleep(0);
147             //printf("Audio overflow.\n");
148             //return;
149         }
150     }
151 
152     ALuint openALBufferID;
153     alSourceUnqueueBuffers(audio->source, 1, &openALBufferID);
154 
155     ALuint format = AL_FORMAT_STEREO16;
156 
157     int dataByteLength = frames * sizeof(short) * SOUND_CHANNEL_COUNT;
158     alBufferData(openALBufferID, format, data, dataByteLength, audio->frequency);
159 
160     alSourceQueueBuffers(audio->source, 1, &openALBufferID);
161 
162     ALint result;
163     alGetSourcei(audio->source, AL_SOURCE_STATE, &result);
164 
165     if (result != AL_PLAYING)
166     {
167         alSourcePlay(audio->source);
168     }
169 }
170 
go2_audio_volume_get(go2_audio_t * audio)171 uint32_t go2_audio_volume_get(go2_audio_t* audio)
172 {
173     snd_mixer_t *handle;
174     snd_mixer_selem_id_t *sid;
175     const char *card = "default";
176     const char *selem_name = "Playback";
177 
178     snd_mixer_open(&handle, 0);
179     snd_mixer_attach(handle, card);
180     snd_mixer_selem_register(handle, NULL, NULL);
181     snd_mixer_load(handle);
182 
183     snd_mixer_selem_id_alloca(&sid);
184     snd_mixer_selem_id_set_index(sid, 0);
185     snd_mixer_selem_id_set_name(sid, selem_name);
186 
187     snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
188 
189     long min;
190     long max;
191     snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
192 
193 
194     //snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max);
195     long volume;
196     snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &volume);
197 
198     snd_mixer_close(handle);
199 
200     uint32_t result = volume / (float)max * 100.0f;
201     //printf("volume: min=%ld, max=%ld, volume=%ld, result=%d\n", min, max, volume, result);
202 
203     return result;
204 }
205 
go2_audio_volume_set(go2_audio_t * audio,uint32_t value)206 void go2_audio_volume_set(go2_audio_t* audio, uint32_t value)
207 {
208     // https://gist.github.com/wolfg1969/3575700
209 
210     snd_mixer_t *handle;
211     snd_mixer_selem_id_t *sid;
212     const char *card = "default";
213     const char *selem_name = "Playback";
214 
215     snd_mixer_open(&handle, 0);
216     snd_mixer_attach(handle, card);
217     snd_mixer_selem_register(handle, NULL, NULL);
218     snd_mixer_load(handle);
219 
220     snd_mixer_selem_id_alloca(&sid);
221     snd_mixer_selem_id_set_index(sid, 0);
222     snd_mixer_selem_id_set_name(sid, selem_name);
223 
224     snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
225 
226     long min;
227     long max;
228     snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
229     //printf("volume: min=%ld, max=%ld\n", min, max);
230 
231     snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max);
232 
233     snd_mixer_close(handle);
234 }
235 
go2_audio_path_get(go2_audio_t * audio)236 go2_audio_path_t go2_audio_path_get(go2_audio_t* audio)
237 {
238     snd_mixer_t *handle;
239     snd_mixer_selem_id_t *sid;
240     const char *card = "default";
241     const char *selem_name = "Playback Path";
242 
243     snd_mixer_open(&handle, 0);
244     snd_mixer_attach(handle, card);
245     snd_mixer_selem_register(handle, NULL, NULL);
246     snd_mixer_load(handle);
247 
248     snd_mixer_selem_id_alloca(&sid);
249     snd_mixer_selem_id_set_index(sid, 0);
250     snd_mixer_selem_id_set_name(sid, selem_name);
251 
252     snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
253 
254     unsigned int value;
255     snd_mixer_selem_get_enum_item(elem, SND_MIXER_SCHN_MONO, &value);
256 
257     // char name[128];
258     // snd_mixer_selem_get_enum_item_name(elem, value, 128, name);
259     // printf("audio path: value=%d [%s]\n", value, name);
260 
261     snd_mixer_close(handle);
262 
263     return (go2_audio_path_t)value;
264 }
265 
go2_audio_path_set(go2_audio_t * audio,go2_audio_path_t value)266 void go2_audio_path_set(go2_audio_t* audio, go2_audio_path_t value)
267 {
268     snd_mixer_t *handle;
269     snd_mixer_selem_id_t *sid;
270     const char *card = "default";
271     const char *selem_name = "Playback Path";
272 
273     snd_mixer_open(&handle, 0);
274     snd_mixer_attach(handle, card);
275     snd_mixer_selem_register(handle, NULL, NULL);
276     snd_mixer_load(handle);
277 
278     snd_mixer_selem_id_alloca(&sid);
279     snd_mixer_selem_id_set_index(sid, 0);
280     snd_mixer_selem_id_set_name(sid, selem_name);
281 
282     snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
283 
284     snd_mixer_selem_set_enum_item(elem, SND_MIXER_SCHN_MONO, (unsigned int)value);
285 
286     snd_mixer_close(handle);
287 }
288