21 const ALCchar* dev_string = alcGetString(
nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
23 return dev_string ? std::string(dev_string) : std::string();
27 assert(alcIsExtensionPresent(
nullptr,
"ALC_ENUMERATE_ALL_EXT"));
28 const ALCchar* dev_strings = alcGetString(
nullptr, ALC_ALL_DEVICES_SPECIFIER);
31 std::vector<std::string> device_names;
34 while (strlen(dev_strings) != 0) {
35 device_names.emplace_back(dev_strings);
37 dev_strings += device_names.back().length() + 1;
42 oal_device(alcOpenDevice(device.empty() ? nullptr : device.c_str()))
45 oal_context = alcCreateContext(oal_device,
nullptr);
46 alcMakeContextCurrent(oal_context);
51 alcMakeContextCurrent(oal_context);
55 const ALCchar* device_name = alcGetString(oal_device, ALC_ALL_DEVICES_SPECIFIER);
56 return std::string(device_name);
61 for (
const auto& buf : sample_buffers) {
62 alDeleteBuffers(1, &buf.second);
64 auto*
const context = alcGetCurrentContext();
65 auto*
const device = alcGetContextsDevice(context);
66 alcMakeContextCurrent(
nullptr);
67 alcDestroyContext(context);
68 alcCloseDevice(device);
70bool read_soundfile_to_memory_buffer(SndfileHandle& soundfile,
OALSoundFormat& format, std::vector<int16_t>& memory_buffer)
72 if (soundfile.error() != SF_ERR_NO_ERROR) {
73 std::cerr << soundfile.strError() << std::endl;
76 assert(soundfile.channels() == 1 || soundfile.channels() == 2);
77 format.nr_channels = OALSoundFormat::Channels(soundfile.channels());
78 format.nr_frames = int(soundfile.frames());
79 format.sampling_rate = soundfile.samplerate();
80 format.value_type = OALSoundFormat::SFT_INT16;
81 size_t num_values = soundfile.frames() * soundfile.channels();
82 memory_buffer.resize(num_values);
84 soundfile.command(SFC_SET_SCALE_FLOAT_INT_READ,
nullptr, SF_TRUE);
85 soundfile.readf(memory_buffer.data(), soundfile.frames());
86 if (soundfile.error() != SF_ERR_NO_ERROR) {
87 std::cerr << soundfile.strError() << std::endl;
94 const char* data_ptr =
reinterpret_cast<const char*
>(data);
98 const char*
const data;
100 const char* current_byte_ptr;
101 } vio_state{ data_ptr, data_length, data_ptr };
104 sfvio.get_filelen = [](
void* user_data) -> sf_count_t {
105 return static_cast<const VIO_state*
>(user_data)->data_length;
107 sfvio.seek = [](sf_count_t offset,
int whence,
void* user_data) -> sf_count_t {
108 auto* state =
static_cast<VIO_state*
>(user_data);
111 state->current_byte_ptr += offset;
112 return state->current_byte_ptr - state->data;
114 state->current_byte_ptr = state->data + offset;
117 state->current_byte_ptr = (state->data + state->data_length - 1) - offset;
118 return state->current_byte_ptr - state->data;
123 sfvio.read = [](
void* ptr, sf_count_t count,
void* user_data) -> sf_count_t {
124 auto* state =
static_cast<VIO_state*
>(user_data);
125 memcpy(ptr, state->current_byte_ptr, count);
126 state->current_byte_ptr += count;
129 sfvio.write = [](
const void* ptr, sf_count_t count,
void* user_data) -> sf_count_t {
return 0; };
130 sfvio.tell = [](
void* user_data) -> sf_count_t {
131 auto* state =
static_cast<const VIO_state*
>(user_data);
132 return state->current_byte_ptr - state->data;
134 SndfileHandle soundfile(sfvio, &vio_state);
135 return read_soundfile_to_memory_buffer(soundfile, format, memory_buffer);
139 std::filesystem::path path(filepath);
140 if (path.empty() || !std::filesystem::exists(path))
142 SndfileHandle soundfile(path.string().c_str());
143 if (soundfile.error() != SF_ERR_NO_ERROR) {
144 std::cerr << soundfile.strError() << std::endl;
147 return read_soundfile_to_memory_buffer(soundfile, format, memory_buffer);
151 if (symbolic_name.empty())
152 throw std::invalid_argument(
"Symbolic file name can not be empty!");
153 if (std::end(sample_buffers) != sample_buffers.find(symbolic_name))
154 throw std::invalid_argument(
"Symbolic name \"" + symbolic_name +
"\" already present");
160 alGenBuffers(1, &buffer);
161 assert(AL_NO_ERROR == alcGetError(oal_device));
162 alBufferData(buffer, format.value_type == OALSoundFormat::SFT_UINT8 ?
163 (format.nr_channels == OALSoundFormat::Channels::SFC_STEREO ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8)
164 : (format.nr_channels == OALSoundFormat::Channels::SFC_STEREO ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16),
165 data, (ALsizei)data_length, (ALsizei)format.sampling_rate);
166 assert(AL_NO_ERROR == alcGetError(oal_device));
167 sample_buffers.emplace(symbolic_name, buffer);
171 if (symbolic_name.empty()) {
172 std::filesystem::path path(filepath);
173 if (path.empty() || !std::filesystem::exists(path))
175 assert(path.has_filename());
176 symbolic_name = path.stem().string();
179 std::vector<int16_t> memory_buffer;
182 create_buffer(symbolic_name, format, memory_buffer.data(),
sizeof(int16_t) * memory_buffer.size());
186 std::vector<int16_t> memory_buffer;
190 create_buffer(symbolic_name, format,
reinterpret_cast<const char*
>(memory_buffer.data()),
sizeof(int16_t)*memory_buffer.size());
194 std::filesystem::path f(folder);
195 if (!std::filesystem::is_directory(f)) {
196 std::cerr << __FUNCTION__ <<
": no valid folder '" << folder <<
"'";
201 for (
auto const& entry : std::filesystem::recursive_directory_iterator(f)) {
202 if (!entry.is_regular_file())
204 load_sample(
static_cast<std::filesystem::path
>(entry).
string());
208 for (
auto const& entry : std::filesystem::directory_iterator(f)) {
209 if (!entry.is_regular_file())
211 load_sample(
static_cast<std::filesystem::path
>(entry).
string());
217 auto buffer_id = sample_buffers.find(sound_name);
218 return buffer_id != sample_buffers.end() ? buffer_id->second : AL_NONE;
227 auto reset_function = (LPALCRESETDEVICESOFT)alcGetProcAddress(oal_device,
"alcResetDeviceSOFT");
229 std::array<ALCint, 2> attribs = {ALC_OUTPUT_MODE_SOFT, active ? ALC_STEREO_HRTF_SOFT : ALC_STEREO_BASIC_SOFT};
230 reset_function(oal_device, attribs.data());
242 alDeleteSources(1, &src_id);
247OALSource::~OALSource()
254 this->ctx_ptr = &ctx;
257 alGenSources(1, &src_id);
258 if (!sound_name.empty())
259 append_sound_impl(sound_name);
266 return append_sound_impl(sound_name);
269bool OALSource::append_sound_impl(std::string sound_name)
275 alSourcei(src_id, AL_BUFFER, buf_id);
279 alSourceQueueBuffers(src_id, 1, &buf_id);
295 alSourcei(src_id, AL_LOOPING, should_loop ? AL_TRUE : AL_FALSE);
301 alGetSourcefv(src_id, AL_POSITION, vec.
data());
308 alGetSourcefv(src_id, AL_VELOCITY, vec.
data());
315 alGetSourcef(src_id, AL_PITCH, &pitch);
322 alGetSourcef(src_id, AL_GAIN, &gain);
329 alGetSourcei(src_id, AL_LOOPING, &looping);
330 return looping == AL_TRUE;
336 alGetSourcei(src_id, AL_PLAYING, &playing);
337 return playing == AL_TRUE;
343 alGetSourcei(src_id, AL_SOURCE_STATE, &state);
344 return OALSourceState(state - AL_INITIAL);
368 alGetListenerfv(AL_POSITION, pos.
data());
375 alGetListenerfv(AL_VELOCITY, vel.
data());
382 alGetListenerfv(AL_ORIENTATION, orientation.
data());
392 const std::array<float, 6> orientation = {at[0], at[1], at[2], up[0], up[1], up[2]};
393 alListenerfv(AL_ORIENTATION, orientation.data());
This class provides easy sample loading, device enumeration and error retrieval.
void load_samples(std::string folder, bool recursive=false)
Loads all audio files in a folder into the internal buffer list.
ALCdevice * get_native_device()
Gets the native device handle for direct OpenAL calls.
void make_current()
in case of multiple contexts, this makes this context current
std::string get_device_name() const
return device name of contexts device
static std::vector< std::string > enumerate_devices()
Enumerate audio output devices available on system.
std::string get_error_string()
Returns the last error from the OpenAL context.
static std::string get_default_device_name()
returns name of system's default device
static bool decode_sound_file(const void *data, size_t data_length, OALSoundFormat &format, std::vector< int16_t > &memory_buffer)
decode sound file that is already in memory data with data_length bytes to a newly constructed memory...
~OALContext()
detach context from output device and destruct
void create_buffer(const std::string &symbolic_name, const OALSoundFormat &format, const void *data, size_t data_length)
create a named sound buffer from a format descriptor and a data buffer of given length in bytes
void load_sample(std::string filepath, std::string symbolic_name="")
Loads a sound into the internal buffer list.
static bool load_sound_file(const std::string &filepath, OALSoundFormat &format, std::vector< int16_t > &memory_buffer)
load and decode sound file to newly constructed memory buffer
bool is_no_error()
Determines if there were any errors since last call.
void set_HRTF(bool active)
Toggles the OpenAL output mode to use HRTF.
OALContext(const std::string &device_name)
Construct context, connect to output device and make context current.
ALuint get_buffer_id(std::string sound_name) const
Gets the buffer identifier for the symbolic name.
ALCcontext * get_native_context()
Gets the native context handle for direct OpenAL calls.
static cgv::math::fvec< float, 3 > get_position()
Gets the listener position.
static cgv::math::fvec< float, 3 > get_velocity()
Gets the listener velocity.
static void set_position(cgv::math::fvec< float, 3 > pos)
Sets the listener position.
static void set_orientation(cgv::math::fvec< float, 3 > at, cgv::math::fvec< float, 3 > up)
Sets the listener orientation.
static cgv::math::fmat< float, 3, 2 > get_orientation()
Gets the listener orientation.
static void set_velocity(cgv::math::fvec< float, 3 > vel)
Sets the listener velocity.
void clear()
destruct source and restore uninitialized state
bool append_sound(std::string sound_name)
Append buffer of named sound to source's playback list.
void stop()
Stops the playback of the sound buffer and moves the playhead to the beginning.
void set_looping(bool should_loop)
Sets the looping property of the source.
void set_position(cgv::math::fvec< float, 3 > pos)
Sets the source position.
void play_pause(bool should_play)
Explicitly toggles between pausing and playing the sound buffer.
bool init(OALContext &ctx, std::string sound_name="")
This function links a source to a context.
void set_velocity(cgv::math::fvec< float, 3 > vel)
Sets the source velocity.
void play()
Commences the playback of the sound buffer.
void rewind()
Moves the playhead to the beginning of the buffer.
void pause()
Pauses the playback of the sound buffer without moving the playhead.
cgv::math::fvec< float, 3 > get_velocity() const
Gets the source velocity.
bool is_looping() const
Determines if the source has the looping property.
bool is_playing() const
Determines if the source is playing.
OALSourceState get_state() const
returns state of this source
float get_gain() const
Gets the source gain.
float get_pitch() const
Gets the source pitch.
cgv::math::fvec< float, 3 > get_position() const
Gets the source position.
void set_gain(float gain)
Sets the source gain.
void set_pitch(float pitch)
Sets the source pitch.
matrix of fixed size dimensions
A vector with zero based index.
T * data()
cast into array. This allows calls like glVertex<N><T>v(p.data()) instead of glVertex<N><T,...