Created
November 19, 2020 09:26
-
-
Save MikuAuahDark/9d6dc0de1d46881d9c2d1a3a7da6d98a to your computer and use it in GitHub Desktop.
OpenAL-soft test program used in https://www.youtube.com/watch?v=bAkJcaMlc1E
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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