Поскольку я также ищу ответ на вопрос, я провел небольшое исследование и не нашел простого (простого, например, вызова одной функции) способа воспроизведения аудиофайла. Но с некоторыми строками кода это возможно даже переносимым способом с использованием уже упомянутых portaudio и libsndfile (LGPL).
Вот небольшой тестовый пример, который я написал для тестирования обеих библиотек:
#include <portaudio.h>
#include <sndfile.h>
static int
output_cb(const void * input, void * output, unsigned long frames_per_buffer,
const PaStreamCallbackTimeInfo *time_info,
PaStreamCallbackFlags flags, void * data)
{
SNDFILE * file = data;
/* this should not actually be done inside of the stream callback
* but in an own working thread
*
* Note although I haven't tested it for stereo I think you have
* to multiply frames_per_buffer with the channel count i.e. 2 for
* stereo */
sf_read_short(file, output, frames_per_buffer);
return paContinue;
}
static void
end_cb(void * data)
{
printf("end!\n");
}
#define error_check(err) \
do {\
if (err) { \
fprintf(stderr, "line %d ", __LINE__); \
fprintf(stderr, "error number: %d\n", err); \
fprintf(stderr, "\n\t%s\n\n", Pa_GetErrorText(err)); \
return err; \
} \
} while (0)
int
main(int argc, char ** argv)
{
PaStreamParameters out_param;
PaStream * stream;
PaError err;
SNDFILE * file;
SF_INFO sfinfo;
if (argc < 2)
{
fprintf(stderr, "Usage %s \n", argv[0]);
return 1;
}
file = sf_open(argv[1], SFM_READ, &sfinfo);
printf("%d frames %d samplerate %d channels\n", (int)sfinfo.frames,
sfinfo.samplerate, sfinfo.channels);
/* init portaudio */
err = Pa_Initialize();
error_check(err);
/* we are using the default device */
out_param.device = Pa_GetDefaultOutputDevice();
if (out_param.device == paNoDevice)
{
fprintf(stderr, "Haven't found an audio device!\n");
return -1;
}
/* stero or mono */
out_param.channelCount = sfinfo.channels;
out_param.sampleFormat = paInt16;
out_param.suggestedLatency = Pa_GetDeviceInfo(out_param.device)->defaultLowOutputLatency;
out_param.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(&stream, NULL, &out_param, sfinfo.samplerate,
paFramesPerBufferUnspecified, paClipOff,
output_cb, file);
error_check(err);
err = Pa_SetStreamFinishedCallback(stream, &end_cb);
error_check(err);
err = Pa_StartStream(stream);
error_check(err);
printf("Play for 5 seconds.\n");
Pa_Sleep(5000);
err = Pa_StopStream(stream);
error_check(err);
err = Pa_CloseStream(stream);
error_check(err);
sf_close(file);
Pa_Terminate();
return 0;
}
Некоторые примечания к примеру. Не рекомендуется выполнять загрузку данных внутри обратного вызова потока, но внутри собственного потока загрузки. Если вам нужно воспроизвести несколько аудиофайлов, это становится еще сложнее, потому что не все бэкэнды portaudio поддерживают несколько потоков для одного устройства, например, бэкэнд OSS не поддерживает, а бэкэнд ALSA делает. Я не знаю, как обстоят дела с окнами. Поскольку все ваши входные файлы имеют одинаковый тип, вы можете смешивать их самостоятельно, что немного усложняет код, но тогда у вас также будет поддержка OSS. Если бы у вас также были разные частоты дискретизации или количество каналов, это стало бы очень сложно.
Так что, если вы не хотите воспроизводить несколько файлов одновременно, это может быть решением или, по крайней мере, началом для вас.