Commit a81f09d9 authored by David Reid's avatar David Reid

Add osaudio to the extras folder.

This is just a small project to experiment with a few API ideas. This
is not a replacement for miniaudio or anything so don't panic.
parent f2ea6562
This is just a little experiment to explore some ideas for the kind of API that I would build if I
was building my own operation system. The name "osaudio" means Operating System Audio. Or maybe you
can think of it as Open Source Audio. It's whatever you want it to be.
The idea behind this project came about after considering the absurd complexity of audio APIs on
various platforms after years of working on miniaudio. This project aims to disprove the idea that
complete and flexible audio solutions and simple APIs are mutually exclusive and that it's possible
to have both. I challenge anybody to prove me wrong.
In addition to the above, I also wanted to explore some ideas for a different API design to
miniaudio. miniaudio uses a callback model for data transfer, whereas osaudio uses a blocking
read/write model.
This project is essentially just a header file with a reference implementation that uses miniaudio
under the hood. You can compile this very easily - just compile osaudio_miniaudio.c, and use
osaudio.h just like any other header. There are no dependencies for the header, and the miniaudio
implementation obviously requires miniaudio. Adjust the include path in osaudio_miniaudio.c if need
be.
See osaudio.h for full documentation. Below is an example to get you started:
```c
#include "osaudio.h"
...
osaudio_t audio;
osaudio_config_t config;
osaudio_config_init(&config, OSAUDIO_OUTPUT);
config.format = OSAUDIO_FORMAT_F32;
config.channels = 2;
config.rate = 48000;
osaudio_open(&audio, &config);
osaudio_write(audio, myAudioData, frameCount); // <-- This will block until all of the data has been sent to the device.
osaudio_close(audio);
```
Compare the code above with the likes of other APIs like Core Audio and PipeWire. I challenge
anybody to argue their APIs are cleaner and easier to use than this when it comes to simple audio
playback.
If you have any feedback on this I'd be interested to hear it. In particular, I'd really like to
hear from people who believe the likes of Core Audio (Apple), PipeWire, PulseAudio or any other
audio API actually have good APIs (they don't!) and what makes their's better and/or worse than
this project.
This diff is collapsed.
This diff is collapsed.
#include "../osaudio.h"
/* This example uses miniaudio for decoding audio files. */
#define MINIAUDIO_IMPLEMENTATION
#include "../../../miniaudio.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MODE_PLAYBACK 0
#define MODE_CAPTURE 1
#define MODE_DUPLEX 2
void enumerate_devices()
{
int result;
unsigned int iDevice;
unsigned int count;
osaudio_info_t* pDeviceInfos;
result = osaudio_enumerate(&count, &pDeviceInfos);
if (result != OSAUDIO_SUCCESS) {
printf("Failed to enumerate audio devices.\n");
return;
}
for (iDevice = 0; iDevice < count; iDevice += 1) {
printf("(%s) %s\n", (pDeviceInfos[iDevice].direction == OSAUDIO_OUTPUT) ? "Playback" : "Capture", pDeviceInfos[iDevice].name);
}
free(pDeviceInfos);
}
osaudio_t open_device(int direction)
{
int result;
osaudio_t audio;
osaudio_config_t config;
osaudio_config_init(&config, direction);
config.format = OSAUDIO_FORMAT_F32;
config.channels = 2;
config.rate = 48000;
config.flags = OSAUDIO_FLAG_REPORT_XRUN;
result = osaudio_open(&audio, &config);
if (result != OSAUDIO_SUCCESS) {
printf("Failed to open audio device.\n");
return NULL;
}
return audio;
}
void do_playback(int argc, char** argv)
{
int result;
osaudio_t audio;
const osaudio_config_t* config;
const char* pFilePath = NULL;
ma_result resultMA;
ma_decoder_config decoderConfig;
ma_decoder decoder;
audio = open_device(OSAUDIO_OUTPUT);
if (audio == NULL) {
printf("Failed to open audio device.\n");
return;
}
config = &osaudio_get_info(audio)->configs[0];
/* We want to always use f32. */
if (config->format == OSAUDIO_FORMAT_F32) {
if (argc > 1) {
pFilePath = argv[1];
decoderConfig = ma_decoder_config_init(ma_format_f32, (ma_uint32)config->channels, (ma_uint32)config->rate);
resultMA = ma_decoder_init_file(pFilePath, &decoderConfig, &decoder);
if (resultMA == MA_SUCCESS) {
/* Now just keep looping over each sample until we get to the end. */
for (;;) {
float frames[1024];
ma_uint64 frameCount;
resultMA = ma_decoder_read_pcm_frames(&decoder, frames, ma_countof(frames) / config->channels, &frameCount);
if (resultMA != MA_SUCCESS) {
break;
}
result = osaudio_write(audio, frames, (unsigned int)frameCount); /* Safe cast. */
if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) {
printf("Error writing to audio device.");
break;
}
if (result == OSAUDIO_XRUN) {
printf("WARNING: An xrun occurred while writing to the playback device.\n");
}
}
} else {
printf("Failed to open file: %s\n", pFilePath);
}
} else {
printf("No input file.\n");
}
} else {
printf("Unsupported device format.\n");
}
/* Getting here means we're done and we can tear down. */
osaudio_close(audio);
}
void do_duplex()
{
int result;
osaudio_t capture;
osaudio_t playback;
capture = open_device(OSAUDIO_INPUT);
if (capture == NULL) {
printf("Failed to open capture device.\n");
return;
}
playback = open_device(OSAUDIO_OUTPUT);
if (playback == NULL) {
osaudio_close(capture);
printf("Failed to open playback device.\n");
return;
}
for (;;) {
float frames[1024];
unsigned int frameCount;
frameCount = ma_countof(frames) / osaudio_get_info(capture)->configs[0].channels;
/* Capture. */
result = osaudio_read(capture, frames, frameCount);
if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) {
printf("Error reading from capture device.\n");
break;
}
if (result == OSAUDIO_XRUN) {
printf("WARNING: An xrun occurred while reading from the capture device.\n");
}
/* Playback. */
result = osaudio_write(playback, frames, frameCount);
if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) {
printf("Error writing to playback device.\n");
break;
}
if (result == OSAUDIO_XRUN) {
printf("WARNING: An xrun occurred while writing to the playback device.\n");
}
}
osaudio_close(capture);
osaudio_close(playback);
}
int main(int argc, char** argv)
{
int mode = MODE_PLAYBACK;
int iarg;
enumerate_devices();
for (iarg = 0; iarg < argc; iarg += 1) {
if (strcmp(argv[iarg], "capture") == 0) {
mode = MODE_CAPTURE;
} else if (strcmp(argv[iarg], "duplex") == 0) {
mode = MODE_DUPLEX;
}
}
switch (mode)
{
case MODE_PLAYBACK: do_playback(argc, argv); break;
case MODE_CAPTURE: break;
case MODE_DUPLEX: do_duplex(); break;
}
(void)argc;
(void)argv;
return 0;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment