Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark

MikuAuahDark/main.c

Created Nov 19, 2020
Embed
What would you like to do?
OpenAL-soft test program used in https://www.youtube.com/watch?v=bAkJcaMlc1E
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <AL/al.h>
#include <AL/alc.h>
#include "sleep.h"
int globalStopProgram = 0;
struct WaveFileInfo
{
unsigned short format, channels;
unsigned int sampleRate, bytesPerSecAvg;
unsigned short blockAlign, bitsPerSample;
unsigned int dataSize;
void *data;
};
static void interruptHand(int sig)
{
puts("Interrupt!");
globalStopProgram = 1;
}
/* This is simple WAV file loader */
static const char *loadWavFile(const char *filename, struct WaveFileInfo *out)
{
FILE *f = fopen(filename, "rb");
const char *err = NULL;
/* Type punning is bad but okay in C */
union Int3264Char
{
unsigned char c[8];
unsigned long long i64;
struct Int64as232
{
unsigned int low;
unsigned int high;
} i32;
} temp;
#define ERRCHECK(expr) if (expr) {err = strerror(errno); goto bruh;}
#define ERRCHECK2(expr, errmsg) if (expr) {err = errmsg; goto bruh;}
if (f == NULL) return strerror(errno);
out->data = NULL;
/* Bad practice but whatever */
ERRCHECK(fread(temp.c, 1, 8, f) != 8);
ERRCHECK2(memcmp(temp.c, "RIFF", 4), "Not RIFF/RIFF big endian");
ERRCHECK(fread(temp.c, 1, 8, f) != 8);
ERRCHECK2(memcmp(temp.c, "WAVEfmt ", 8), "Not WAVE (followed by fmt)");
ERRCHECK(fread(temp.c, 1, 4, f) != 4);
ERRCHECK2(temp.i32.low != 16, "invalid fmt size");
ERRCHECK(fread(out, 1, 16, f) != 16);
ERRCHECK2(out->format != 1, "Not raw PCM");
ERRCHECK2(out->bitsPerSample != 8 && out->bitsPerSample != 16, "Only 8-bit or 16-bit is supported");
ERRCHECK2(out->channels > 2, "Channel > 2 is not supported");
/* Find "data" chunk */
for (;;)
{
ERRCHECK(fread(temp.c, 1, 8, f) != 8);
if (memcmp(temp.c, "data", 4) == 0)
{
/* Found */
out->dataSize = temp.i32.high;
out->data = malloc(out->dataSize);
ERRCHECK2(out->data == NULL, "Not enough memory");
ERRCHECK(fread(out->data, 1, out->dataSize, f) != out->dataSize);
goto ok;
}
ERRCHECK(fseek(f, (long) temp.i32.high, SEEK_CUR));
}
#undef ERRCHECK
#undef ERRCHECK2
bruh:
free(out->data);
ok:
fclose(f);
return err;
}
/* Convert bits/sample and channels to AL_FORMAT_* */
static ALenum toAlFormat(unsigned short channels, unsigned short bps)
{
switch (bps)
{
case 16:
return channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
case 8:
return channels == 2 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;
default:
return -1;
}
}
int main(int argc, char *argv[])
{
struct WaveFileInfo waveData;
if (argc < 2)
{
printf("Usage: %s <audio.wav>\n", argv[0]);
return 1;
}
const char *wavErr = loadWavFile(argv[1], &waveData);
if (wavErr)
{
fputs(wavErr, stderr);
fputc('\n', stderr);
return 1;
}
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
{
const char *devs = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
const char *next = devs + 1;
size_t len = 0;
puts("Device list:");
while (devs && *devs != '\0' && next && *next != '\0') {
printf("* %s\n", devs);
len = strlen(devs);
devs += (len + 1);
next += (len + 2);
}
puts("-----");
}
ALCdevice *device = alcOpenDevice(NULL);
if (device == NULL)
{
fputs("Cannot create device\n", stderr);
free(waveData.data);
return 1;
}
ALCcontext *context = alcCreateContext(device, NULL);
if (context == NULL)
{
fputs("Cannot create context\n", stderr);
free(waveData.data);
alcCloseDevice(device);
return 1;
}
if (!alcMakeContextCurrent(context))
{
fputs("Cannot set current context\n", stderr);
free(waveData.data);
alcDestroyContext(context);
alcCloseDevice(device);
return 1;
}
/* Again, bad practice but for sake of smaller codesize */
#define ERRCHECK(x) \
{\
x; \
errstatus = alGetError(); \
if (errstatus != AL_NO_ERROR) \
{ \
fputs(alGetString(errstatus), stderr); \
alcMakeContextCurrent(NULL); \
alcDestroyContext(context); \
alcCloseDevice(device); \
free(waveData.data); \
return 1; \
} \
}
ALuint source, buffer;
ALenum errstatus;
ERRCHECK(alGenSources(1, &source));
ERRCHECK(alGenBuffers(1, &buffer));
ERRCHECK(alBufferData(buffer, toAlFormat(waveData.channels, waveData.bitsPerSample), waveData.data, waveData.dataSize, waveData.sampleRate));
ERRCHECK(alSourcei(source, AL_BUFFER, buffer));
ERRCHECK(alSourcei(source, AL_LOOPING, argc > 2 && strcmp(argv[2], "loop") == 0));
ERRCHECK(alSourcePlay(source));
signal(SIGINT, &interruptHand);
for (; !globalStopProgram;)
{
ALint state;
ERRCHECK(alGetSourcei(source, AL_SOURCE_STATE, &state));
if (state == AL_PLAYING)
{
msleep(50);
continue;
}
break;
}
alDeleteSources(1, &source);
alDeleteBuffers(1, &buffer);
alcMakeContextCurrent(NULL);
alcDestroyContext(context);
alcCloseDevice(device);
free(waveData.data);
return 0;
}
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void (__stdcall *msleep)(unsigned int) = (void (__stdcall *)(unsigned int)) &Sleep;
#else
#include <time.h>
void msleep(unsigned int ms)
{
struct timespec ts;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000L;
nanosleep(&ts, NULL);
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment