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