]> git.localhorst.tv Git - blank.git/blob - src/audio/audio.cpp
merge common parts of pre- and unload states
[blank.git] / src / audio / audio.cpp
1 #include "ALError.hpp"
2 #include "Audio.hpp"
3 #include "Sound.hpp"
4
5 #include <algorithm>
6 #include <alut.h>
7 #include <iostream>
8 #include <glm/gtc/type_ptr.hpp>
9 #include <glm/gtx/io.hpp>
10
11
12 namespace {
13
14 const char *al_error_string(ALenum num) {
15         switch (num) {
16                 case AL_NO_ERROR:
17                         return "no error";
18                 case AL_INVALID_NAME:
19                         return "invalid name";
20                 case AL_INVALID_ENUM:
21                         return "invalid enum";
22                 case AL_INVALID_VALUE:
23                         return "invalid value";
24                 case AL_INVALID_OPERATION:
25                         return "invalid operation";
26                 case AL_OUT_OF_MEMORY:
27                         return "out of memory";
28         }
29         return "unknown AL error";
30 }
31
32 std::string al_error_append(ALenum num, std::string msg) {
33         return msg + ": " + al_error_string(num);
34 }
35
36 }
37
38 namespace blank {
39
40 ALError::ALError(ALenum num)
41 : std::runtime_error(al_error_string(num)) {
42
43 }
44
45 ALError::ALError(ALenum num, const std::string &msg)
46 : std::runtime_error(al_error_append(num, msg)) {
47
48 }
49
50
51 Audio::Audio()
52 : last_free(0) {
53         alGenSources(NUM_SRC, source);
54         ALenum err = alGetError();
55         if (err != AL_NO_ERROR) {
56                 throw ALError(err, "alGenSources");
57         }
58         for (std::size_t i = 0; i < NUM_SRC; ++i) {
59                 alSourcef(source[i], AL_REFERENCE_DISTANCE, 2.0f);
60                 alSourcef(source[i], AL_ROLLOFF_FACTOR, 1.0f);
61         }
62 }
63
64 Audio::~Audio() {
65         alDeleteSources(NUM_SRC, source);
66         ALenum err = alGetError();
67         if (err != AL_NO_ERROR) {
68                 std::cerr << "warning: alDeleteSources failed with " << al_error_string(err) << std::endl;
69                 //throw ALError(err, "alDeleteSources");
70         }
71 }
72
73 void Audio::Position(const glm::vec3 &pos) noexcept {
74         alListenerfv(AL_POSITION, glm::value_ptr(pos));
75         //std::cout << "listener at " << pos << std::endl;
76 }
77
78 void Audio::Velocity(const glm::vec3 &vel) noexcept {
79         alListenerfv(AL_VELOCITY, glm::value_ptr(vel));
80 }
81
82 void Audio::Orientation(const glm::vec3 &dir, const glm::vec3 &up) noexcept {
83         ALfloat orient[6] = {
84                 dir.x, dir.y, dir.z,
85                 up.x, up.y, up.z,
86         };
87         alListenerfv(AL_ORIENTATION, orient);
88 }
89
90 void Audio::Play(
91         const Sound &sound,
92         const glm::vec3 &pos,
93         const glm::vec3 &vel,
94         const glm::vec3 &dir
95 ) noexcept {
96         int i = NextFree();
97         if (i < 0) {
98                 std::cerr << "unable to find free audio source" << std::endl;
99                 return;
100         }
101
102         ALuint src = source[i];
103         IntervalTimer &t = timer[i];
104
105         sound.Bind(src);
106         alSourcefv(src, AL_POSITION, glm::value_ptr(pos));
107         alSourcefv(src, AL_VELOCITY, glm::value_ptr(vel));
108         alSourcefv(src, AL_DIRECTION, glm::value_ptr(dir));
109         alSourcePlay(src);
110
111         t = IntervalTimer(sound.Duration());
112         t.Start();
113 }
114
115 void Audio::StopAll() noexcept {
116         alSourceStopv(NUM_SRC, source);
117         for (std::size_t i = 0; i < NUM_SRC; ++i) {
118                 alSourcei(source[i], AL_BUFFER, AL_NONE);
119         }
120 }
121
122 void Audio::Update(int dt) noexcept {
123         for (std::size_t i = 0; i < NUM_SRC; ++i) {
124                 timer[i].Update(dt);
125                 if (timer[i].HitOnce()) {
126                         timer[i].Stop();
127                         alSourceStop(source[i]);
128                         alSourcei(source[i], AL_BUFFER, AL_NONE);
129                         last_free = i;
130                 }
131         }
132 }
133
134 int Audio::NextFree() noexcept {
135         if (!timer[last_free].Running()) {
136                 return last_free;
137         }
138         for (int i = (last_free + 1) % NUM_SRC; i != last_free; i = (i + 1) % NUM_SRC) {
139                 if (!timer[i].Running()) {
140                         last_free = i;
141                         return i;
142                 }
143         }
144         return -1;
145 }
146
147
148 Sound::Sound()
149 : handle(AL_NONE)
150 , duration(0) {
151         alGenBuffers(1, &handle);
152         ALenum err = alGetError();
153         if (err != AL_NO_ERROR) {
154                 throw ALError(err, "alGenBuffers");
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                         //throw ALError(err, "alDeleteBuffers");
180                 }
181         }
182 }
183
184 Sound::Sound(Sound &&other)
185 : handle(other.handle) {
186         other.handle = AL_NONE;
187 }
188
189 Sound &Sound::operator =(Sound &&other) {
190         std::swap(handle, other.handle);
191         return *this;
192 }
193
194 void Sound::Bind(ALuint src) const {
195         alSourcei(src, AL_BUFFER, handle);
196 }
197
198 }