KDEGames

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

KDE's Doxygen guidelines are available online.