diff --git a/samples/openal/Makefile b/samples/openal/Makefile new file mode 100644 index 000000000..db6099a6d --- /dev/null +++ b/samples/openal/Makefile @@ -0,0 +1,6 @@ +XBE_TITLE = openal +GEN_XISO = $(XBE_TITLE).iso +SRCS += $(wildcard $(CURDIR)/*.c) +NXDK_DIR = $(CURDIR)/../.. +NXDK_SDL = y +include $(NXDK_DIR)/Makefile diff --git a/samples/openal/alhelpers.c b/samples/openal/alhelpers.c new file mode 100644 index 000000000..fab039e90 --- /dev/null +++ b/samples/openal/alhelpers.c @@ -0,0 +1,116 @@ +/* + * OpenAL Helpers + * + * Copyright (c) 2011 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains routines to help with some menial OpenAL-related tasks, + * such as opening a device and setting up a context, closing the device and + * destroying its context, converting between frame counts and byte lengths, + * finding an appropriate buffer format, and getting readable strings for + * channel configs and sample types. */ + +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "alhelpers.h" + + +/* InitAL opens a device and sets up a context using default attributes, making + * the program ready to call OpenAL functions. */ +int InitAL(char ***argv, int *argc) +{ + const ALCchar *name; + ALCdevice *device; + ALCcontext *ctx; + + /* Open and initialize a device */ + device = NULL; + if(argc && argv && *argc > 1 && strcmp((*argv)[0], "-device") == 0) + { + device = alcOpenDevice((*argv)[1]); + if(!device) + fprintf(stderr, "Failed to open \"%s\", trying default\n", (*argv)[1]); + (*argv) += 2; + (*argc) -= 2; + } + if(!device) + device = alcOpenDevice(NULL); + if(!device) + { + fprintf(stderr, "Could not open a device!\n"); + return 1; + } + + ctx = alcCreateContext(device, NULL); + if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE) + { + if(ctx != NULL) + alcDestroyContext(ctx); + alcCloseDevice(device); + fprintf(stderr, "Could not set a context!\n"); + return 1; + } + + name = NULL; + if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT")) + name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER); + if(!name || alcGetError(device) != AL_NO_ERROR) + name = alcGetString(device, ALC_DEVICE_SPECIFIER); + printf("Opened \"%s\"\n", name); + + return 0; +} + +/* CloseAL closes the device belonging to the current context, and destroys the + * context. */ +void CloseAL(void) +{ + ALCdevice *device; + ALCcontext *ctx; + + ctx = alcGetCurrentContext(); + if(ctx == NULL) + return; + + device = alcGetContextsDevice(ctx); + + alcMakeContextCurrent(NULL); + alcDestroyContext(ctx); + alcCloseDevice(device); +} + + +const char *FormatName(ALenum format) +{ + switch(format) + { + case AL_FORMAT_MONO8: return "Mono, U8"; + case AL_FORMAT_MONO16: return "Mono, S16"; + case AL_FORMAT_STEREO8: return "Stereo, U8"; + case AL_FORMAT_STEREO16: return "Stereo, S16"; + } + return "Unknown Format"; +} diff --git a/samples/openal/alhelpers.h b/samples/openal/alhelpers.h new file mode 100644 index 000000000..41a7ce58f --- /dev/null +++ b/samples/openal/alhelpers.h @@ -0,0 +1,25 @@ +#ifndef ALHELPERS_H +#define ALHELPERS_H + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +#include "threads.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Some helper functions to get the name from the format enums. */ +const char *FormatName(ALenum type); + +/* Easy device init/deinit functions. InitAL returns 0 on success. */ +int InitAL(char ***argv, int *argc); +void CloseAL(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ALHELPERS_H */ diff --git a/samples/openal/main.c b/samples/openal/main.c new file mode 100644 index 000000000..19c332c2e --- /dev/null +++ b/samples/openal/main.c @@ -0,0 +1,154 @@ +/* + * OpenAL Source Play Example + * + * Copyright (c) 2017 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains an example for playing a sound buffer. */ + +#include +#include + +#include + +#include "AL/al.h" +#include "AL/alc.h" + +#include "alhelpers.h" + + +/* LoadBuffer loads the named audio file into an OpenAL buffer object, and + * returns the new buffer ID. + */ +static ALuint LoadSound(const char *filename) +{ + SDL_AudioSpec wav_spec; + Uint32 wav_length; + Uint8 *wav_buffer; + ALenum err, format; + ALuint buffer; + + /* Load the WAV */ + if (SDL_LoadWAV("test.wav", &wav_spec, &wav_buffer, &wav_length) == NULL) { + fprintf(stderr, "Could not open audio in %s: %s\n", filename, SDL_GetError()); + return 0; + } + + /* Get the sound format, and figure out the OpenAL format */ + if(wav_spec.channels == 1) + { + if(wav_spec.format == AUDIO_U8) + format = AL_FORMAT_MONO8; + else if(wav_spec.format == AUDIO_S16SYS) + format = AL_FORMAT_MONO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", wav_spec.format); + SDL_FreeWAV(wav_buffer); + return 0; + } + } + else if(wav_spec.channels == 2) + { + if(wav_spec.format == AUDIO_U8) + format = AL_FORMAT_STEREO8; + else if(wav_spec.format == AUDIO_S16SYS) + format = AL_FORMAT_STEREO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", wav_spec.format); + SDL_FreeWAV(wav_buffer); + return 0; + } + } + else + { + fprintf(stderr, "Unsupported channel count: %d\n", wav_spec.channels); + SDL_FreeWAV(wav_buffer); + return 0; + } + + /* Buffer the audio data into a new buffer object, then free the data and + * close the file. */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, wav_buffer, wav_length, wav_spec.freq); + SDL_FreeWAV(wav_buffer); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(buffer && alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main(int argc, char **argv) +{ + ALuint source, buffer; + ALfloat offset; + ALenum state; + + /* Initialize OpenAL. */ + if(InitAL(&argv, &argc) != 0) + return 1; + + /* Load the sound into a buffer. */ + buffer = LoadSound("D:\\sound.wav"); + if(!buffer) + { + CloseAL(); + return 1; + } + + /* Create the source to play the sound with. */ + source = 0; + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play the sound until it finishes. */ + alSourcePlay(source); + do { + al_nssleep(10000000); + alGetSourcei(source, AL_SOURCE_STATE, &state); + + /* Get the source offset. */ + alGetSourcef(source, AL_SEC_OFFSET, &offset); + printf("\rOffset: %f ", offset); + fflush(stdout); + } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); + printf("\n"); + + /* All done. Delete resources, and close down OpenAL. */ + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); + + CloseAL(); + + return 0; +}