У меня есть сервер, который обрабатывает входящие данные с микрофона (1 канал) и отправляет обратно данные динамиков (2 канала). Opus Float 32 требуется со стороны сервера, и вокруг него делается несколько простых кадров. У меня есть клиент на базе Mac (простая командная строка), который работает хорошо (с небольшим дрожанием, но с достаточными допусками), но я не могу заставить звук правильно воспроизводиться с пи на динамик USB.
Похоже, что пакеты opus декодируются правильно, и я убедился, что потоки одинаковы с обеих сторон, поэтому я предполагаю, что каким-то образом неправильно управляю аудиооборудованием.
Пример кода прилагается, который не показывает кадрирование и тому подобное - а также кольцевой буфер, который я использую для передачи данных между потоками - но это охватывает декодирование опуса, а также всю конфигурацию alsa PCM и воспроизведение. Определения в верхней части являются общими для сервера и клиента.
Любая помощь была бы очень признательна, я бы несколько дней боролся с этим!
Кажется, что это работает декодирование с Mac с использованием AVFoundation и т. Д. - но я не могу воспроизвести звук без всплывающих окон на пи.
#define BUFFER_LEN 48000
#define MAXDATASIZE 1000 // max number of bytes we can get at once
#define TAG_TYPE 1
#define TAG_LENGTH 2
#define TAG_DATA 3
#define TYPE_DATA 1
#define TYPE_SAMPLE_RATE 2
#define OPUS_PACKET_TIME 20
#define INPUT_SAMPLE_RATE 16000
#define INPUT_CHANNELS 1
#define INPUT_MAX_PACKET_SIZE 64
#define INPUT_FRAMES_PER_PACKET (INPUT_SAMPLE_RATE / 1000) * OPUS_PACKET_TIME
#define OUTPUT_SAMPLE_RATE 48000
#define OUTPUT_CHANNELS 2
#define OUTPUT_MAX_PACKET_SIZE 256
#define OUTPUT_FRAMES_PER_PACKET (OUTPUT_SAMPLE_RATE / 1000) * OPUS_PACKET_TIME
#define MAX_FRAME_SIZE 6 * 960
#define OUTPUT_BUFFER_SIZE 960
#define OUTPUT_PERIOD_SIZE 480
static char *device = "default"; // soundcard
static int currentPacketType = TYPE_SAMPLE_RATE;
static int currentReadState = 0;
static int nextDataLengthToRead = 0;
static OpusDecoder *opusDecoder;
void readData(int socket, int32_t length, int tag);
void readNextPacket(int socket);
struct sample_holder {
float *ref;
};
CIRC_GBUF_DEF(struct sample_holder, playback_buffer, 8192);
#define NON_BLOCKING_PLAYBACK 0
void *playbackThread(void *sock_desc) {
plog("playback", "Playback thread started");
snd_pcm_t *handle = NULL;
int err;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
plog("playback", "Playback open error: %s", snd_strerror(err));
exit(-1);
} else {
plog("playback", "Opened device: %s", device);
}
if ((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, OUTPUT_SAMPLE_RATE, 0, 100000)) < 0) {
plog("playback", "Playback open error: %s", snd_strerror(err));
} else {
plog("playback", " - Configured device: %s", device);
}
unsigned int tmp = 0;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
int rate;
unsigned int rrate = OUTPUT_SAMPLE_RATE;
snd_pcm_uframes_t buffer_size = OUTPUT_BUFFER_SIZE;
snd_pcm_uframes_t period_size = OUTPUT_PERIOD_SIZE;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_FLOAT_LE);
snd_pcm_hw_params_set_rate_near(handle, params, &rrate, NULL);
snd_pcm_hw_params_set_channels(handle, params, 2);
snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, NULL);
snd_pcm_hw_params_get_rate(params, &tmp, 0);
plog("playback", "rate: %d bps", tmp);
snd_pcm_hw_params_get_period_size(params, &frames, 0);
plog("playback", "frames: %d", frames);
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
plog("playback", "time: %d", tmp);
#if NON_BLOCKING_PLAYBACK
if ((err = snd_pcm_nonblock(handle, 1) < 0))
{
plog("playback", "Could not set non blocking mode: %s",
snd_strerror(err));
}
else
{
plog("playback", " - Non blocking: %s", device);
}
#endif
snd_pcm_prepare(handle);
while (1) {
struct sample_holder container;
if (CIRC_GBUF_POP(playback_buffer, &container)) {
float *outPCM = container.ref;
float *buffer = outPCM;
if (outPCM != NULL) {
// plog("playback", "Pulled a buffer, free space is now: %d",
// CIRC_GBUF_FS(playback_buffer));
snd_pcm_sframes_t frames = -1;
int length = (OUTPUT_FRAMES_PER_PACKET);
while (frames != 0 && length != 0) {
snd_pcm_sframes_t avail = snd_pcm_avail_update(handle);
plog("playback", " - avail: %d", avail);
plog("playback", " - writing frames: %d", length);
snd_pcm_sframes_t availp;
snd_pcm_sframes_t delayp;
int result = snd_pcm_avail_delay(handle, &availp, &delayp);
if (availp < ((OUTPUT_FRAMES_PER_PACKET) / 3)) {
plog("playback", " - no space, waiting");
msleep(2);
continue;
}
plog("playback", " - availp: %d", availp);
plog("playback", " - delayp: %d", delayp);
for (int i = 0; i < length; i++) {
; // buffer[i] /= 1.4;
}
frames = snd_pcm_writei(
handle, buffer,
length); // sending values to sound driver
if (frames > 0) {
length -= frames;
} else if (frames == -EAGAIN) {
continue;
} else if (frames == -EPIPE) {
snd_pcm_prepare(handle);
} else {
plog("playback", "bad * wrote %d frames", frames);
}
}
}
}
}
}
void receiveSampleData(uint8_t *data, int32_t length) {
plog("decoding", "decoding %d sample bytes", length);
float *outPCM = malloc(sizeof(float) * (MAX_FRAME_SIZE * OUTPUT_CHANNELS));
int decodedSamples = opus_decode_float(opusDecoder, data, length, outPCM, (OUTPUT_FRAMES_PER_PACKET), 0);
if (decodedSamples != OUTPUT_FRAMES_PER_PACKET) {
plog("decoding", "Could not decode samples, error: %d", decodedSamples);
}
struct sample_holder container;
container.ref = outPCM;
if (CIRC_GBUF_PUSH(playback_buffer, &container)) {
plog("decoding", "Out of space in playback_buffer");
}
}
Звук звучит довольно агрессивно, поэтому его трудно услышать.