]> git.localhorst.tv Git - blank.git/blob - src/audio/audio.cpp
block sounds depending on block type
[blank.git] / src / audio / audio.cpp
1 #include "ALError.hpp"
2 #include "Audio.hpp"
3 #include "Sound.hpp"
4 #include "SoundBank.hpp"
5
6 #include "../app/Assets.hpp"
7 #include "../shared/ResourceIndex.hpp"
8
9 #include <algorithm>
10 #include <alut.h>
11 #include <iostream>
12 #include <glm/gtc/type_ptr.hpp>
13 #include <glm/gtx/io.hpp>
14
15
16 namespace {
17
18 const char *al_error_string(ALenum num) {
19         switch (num) {
20                 case AL_NO_ERROR:
21                         return "no error";
22                 case AL_INVALID_NAME:
23                         return "invalid name";
24                 case AL_INVALID_ENUM:
25                         return "invalid enum";
26                 case AL_INVALID_VALUE:
27                         return "invalid value";
28                 case AL_INVALID_OPERATION:
29                         return "invalid operation";
30                 case AL_OUT_OF_MEMORY:
31                         return "out of memory";
32         }
33         return "unknown AL error";
34 }
35
36 std::string al_error_append(ALenum num, std::string msg) {
37         return msg + ": " + al_error_string(num);
38 }
39
40 }
41
42 namespace blank {
43
44 ALError::ALError(ALenum num)
45 : std::runtime_error(al_error_string(num)) {
46
47 }
48
49 ALError::ALError(ALenum num, const std::string &msg)
50 : std::runtime_error(al_error_append(num, msg)) {
51
52 }
53
54
55 Audio::Audio()
56 : last_free(0) {
57         alGenSources(NUM_SRC, source);
58         ALenum err = alGetError();
59         if (err != AL_NO_ERROR) {
60                 throw ALError(err, "alGenSources");
61         }
62         for (std::size_t i = 0; i < NUM_SRC; ++i) {
63                 alSourcef(source[i], AL_REFERENCE_DISTANCE, 2.0f);
64                 alSourcef(source[i], AL_ROLLOFF_FACTOR, 1.0f);
65         }
66 }
67
68 Audio::~Audio() {
69         alDeleteSources(NUM_SRC, source);
70         ALenum err = alGetError();
71         if (err != AL_NO_ERROR) {
72                 std::cerr << "warning: alDeleteSources failed with " << al_error_string(err) << std::endl;
73                 //throw ALError(err, "alDeleteSources");
74         }
75 }
76
77 void Audio::Position(const glm::vec3 &pos) noexcept {
78         alListenerfv(AL_POSITION, glm::value_ptr(pos));
79         //std::cout << "listener at " << pos << std::endl;
80 }
81
82 void Audio::Velocity(const glm::vec3 &vel) noexcept {
83         alListenerfv(AL_VELOCITY, glm::value_ptr(vel));
84 }
85
86 void Audio::Orientation(const glm::vec3 &dir, const glm::vec3 &up) noexcept {
87         ALfloat orient[6] = {
88                 dir.x, dir.y, dir.z,
89                 up.x, up.y, up.z,
90         };
91         alListenerfv(AL_ORIENTATION, orient);
92 }
93
94 void Audio::Play(
95         const Sound &sound,
96         const glm::vec3 &pos,
97         const glm::vec3 &vel,
98         const glm::vec3 &dir
99 ) noexcept {
100         int i = NextFree();
101         if (i < 0) {
102                 std::cerr << "unable to find free audio source" << std::endl;
103                 return;
104         }
105
106         ALuint src = source[i];
107         IntervalTimer &t = timer[i];
108
109         sound.Bind(src);
110         alSourcefv(src, AL_POSITION, glm::value_ptr(pos));
111         alSourcefv(src, AL_VELOCITY, glm::value_ptr(vel));
112         alSourcefv(src, AL_DIRECTION, glm::value_ptr(dir));
113         alSourcePlay(src);
114
115         t = IntervalTimer(sound.Duration());
116         t.Start();
117 }
118
119 void Audio::StopAll() noexcept {
120         alSourceStopv(NUM_SRC, source);
121         for (std::size_t i = 0; i < NUM_SRC; ++i) {
122                 alSourcei(source[i], AL_BUFFER, AL_NONE);
123         }
124 }
125
126 void Audio::Update(int dt) noexcept {
127         for (std::size_t i = 0; i < NUM_SRC; ++i) {
128                 timer[i].Update(dt);
129                 if (timer[i].HitOnce()) {
130                         timer[i].Stop();
131                         alSourceStop(source[i]);
132                         alSourcei(source[i], AL_BUFFER, AL_NONE);
133                         last_free = i;
134                 }
135         }
136 }
137
138 int Audio::NextFree() noexcept {
139         if (!timer[last_free].Running()) {
140                 return last_free;
141         }
142         for (int i = (last_free + 1) % NUM_SRC; i != last_free; i = (i + 1) % NUM_SRC) {
143                 if (!timer[i].Running()) {
144                         last_free = i;
145                         return i;
146                 }
147         }
148         return -1;
149 }
150
151
152 Sound::Sound()
153 : handle(AL_NONE)
154 , duration(0) {
155
156 }
157
158 Sound::Sound(const char *file)
159 : handle(alutCreateBufferFromFile(file)) {
160         if (handle == AL_NONE) {
161                 throw ALError(alGetError(), "alutCreateBufferFromFile");
162         }
163
164         ALint size, channels, bits, freq;
165         alGetBufferi(handle, AL_SIZE, &size);
166         alGetBufferi(handle, AL_CHANNELS, &channels);
167         alGetBufferi(handle, AL_BITS, &bits);
168         alGetBufferi(handle, AL_FREQUENCY, &freq);
169
170         duration = size * 8 * 1000 / (channels * bits * freq);
171 }
172
173 Sound::~Sound() {
174         if (handle != AL_NONE) {
175                 alDeleteBuffers(1, &handle);
176                 ALenum err = alGetError();
177                 if (err != AL_NO_ERROR) {
178                         std::cerr << "warning: alDeleteBuffers failed with " << al_error_string(err) << std::endl;
179                 }
180         }
181 }
182
183 Sound::Sound(Sound &&other)
184 : handle(other.handle)
185 , duration(other.duration) {
186         other.handle = AL_NONE;
187 }
188
189 Sound &Sound::operator =(Sound &&other) {
190         std::swap(handle, other.handle);
191         std::swap(duration, other.duration);
192         return *this;
193 }
194
195 void Sound::Bind(ALuint src) const {
196         alSourcei(src, AL_BUFFER, handle);
197 }
198
199
200 SoundBank::SoundBank()
201 : sounds() {
202
203 }
204
205 void SoundBank::Load(const AssetLoader &loader, const ResourceIndex &index) {
206         sounds.clear();
207         sounds.resize(index.Size());
208         for (const auto &entry : index.Entries()) {
209                 sounds[entry.second] = loader.LoadSound(entry.first);
210         }
211 }
212
213 }