KDEGames

kgamesound-openal.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Stefan Majewsky <majewsky@gmx.net>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5*/
6
7#include "kgamesound.h"
8
9// own
10#include "kgameopenalruntime_p.h"
11#include "virtualfileqt-openal.h"
12#include <kdegames_audio_logging.h>
13// sndfile
14#include <sndfile.hh>
15
16class KGameSoundPrivate
17{
18public:
20 qreal m_volume = 1.0;
21 QPointF m_pos;
22
23 bool m_valid = false;
24 ALuint m_buffer = AL_NONE;
25
26public:
27 KGameSoundPrivate() = default;
28};
29
30// BEGIN KGameSound
31
33 : QObject(parent)
34 , d_ptr(new KGameSoundPrivate)
35{
37
38 VirtualFileQt fileInterface(file);
39 if (!fileInterface.open()) {
40 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to open sound file" << file;
41 return;
42 }
43
44 // open sound file
45 SndfileHandle handle(VirtualFileQt::getSndfileVirtualIO(), &fileInterface);
46 if (handle.error()) {
47 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to load sound file" << file << ". Error message from libsndfile follows.";
48 qCWarning(KDEGAMES_AUDIO_LOG) << handle.strError();
49 return;
50 }
51 const int channelCount = handle.channels();
52 const int sampleCount = channelCount * handle.frames();
53 const int sampleRate = handle.samplerate();
54 // load data from sound file
55 QList<ALshort> samples(sampleCount);
56 if (handle.read(samples.data(), sampleCount) < sampleCount) {
57 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to read sound file" << file;
58 qCWarning(KDEGAMES_AUDIO_LOG) << "File ended unexpectedly.";
59 return;
60 }
61 // determine file format from number of channels
62 ALenum format;
63 switch (channelCount) {
64 case 1:
65 format = AL_FORMAT_MONO16;
66 break;
67 case 2:
68 format = AL_FORMAT_STEREO16;
69 break;
70 default:
71 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to read sound file" << file;
72 qCWarning(KDEGAMES_AUDIO_LOG) << "More than two channels are not supported.";
73 return;
74 }
75 // make sure OpenAL is initialized; clear OpenAL error storage
76 KGameOpenALRuntime::instance();
77 int error;
78 alGetError();
79 // create OpenAL buffer
80 alGenBuffers(1, &d->m_buffer);
81 if ((error = alGetError()) != AL_NO_ERROR) {
82 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to create OpenAL buffer: Error code" << error;
83 return;
84 }
85 alBufferData(d->m_buffer, format, samples.data(), sampleCount * sizeof(ALshort), sampleRate);
86 if ((error = alGetError()) != AL_NO_ERROR) {
87 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to fill OpenAL buffer: Error code" << error;
88 alDeleteBuffers(1, &d->m_buffer);
89 return;
90 }
91 // loading finished
92 d->m_valid = true;
93}
94
96{
98
99 if (d->m_valid) {
100 stop();
101 KGameOpenALRuntime::instance()->m_soundsEvents.remove(this);
102 alDeleteBuffers(1, &d->m_buffer);
103 }
104}
105
107{
108 Q_D(const KGameSound);
109
110 return d->m_valid;
111}
112
113KGameSound::PlaybackType KGameSound::playbackType() const
114{
115 Q_D(const KGameSound);
116
117 return d->m_type;
118}
119
121{
123
124 if (d->m_type == type)
125 return;
126 d->m_type = type;
127 Q_EMIT playbackTypeChanged(type);
128}
129
130QPointF KGameSound::pos() const
131{
132 Q_D(const KGameSound);
133
134 return d->m_pos;
135}
136
138{
140
141 if (d->m_pos == pos)
142 return;
143 d->m_pos = pos;
144 Q_EMIT posChanged(pos);
145}
146
147qreal KGameSound::volume() const
148{
149 Q_D(const KGameSound);
150
151 return d->m_volume;
152}
153
154void KGameSound::setVolume(qreal volume)
155{
157
158 if (d->m_volume == volume)
159 return;
160 d->m_volume = volume;
161 Q_EMIT volumeChanged(volume);
162}
163
165{
166 Q_D(const KGameSound);
167
168 return !d->m_valid;
169}
170
172{
174
175 start(d->m_pos);
176}
177
179{
181
182 if (d->m_valid) {
183 KGameOpenALRuntime *runtime = KGameOpenALRuntime::instance();
184 if (!runtime->instance()->m_soundsEvents[this].isEmpty()) {
185 if (runtime->instance()->m_soundsEvents[this].last()->replay(pos) == false) {
186 new KGamePlaybackEvent(this, pos);
187 }
188 } else {
189 new KGamePlaybackEvent(this, pos);
190 }
191 }
192}
193
195{
196 qDeleteAll(KGameOpenALRuntime::instance()->m_soundsEvents.take(this));
197}
198
199// END KGameSound
200// BEGIN KGamePlaybackEvent
201
202KGamePlaybackEvent::KGamePlaybackEvent(KGameSound *sound, QPointF pos)
203 : m_valid(false)
204{
205 // make sure OpenAL is initialized
206 KGameOpenALRuntime *runtime = KGameOpenALRuntime::instance();
207 // clear OpenAL error storage
208 int error;
209 alGetError();
210 // create source for playback
211 alGenSources(1, &m_source);
212 if ((error = alGetError()) != AL_NO_ERROR) {
213 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to create OpenAL source: Error code" << error;
214 return;
215 }
216 // store in OpenALRuntime
217 runtime->m_soundsEvents[sound] << this;
218 m_valid = true;
219 // connect to sound (buffer)
220 alSource3f(m_source, AL_POSITION, pos.x(), pos.y(), 0);
221 alSourcef(m_source, AL_PITCH, 1.0); // TODO: debug
222 alSourcef(m_source, AL_GAIN, sound->volume());
223 alSourcei(m_source, AL_BUFFER, sound->d_ptr->m_buffer);
224 const KGameSound::PlaybackType type = sound->playbackType();
225 alSourcef(m_source, AL_ROLLOFF_FACTOR, type == KGameSound::AmbientPlayback ? 0.0 : 1.0);
226 alSourcei(m_source, AL_SOURCE_RELATIVE, type == KGameSound::RelativePlayback ? AL_TRUE : AL_FALSE);
227 if ((error = alGetError()) != AL_NO_ERROR) {
228 qCWarning(KDEGAMES_AUDIO_LOG) << "Failed to setup OpenAL source: Error code" << error;
229 return;
230 }
231 // start playback
232 alSourcePlay(m_source);
233}
234
235KGamePlaybackEvent::~KGamePlaybackEvent()
236{
237 if (alIsSource(m_source) == AL_TRUE) {
238 alSourceStop(m_source);
239 alDeleteSources(1, &m_source);
240 }
241}
242
243bool KGamePlaybackEvent::isRunning() const
244{
245 ALint state;
246 alGetSourcei(m_source, AL_SOURCE_STATE, &state);
247 return state == AL_PLAYING;
248}
249
250bool KGamePlaybackEvent::replay(QPointF pos) const
251{
252 if (alIsSource(m_source) == AL_TRUE) {
253 alSourceStop(m_source);
254 alSource3f(m_source, AL_POSITION, pos.x(), pos.y(), 0);
255 alSourcePlay(m_source);
256 return true;
257 } else {
258 return false;
259 }
260}
261
262// END KGamePlaybackEvent
263
264#include "moc_kgamesound.cpp"
This class models a sound file.
Definition kgamesound.h:38
void setVolume(qreal volume)
Sets the volume of this sound.
~KGameSound() override
Destroys this KGameSound instance.
void stop()
Stops any playbacks of this sounds.
bool isValid() const
bool hasError() const
void setPlaybackType(KGameSound::PlaybackType type)
Sets the playback type for this sound.
PlaybackType
This enumeration describes how a sound can be played back.
Definition kgamesound.h:47
@ RelativePlayback
Positional playback enabled.
Definition kgamesound.h:62
@ AmbientPlayback
Positional playback disabled.
Definition kgamesound.h:51
void setPos(QPointF pos)
Sets the position of this sound.
void start()
Starts a new playback instance of this sound.
KGameSound(const QString &file, QObject *parent=nullptr)
Loads a new sound from the given file.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
pointer data()
Q_EMITQ_EMIT
qreal x() const const
qreal y() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Apr 27 2024 22:10:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.