1 | // SuperTux |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "audio/sound_file.hpp" |
18 | #include "audio/sound_manager.hpp" |
19 | #include "audio/stream_sound_source.hpp" |
20 | #include "supertux/globals.hpp" |
21 | #include "util/log.hpp" |
22 | |
23 | StreamSoundSource::StreamSoundSource() : |
24 | m_file(), |
25 | m_fade_state(NoFading), |
26 | m_fade_start_time(), |
27 | m_fade_time(), |
28 | m_looping(false) |
29 | { |
30 | alGenBuffers(STREAMFRAGMENTS, m_buffers); |
31 | try |
32 | { |
33 | SoundManager::check_al_error("Couldn't allocate audio buffers: " ); |
34 | } |
35 | catch(std::exception& e) |
36 | { |
37 | log_warning << e.what() << std::endl; |
38 | } |
39 | //add me to update list |
40 | SoundManager::current()->register_for_update( this ); |
41 | } |
42 | |
43 | StreamSoundSource::~StreamSoundSource() |
44 | { |
45 | //don't update me any longer |
46 | SoundManager::current()->remove_from_update( this ); |
47 | m_file.reset(); |
48 | stop(); |
49 | alDeleteBuffers(STREAMFRAGMENTS, m_buffers); |
50 | try |
51 | { |
52 | SoundManager::check_al_error("Couldn't delete audio buffers: " ); |
53 | } |
54 | catch(std::exception& e) |
55 | { |
56 | // Am I bovvered? |
57 | log_warning << e.what() << std::endl; |
58 | } |
59 | } |
60 | |
61 | void |
62 | StreamSoundSource::set_sound_file(std::unique_ptr<SoundFile> newfile) |
63 | { |
64 | m_file = std::move(newfile); |
65 | |
66 | ALint queued; |
67 | alGetSourcei(m_source, AL_BUFFERS_QUEUED, &queued); |
68 | for (size_t i = 0; i < STREAMFRAGMENTS - queued; ++i) { |
69 | if (fillBufferAndQueue(m_buffers[i]) == false) |
70 | break; |
71 | } |
72 | } |
73 | |
74 | void |
75 | StreamSoundSource::update() |
76 | { |
77 | ALint processed = 0; |
78 | alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &processed); |
79 | for (ALint i = 0; i < processed; ++i) { |
80 | ALuint buffer; |
81 | alSourceUnqueueBuffers(m_source, 1, &buffer); |
82 | try |
83 | { |
84 | SoundManager::check_al_error("Couldn't unqueue audio buffer: " ); |
85 | } |
86 | catch(std::exception& e) |
87 | { |
88 | log_warning << e.what() << std::endl; |
89 | } |
90 | |
91 | if (fillBufferAndQueue(buffer) == false) |
92 | break; |
93 | } |
94 | |
95 | if (!playing()) { |
96 | if (processed == 0 || !m_looping) |
97 | return; |
98 | |
99 | // we might have to restart the source if we had a buffer underrun |
100 | log_info << "Restarting audio source because of buffer underrun" << std::endl; |
101 | play(); |
102 | } |
103 | |
104 | if (m_fade_state == FadingOn || m_fade_state == FadingResume) { |
105 | float time = g_real_time - m_fade_start_time; |
106 | if (time >= m_fade_time) { |
107 | set_gain(1.0); |
108 | m_fade_state = NoFading; |
109 | } else { |
110 | set_gain(time / m_fade_time); |
111 | } |
112 | } else if (m_fade_state == FadingOff || m_fade_state == FadingPause) { |
113 | float time = g_real_time - m_fade_start_time; |
114 | if (time >= m_fade_time) { |
115 | if (m_fade_state == FadingOff) |
116 | stop(); |
117 | else |
118 | pause(); |
119 | m_fade_state = NoFading; |
120 | } else { |
121 | set_gain( (m_fade_time - time) / m_fade_time); |
122 | } |
123 | } |
124 | } |
125 | |
126 | void |
127 | StreamSoundSource::set_fading(FadeState state, float fade_time_) |
128 | { |
129 | m_fade_state = state; |
130 | m_fade_time = fade_time_; |
131 | m_fade_start_time = g_real_time; |
132 | } |
133 | |
134 | bool |
135 | StreamSoundSource::fillBufferAndQueue(ALuint buffer) |
136 | { |
137 | // fill buffer |
138 | std::unique_ptr<char[]> bufferdata(new char[STREAMFRAGMENTSIZE]); |
139 | size_t bytesread = 0; |
140 | do { |
141 | bytesread += m_file->read(bufferdata.get() + bytesread, |
142 | STREAMFRAGMENTSIZE - bytesread); |
143 | // end of sound file |
144 | if (bytesread < STREAMFRAGMENTSIZE) { |
145 | if (m_looping) |
146 | m_file->reset(); |
147 | else |
148 | break; |
149 | } |
150 | } while(bytesread < STREAMFRAGMENTSIZE); |
151 | |
152 | if (bytesread > 0) { |
153 | ALenum format = SoundManager::get_sample_format(*m_file); |
154 | try |
155 | { |
156 | alBufferData(buffer, format, bufferdata.get(), static_cast<ALsizei>(bytesread), m_file->m_rate); |
157 | SoundManager::check_al_error("Couldn't refill audio buffer: " ); |
158 | |
159 | alSourceQueueBuffers(m_source, 1, &buffer); |
160 | SoundManager::check_al_error("Couldn't queue audio buffer: " ); |
161 | } |
162 | catch(std::exception& e) |
163 | { |
164 | log_warning << e.what() << std::endl; |
165 | } |
166 | } |
167 | |
168 | // return false if there aren't more buffers to fill |
169 | return bytesread >= STREAMFRAGMENTSIZE; |
170 | } |
171 | |
172 | /* EOF */ |
173 | |