KDEGames

kgsound-openal.cpp
1 /***************************************************************************
2  * Copyright 2010 Stefan Majewsky <[email protected]> *
3  * *
4  * This program is free software; you can redistribute it and/or modify *
5  * it under the terms of the GNU Library General Public License *
6  * version 2 as published by the Free Software Foundation *
7  * *
8  * This program is distributed in the hope that it will be useful, *
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
11  * GNU Library General Public License for more details. *
12  * *
13  * You should have received a copy of the GNU Library General Public *
14  * License along with this program; if not, write to the *
15  * Free Software Foundation, Inc., *
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
17  ***************************************************************************/
18 
19 #include "kgsound.h"
20 
21 #include <sndfile.hh>
22 
23 #include <QDebug>
24 
25 #include "kgopenalruntime_p.h"
26 #include "virtualfileqt-openal.h"
27 
28 class KgSound::Private
29 {
30  public:
31  KgSound::PlaybackType m_type;
32  qreal m_volume;
33  QPointF m_pos;
34 
35  bool m_valid;
36  ALuint m_buffer;
37 
38  Private() : m_type(KgSound::AmbientPlayback), m_volume(1.0), m_valid(false), m_buffer(AL_NONE) {}
39 };
40 
41 //BEGIN KgSound
42 
44  : QObject(parent)
45  , d(new Private)
46 {
47  VirtualFileQt fileInterface(file);
48  if (!fileInterface.open()) {
49  qWarning() << "Failed to open sound file" << file;
50  return;
51  }
52 
53  //open sound file
54  SndfileHandle handle(VirtualFileQt::getSndfileVirtualIO(), &fileInterface);
55  if (handle.error())
56  {
57  qWarning() << "Failed to load sound file" << file << ". Error message from libsndfile follows.";
58  qWarning() << handle.strError();
59  return;
60  }
61  const int channelCount = handle.channels();
62  const int sampleCount = channelCount * handle.frames();
63  const int sampleRate = handle.samplerate();
64  //load data from sound file
65  QVector<ALshort> samples(sampleCount);
66  if (handle.read(samples.data(), sampleCount) < sampleCount)
67  {
68  qWarning() << "Failed to read sound file" << file;
69  qWarning() << "File ended unexpectedly.";
70  return;
71  }
72  //determine file format from number of channels
73  ALenum format;
74  switch (channelCount)
75  {
76  case 1:
77  format = AL_FORMAT_MONO16;
78  break;
79  case 2:
80  format = AL_FORMAT_STEREO16;
81  break;
82  default:
83  qWarning() << "Failed to read sound file" << file;
84  qWarning() << "More than two channels are not supported.";
85  return;
86  }
87  //make sure OpenAL is initialized; clear OpenAL error storage
88  KgOpenALRuntime::instance();
89  int error; alGetError();
90  //create OpenAL buffer
91  alGenBuffers(1, &d->m_buffer);
92  if ((error = alGetError()) != AL_NO_ERROR)
93  {
94  qWarning() << "Failed to create OpenAL buffer: Error code" << error;
95  return;
96  }
97  alBufferData(d->m_buffer, format, samples.data(), sampleCount * sizeof(ALshort), sampleRate);
98  if ((error = alGetError()) != AL_NO_ERROR)
99  {
100  qWarning() << "Failed to fill OpenAL buffer: Error code" << error;
101  alDeleteBuffers(1, &d->m_buffer);
102  return;
103  }
104  //loading finished
105  d->m_valid = true;
106 }
107 
109 {
110  if (d->m_valid)
111  {
112  stop();
113  KgOpenALRuntime::instance()->m_soundsEvents.remove(this);
114  alDeleteBuffers(1, &d->m_buffer);
115  }
116  delete d;
117 }
118 
119 bool KgSound::isValid() const
120 {
121  return d->m_valid;
122 }
123 
125 {
126  return d->m_type;
127 }
128 
130 {
131  if (d->m_type == type)
132  return;
133  d->m_type = type;
134  Q_EMIT playbackTypeChanged(type);
135 }
136 
137 QPointF KgSound::pos() const
138 {
139  return d->m_pos;
140 }
141 
143 {
144  if (d->m_pos == pos)
145  return;
146  d->m_pos = pos;
147  Q_EMIT posChanged(pos);
148 }
149 
150 qreal KgSound::volume() const
151 {
152  return d->m_volume;
153 }
154 
156 {
157  if (d->m_volume == volume)
158  return;
159  d->m_volume = volume;
160  Q_EMIT volumeChanged(volume);
161 }
162 
163 bool KgSound::hasError() const
164 {
165  return !d->m_valid;
166 }
167 
169 {
170  start(d->m_pos);
171 }
172 
174 {
175  if (d->m_valid)
176  {
177  KgOpenALRuntime* runtime = KgOpenALRuntime::instance();
178  if(!runtime->instance()->m_soundsEvents[this].isEmpty())
179  {
180  if(runtime->instance()->m_soundsEvents[this].last()->replay(pos) == false)
181  {
182  new KgPlaybackEvent(this, pos);
183  }
184  }
185  else
186  {
187  new KgPlaybackEvent(this, pos);
188  }
189  }
190 }
191 
193 {
194  qDeleteAll(KgOpenALRuntime::instance()->m_soundsEvents.take(this));
195 }
196 
197 //END KgSound
198 //BEGIN KgPlaybackEvent
199 
200 KgPlaybackEvent::KgPlaybackEvent(KgSound* sound, const QPointF& pos)
201  : m_valid(false)
202 {
203  //make sure OpenAL is initialized
204  KgOpenALRuntime* runtime = KgOpenALRuntime::instance();
205  //clear OpenAL error storage
206  int error; alGetError();
207  //create source for playback
208  alGenSources(1, &m_source);
209  if ((error = alGetError()) != AL_NO_ERROR)
210  {
211  qWarning() << "Failed to create OpenAL source: Error code" << error;
212  return;
213  }
214  //store in OpenALRuntime
215  runtime->m_soundsEvents[sound] << this;
216  m_valid = true;
217  //connect to sound (buffer)
218  alSource3f(m_source, AL_POSITION, pos.x(), pos.y(), 0);
219  alSourcef(m_source, AL_PITCH, 1.0); //TODO: debug
220  alSourcef(m_source, AL_GAIN, sound->volume());
221  alSourcei(m_source, AL_BUFFER, sound->d->m_buffer);
222  const KgSound::PlaybackType type = sound->playbackType();
223  alSourcef(m_source, AL_ROLLOFF_FACTOR, type == KgSound::AmbientPlayback ? 0.0 : 1.0);
224  alSourcei(m_source, AL_SOURCE_RELATIVE, type == KgSound::RelativePlayback ? AL_TRUE : AL_FALSE);
225  if ((error = alGetError()) != AL_NO_ERROR)
226  {
227  qWarning() << "Failed to setup OpenAL source: Error code" << error;
228  return;
229  }
230  //start playback
231  alSourcePlay(m_source);
232 }
233 
234 KgPlaybackEvent::~KgPlaybackEvent()
235 {
236  if(alIsSource(m_source) == AL_TRUE)
237  {
238  alSourceStop(m_source);
239  alDeleteSources(1, &m_source);
240  }
241 }
242 
243 bool KgPlaybackEvent::isRunning() const
244 {
245  ALint state;
246  alGetSourcei(m_source, AL_SOURCE_STATE, &state);
247  return state == AL_PLAYING;
248 }
249 
250 bool KgPlaybackEvent::replay(const QPointF& pos) const
251 {
252  if(alIsSource(m_source) == AL_TRUE)
253  {
254  alSourceStop(m_source);
255  alSource3f(m_source, AL_POSITION, pos.x(), pos.y(), 0);
256  alSourcePlay(m_source);
257  return true;
258  }
259  else
260  {
261  return false;
262  }
263 }
264 
265 //END KgPlaybackEvent
266 
267 #include "moc_kgsound.cpp"
KgSound::PlaybackType playbackType() const
virtual ~KgSound()
Destroys this KgSound instance.
QPointF pos() const
bool hasError() const
PlaybackType
This enumeration describes how a sound can be played back.
Definition: audio/kgsound.h:55
bool isValid() const
void start()
Starts a new playback instance of this sound.
Positional playback disabled.
Definition: audio/kgsound.h:60
This class models a sound file.
Definition: audio/kgsound.h:46
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
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:71
void stop()
Stops any playbacks of this sounds.
QObject * parent() const const
Q_EMITQ_EMIT
void setVolume(qreal volume)
Sets the volume of this sound.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Nov 26 2020 22:36:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.