1 // Copyright (C) 2015 Lucas Pires Camargo
3 // This file is part of neiasound - Qt-style OpenAL wrapper for games.
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
9 // 1. Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright notice,
13 // this list of conditions and the following disclaimer in the documentation
14 // and/or other materials provided with the distribution.
16 // THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS
17 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19 // NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
20 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "nSoundStreamer.h"
27 #include "nSoundSystem.h"
28 #include "nSoundSource.h"
29 #include "nSoundBag.h"
30 #include "nSoundStream.h"
31 #include "nSoundStreamerPlaylist.h"
35 const int nSS_BUFFER_SIZE = 4096;
37 nSoundStreamer::nSoundStreamer(QString name, nSoundSource * source, nSoundStreamerPlaylist * playlist, nSoundSystem * parent) :
41 m_playlist = playlist;
43 if(!playlist->itemCount()) {
44 qWarning("nSoundStreamer has no items in playlist");
48 // make sure we can stream to source
51 alGetSourcei(source->openalHandle(), AL_SOURCE_TYPE, &sourceType);
52 if(sourceType == AL_STATIC)
53 throw QString("nSoundStreamer::nSoundStreamer(...): Tried to create stream \"")+name+("\" to an AL_STATIC source.");
56 // create OpenAL buffers
58 unsigned int buffers[3];
59 alGenBuffers(3, buffers);
60 m_buffer0 = buffers[0];
61 m_buffer1 = buffers[1];
62 m_buffer2 = buffers[2];
63 if(alGetError()!=AL_NO_ERROR)
64 throw QString("nSoundStreamer::nSoundStreamer(...): Failed to create streaming buffers for \"")+name+QString("\".");
67 //reserve buffer memory
69 nSoundStream * stream = m_playlist->m_items[m_currentStream].m_soundStream;
70 m_bag = new nSoundBag(stream->format(),
71 (stream->frames() < nSS_BUFFER_SIZE? stream->frames() : nSS_BUFFER_SIZE),
72 stream->frequency(), this);
74 //fill in initial data and queue buffers
75 m_keepStreaming = fillAndQueueBuffer(m_buffer0);
76 if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(m_buffer1);
77 if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(m_buffer2);
79 // start threaded updater
80 QThread * updaterThread = new QThread(0);
81 updaterThread->setObjectName(objectName() + "_THREAD");
82 m_updater = new nSoundStreamerUpdater(this);
83 m_updater->moveToThread(updaterThread);
84 updaterThread->start();
85 updaterThread->setPriority(QThread::LowPriority);
90 nSoundStreamer::~nSoundStreamer()
92 if(!m_playlist->itemCount()) {
96 QMutexLocker lock(&_mutex);
98 m_updater->_keepGoing = false;
104 alGetSourcei(m_source->openalHandle(), AL_BUFFERS_QUEUED, &queuedBuffers);
105 while(queuedBuffers--)
108 alSourceUnqueueBuffers(m_source->openalHandle(), 1, &buffer);
109 if(alGetError()!=AL_NO_ERROR)
111 qWarning("nSoundStreamer::~nSoundStreamer(): Failed to unqueue buffer");
115 unsigned int buffers[3];
116 buffers[0] = m_buffer0;
117 buffers[1] = m_buffer1;
118 buffers[2] = m_buffer2;
119 alDeleteBuffers(3, buffers);
121 if(alGetError()!=AL_NO_ERROR)
122 qWarning("nSoundStreamer::~nSoundStreamer(): Failed to destroy buffers.");
127 void nSoundStreamer::update(float frameTime)
130 if(!m_playlist->itemCount()) {
134 QMutexLocker lock(&_mutex);
138 unsigned int sourceHandle = m_source->openalHandle();
141 int processedBuffers;
142 alGetSourcei(sourceHandle, AL_BUFFERS_PROCESSED, &processedBuffers);
144 while(processedBuffers--)
147 alSourceUnqueueBuffers(sourceHandle, 1, &buffer);
148 if(alGetError()!=AL_NO_ERROR)
149 throw QString("nSoundStreamer::update(...): Failed to unqueue buffer.");
151 if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(buffer);
158 void nSoundStreamer::rewind()
160 if(!m_playlist->itemCount()) {
164 bool playing = m_source->state() == nSoundSource::SSS_PLAYING;
166 m_playlist->m_items[m_currentStream].m_soundStream->rewind();
170 if(playing) m_source->play();
174 bool nSoundStreamer::fillAndQueueBuffer(unsigned int buffer)
178 nSoundBag * bag = m_bag;
179 quint64 frames = bag->m_frames;
180 quint64 readFrames = 0;
181 int byteFactor = nSoundFormat_getFramesize(bag->m_format);
185 nSoundStream * stream = m_playlist->m_items[m_currentStream].m_soundStream;
187 readFrames += stream->read(m_bag->m_data+(readFrames*byteFactor), m_bag->m_frames - readFrames);
189 if(readFrames!=frames)
192 if( ! m_playlist->item(m_currentStream).m_loop)
195 if(m_currentStream==m_playlist->m_items.size())
196 if(m_playlist->loopPlaylist())m_currentStream = 0;
200 }while ( keep && (readFrames < frames));
203 alBufferData(buffer, openalFormat(m_bag->m_format), m_bag->m_data, readFrames*byteFactor, m_bag->m_frequency);
204 if(alGetError()!=AL_NO_ERROR)
205 throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to refill buffer.");
207 alSourceQueueBuffers(m_source->openalHandle(), 1, &buffer);
208 if(alGetError()!=AL_NO_ERROR)
209 throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to queue buffer.");
214 int nSoundStreamer::openalFormat(nSoundFormat format)
219 return AL_FORMAT_MONO8;
222 return AL_FORMAT_STEREO8;
225 return AL_FORMAT_MONO16;
227 case SF_16BIT_STEREO:
228 return AL_FORMAT_STEREO16;
235 nSoundStreamerUpdater::nSoundStreamerUpdater(nSoundStreamer *parent) : QObject(0),
239 startTimer(static_cast<int>(nSS_BUFFER_SIZE / 44100.0 * 1000));
242 nSoundStreamerUpdater::~nSoundStreamerUpdater()
247 void nSoundStreamerUpdater::setup()
251 void nSoundStreamerUpdater::timerEvent(QTimerEvent * evt)
254 _streamer->update(0);
257 QThread::currentThread()->deleteLater();
258 QThread::currentThread()->exit(0);