RetroArch/deps/libgo2/src/audio.c

288 lines
7.1 KiB
C

/*
libgo2 - Support library for the ODROID-GO Advance
Copyright (C) 2020 OtherCrashOverride
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "audio.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#define SOUND_SAMPLES_SIZE (2048)
#define SOUND_CHANNEL_COUNT 2
typedef struct go2_audio
{
ALsizei frequency;
ALCdevice *device;
ALCcontext *context;
ALuint source;
bool isAudioInitialized;
} go2_audio_t;
go2_audio_t* go2_audio_create(int frequency)
{
go2_audio_t* result = malloc(sizeof(*result));
if (!result)
{
printf("malloc failed.\n");
goto out;
}
memset(result, 0, sizeof(*result));
result->frequency = frequency;
result->device = alcOpenDevice(NULL);
if (!result->device)
{
printf("alcOpenDevice failed.\n");
goto err_00;
}
result->context = alcCreateContext(result->device, NULL);
if (!alcMakeContextCurrent(result->context))
{
printf("alcMakeContextCurrent failed.\n");
goto err_01;
}
alGenSources((ALuint)1, &result->source);
alSourcef(result->source, AL_PITCH, 1);
alSourcef(result->source, AL_GAIN, 1);
alSource3f(result->source, AL_POSITION, 0, 0, 0);
alSource3f(result->source, AL_VELOCITY, 0, 0, 0);
alSourcei(result->source, AL_LOOPING, AL_FALSE);
//memset(audioBuffer, 0, AUDIOBUFFER_LENGTH * sizeof(short));
const int BUFFER_COUNT = 4;
for (int i = 0; i < BUFFER_COUNT; ++i)
{
ALuint buffer;
alGenBuffers((ALuint)1, &buffer);
alBufferData(buffer, AL_FORMAT_STEREO16, NULL, 0, frequency);
alSourceQueueBuffers(result->source, 1, &buffer);
}
alSourcePlay(result->source);
result->isAudioInitialized = true;
// testing
//uint32_t vol = go2_audio_volume_get(result);
//printf("audio: vol=%d\n", vol);
//go2_audio_path_get(result);
return result;
err_01:
alcCloseDevice(result->device);
err_00:
free(result);
out:
return NULL;
}
void go2_audio_destroy(go2_audio_t* audio)
{
alDeleteSources(1, &audio->source);
alcDestroyContext(audio->context);
alcCloseDevice(audio->device);
free(audio);
}
void go2_audio_submit(go2_audio_t* audio, const short* data, int frames)
{
if (!audio || !audio->isAudioInitialized) return;
if (!alcMakeContextCurrent(audio->context))
{
printf("alcMakeContextCurrent failed.\n");
return;
}
ALint processed = 0;
while(!processed)
{
alGetSourceiv(audio->source, AL_BUFFERS_PROCESSED, &processed);
if (!processed)
{
sleep(0);
//printf("Audio overflow.\n");
//return;
}
}
ALuint openALBufferID;
alSourceUnqueueBuffers(audio->source, 1, &openALBufferID);
ALuint format = AL_FORMAT_STEREO16;
int dataByteLength = frames * sizeof(short) * SOUND_CHANNEL_COUNT;
alBufferData(openALBufferID, format, data, dataByteLength, audio->frequency);
alSourceQueueBuffers(audio->source, 1, &openALBufferID);
ALint result;
alGetSourcei(audio->source, AL_SOURCE_STATE, &result);
if (result != AL_PLAYING)
{
alSourcePlay(audio->source);
}
}
uint32_t go2_audio_volume_get(go2_audio_t* audio)
{
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Playback";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
long min;
long max;
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
//snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max);
long volume;
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &volume);
snd_mixer_close(handle);
uint32_t result = volume / (float)max * 100.0f;
//printf("volume: min=%ld, max=%ld, volume=%ld, result=%d\n", min, max, volume, result);
return result;
}
void go2_audio_volume_set(go2_audio_t* audio, uint32_t value)
{
// https://gist.github.com/wolfg1969/3575700
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Playback";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
long min;
long max;
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
//printf("volume: min=%ld, max=%ld\n", min, max);
snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max);
snd_mixer_close(handle);
}
go2_audio_path_t go2_audio_path_get(go2_audio_t* audio)
{
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Playback Path";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
unsigned int value;
snd_mixer_selem_get_enum_item(elem, SND_MIXER_SCHN_MONO, &value);
// char name[128];
// snd_mixer_selem_get_enum_item_name(elem, value, 128, name);
// printf("audio path: value=%d [%s]\n", value, name);
snd_mixer_close(handle);
return (go2_audio_path_t)value;
}
void go2_audio_path_set(go2_audio_t* audio, go2_audio_path_t value)
{
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Playback Path";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
snd_mixer_selem_set_enum_item(elem, SND_MIXER_SCHN_MONO, (unsigned int)value);
snd_mixer_close(handle);
}