185 lines
5.7 KiB
C
185 lines
5.7 KiB
C
#include "audio.h"
|
|
|
|
#include <portaudio.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
int audio_init_soundsystem(void) {
|
|
PaError pa_err;
|
|
|
|
if ((pa_err = Pa_Initialize()) != paNoError) {
|
|
fprintf(stderr, "Cannot initialise PortAudio: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_terminate_soundsystem(void) {
|
|
PaError pa_err;
|
|
|
|
if ((pa_err = Pa_Terminate()) != paNoError) {
|
|
fprintf(stderr, "Cannot terminate PortAudio: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_init_default(audio_t *aud, int channels, int sample_rate, int frame_size) {
|
|
PaError pa_err;
|
|
int opus_err;
|
|
|
|
if ((pa_err = Pa_OpenDefaultStream(&aud->stream_in, channels, 0, AUDIO_SAMPLE_FORMAT,
|
|
sample_rate, frame_size, NULL, NULL)) != paNoError) {
|
|
fprintf(stderr, "Cannot open an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_OpenDefaultStream(&aud->stream_out, 0, channels, AUDIO_SAMPLE_FORMAT,
|
|
sample_rate, frame_size, NULL, NULL)) != paNoError) {
|
|
fprintf(stderr, "Cannot open an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
aud->buffer_size = frame_size * channels;
|
|
aud->buffer = (audio_sample_t *)malloc(aud->buffer_size * sizeof(audio_sample_t));
|
|
if (aud->buffer == NULL)
|
|
return -1;
|
|
|
|
aud->opus_enc = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &opus_err);
|
|
if (opus_err != OPUS_OK) {
|
|
fprintf(stderr, "An Opus encoder cannot be created: %s\n", opus_strerror(opus_err));
|
|
return -1;
|
|
}
|
|
|
|
aud->opus_dec = opus_decoder_create(sample_rate, channels, &opus_err);
|
|
if (opus_err != OPUS_OK) {
|
|
fprintf(stderr, "An Opus decoder cannot be created: %s\n", opus_strerror(opus_err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_destroy(audio_t *aud) {
|
|
PaError pa_err;
|
|
|
|
if (aud->stream_in != NULL) {
|
|
if ((pa_err = Pa_StopStream(aud->stream_in)) != paNoError && pa_err != paStreamIsStopped) {
|
|
fprintf(stderr, "Cannot stop an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_CloseStream(aud->stream_in)) != paNoError) {
|
|
fprintf(stderr, "Cannot close an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (aud->stream_out != NULL) {
|
|
if ((pa_err = Pa_StopStream(aud->stream_out)) != paNoError) {
|
|
fprintf(stderr, "Cannot stop an output PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_CloseStream(aud->stream_out)) != paNoError) {
|
|
fprintf(stderr, "Cannot close an output PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (aud->buffer)
|
|
free(aud->buffer);
|
|
|
|
if (aud->opus_enc)
|
|
opus_encoder_destroy(aud->opus_enc);
|
|
|
|
if (aud->opus_dec)
|
|
opus_decoder_destroy(aud->opus_dec);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_stream_input_toggle(audio_t *aud) {
|
|
PaError pa_err;
|
|
|
|
if ((pa_err = Pa_IsStreamStopped(aud->stream_in)) != 0) {
|
|
if (pa_err < 0 && pa_err != paStreamIsStopped) {
|
|
fprintf(stderr, "Cannot get state of an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_StartStream(aud->stream_in)) != paNoError) {
|
|
fprintf(stderr, "Cannot start an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
free(aud->buffer);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if ((pa_err = Pa_StopStream(aud->stream_in)) != paNoError) {
|
|
fprintf(stderr, "Cannot stop an input PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_stream_output_toggle(audio_t *aud) {
|
|
PaError pa_err;
|
|
|
|
if ((pa_err = Pa_IsStreamStopped(aud->stream_out)) != 0) {
|
|
if (pa_err < 0) {
|
|
fprintf(stderr, "Cannot get state of an output PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_StartStream(aud->stream_out)) != paNoError) {
|
|
fprintf(stderr, "Cannot start an output PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
free(aud->buffer);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if ((pa_err = Pa_StopStream(aud->stream_out)) != paNoError) {
|
|
fprintf(stderr, "Cannot stop an output PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int audio_read(audio_t *aud, unsigned char *output, int output_len) {
|
|
PaError pa_err;
|
|
|
|
if ((pa_err = Pa_ReadStream(aud->stream_in, aud->buffer, aud->buffer_size)) != paNoError) {
|
|
fprintf(stderr, "Cannot read from a PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
int encodedLen = opus_encode_float(aud->opus_enc, aud->buffer, aud->buffer_size, output, output_len);
|
|
if (encodedLen < 0) {
|
|
fprintf(stderr, "Opus failed to encode: %s\n", opus_strerror(encodedLen));
|
|
return -1;
|
|
}
|
|
|
|
return encodedLen;
|
|
}
|
|
|
|
int audio_write(audio_t *aud, const unsigned char *input, int input_len) {
|
|
PaError pa_err;
|
|
|
|
int frame_size = opus_decode_float(aud->opus_dec, input, input_len, aud->buffer, aud->buffer_size, 0);
|
|
if (frame_size < 0) {
|
|
fprintf(stderr, "Opus failed to decode: %s\n", opus_strerror(frame_size));
|
|
return -1;
|
|
}
|
|
|
|
if ((pa_err = Pa_WriteStream(aud->stream_out, aud->buffer, aud->buffer_size)) != paNoError) {
|
|
fprintf(stderr, "Cannot write to a PortAudio stream: %s\n", Pa_GetErrorText(pa_err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|