]> git.localhorst.tv Git - blank.git/blob - src/audio/audio.cpp
give unique IDs to entities
[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         alGenSources(NUM_SRC, source);
53         ALenum err = alGetError();
54         if (err != AL_NO_ERROR) {
55                 throw ALError(err, "alGenSources");
56         }
57         for (std::size_t i = 0; i < NUM_SRC; ++i) {
58                 alSourcef(source[i], AL_REFERENCE_DISTANCE, 2.0f);
59                 alSourcef(source[i], AL_ROLLOFF_FACTOR, 1.0f);
60         }
61 }
62
63 Audio::~Audio() {
64         alDeleteSources(NUM_SRC, source);
65         ALenum err = alGetError();
66         if (err != AL_NO_ERROR) {
67                 std::cerr << "warning: alDeleteSources failed with " << al_error_string(err) << std::endl;
68                 //throw ALError(err, "alDeleteSources");
69         }
70 }
71
72 void Audio::Position(const glm::vec3 &pos) noexcept {
73         alListenerfv(AL_POSITION, glm::value_ptr(pos));
74         //std::cout << "listener at " << pos << std::endl;
75 }
76
77 void Audio::Velocity(const glm::vec3 &vel) noexcept {
78         alListenerfv(AL_VELOCITY, glm::value_ptr(vel));
79 }
80
81 void Audio::Orientation(const glm::vec3 &dir, const glm::vec3 &up) noexcept {
82         ALfloat orient[6] = {
83                 dir.x, dir.y, dir.z,
84                 up.x, up.y, up.z,
85         };
86         alListenerfv(AL_ORIENTATION, orient);
87 }
88
89 void Audio::Play(
90         const Sound &sound,
91         const glm::vec3 &pos,
92         const glm::vec3 &vel,
93         const glm::vec3 &dir
94 ) noexcept {
95         int i = NextFree();
96         if (i < 0) {
97                 std::cerr << "unable to find free audio source" << std::endl;
98                 return;
99         }
100
101         ALuint src = source[i];
102         IntervalTimer &t = timer[i];
103
104         sound.Bind(src);
105         alSourcefv(src, AL_POSITION, glm::value_ptr(pos));
106         alSourcefv(src, AL_VELOCITY, glm::value_ptr(vel));
107         alSourcefv(src, AL_DIRECTION, glm::value_ptr(dir));
108         alSourcePlay(src);
109
110         t = IntervalTimer(sound.Duration());
111         t.Start();
112 }
113
114 void Audio::StopAll() noexcept {
115         alSourceStopv(NUM_SRC, source);
116         for (std::size_t i = 0; i < NUM_SRC; ++i) {
117                 alSourcei(source[i], AL_BUFFER, AL_NONE);
118         }
119 }
120
121 void Audio::Update(int dt) noexcept {
122         for (std::size_t i = 0; i < NUM_SRC; ++i) {
123                 timer[i].Update(dt);
124                 if (timer[i].HitOnce()) {
125                         timer[i].Stop();
126                         alSourceStop(source[i]);
127                         alSourcei(source[i], AL_BUFFER, AL_NONE);
128                         last_free = i;
129                 }
130         }
131 }
132
133 int Audio::NextFree() noexcept {
134         if (!timer[last_free].Running()) {
135                 return last_free;
136         }
137         for (int i = (last_free + 1) % NUM_SRC; i != last_free; i = (i + 1) % NUM_SRC) {
138                 if (!timer[i].Running()) {
139                         last_free = i;
140                         return i;
141                 }
142         }
143         return -1;
144 }
145
146
147 Sound::Sound()
148 : handle(AL_NONE)
149 , duration(0) {
150         alGenBuffers(1, &handle);
151         ALenum err = alGetError();
152         if (err != AL_NO_ERROR) {
153                 throw ALError(err, "alGenBuffers");
154         }
155 }
156
157 Sound::Sound(const char *file)
158 : handle(alutCreateBufferFromFile(file)) {
159         if (handle == AL_NONE) {
160                 throw ALError(alGetError(), "alutCreateBufferFromFile");
161         }
162
163         ALint size, channels, bits, freq;
164         alGetBufferi(handle, AL_SIZE, &size);
165         alGetBufferi(handle, AL_CHANNELS, &channels);
166         alGetBufferi(handle, AL_BITS, &bits);
167         alGetBufferi(handle, AL_FREQUENCY, &freq);
168
169         duration = size * 8 * 1000 / (channels * bits * freq);
170 }
171
172 Sound::~Sound() {
173         if (handle != AL_NONE) {
174                 alDeleteBuffers(1, &handle);
175                 ALenum err = alGetError();
176                 if (err != AL_NO_ERROR) {
177                         std::cerr << "warning: alDeleteBuffers failed with " << al_error_string(err) << std::endl;
178                         //throw ALError(err, "alDeleteBuffers");
179                 }
180         }
181 }
182
183 Sound::Sound(Sound &&other)
184 : handle(other.handle) {
185         other.handle = AL_NONE;
186 }
187
188 Sound &Sound::operator =(Sound &&other) {
189         std::swap(handle, other.handle);
190         return *this;
191 }
192
193 void Sound::Bind(ALuint src) const {
194         alSourcei(src, AL_BUFFER, handle);
195 }
196
197 }