]> camargo.eng.br - camargo/neiasound.git/blob - src/nSoundStreamer.cpp
Added shared lib project.
[camargo/neiasound.git] / src / nSoundStreamer.cpp
1 // Copyright (C) 2015 Lucas Pires Camargo
2 // 
3 // This file is part of neiasound - Qt-style OpenAL wrapper for games.
4 // 
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 // 
9 // 1. Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // 
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.
15 // 
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"
32 #include "AL/al.h"
33
34
35 const int nSS_BUFFER_SIZE = 4096;
36
37 nSoundStreamer::nSoundStreamer(QString name, nSoundSource * source, nSoundStreamerPlaylist * playlist, nSoundSystem * parent) :
38     QObject(parent)
39 {
40     setObjectName(name);
41     m_playlist = playlist;
42
43     if(!playlist->itemCount()) {
44         qWarning("nSoundStreamer has no items in playlist");
45         return;
46     }
47
48     // make sure we can stream to source
49     alGetError();
50     int sourceType;
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.");
54     m_source = source;
55
56     // create OpenAL buffers
57     alGetError();
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("\".");
65
66
67     //reserve buffer memory
68     m_currentStream = 0;
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);
73
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);
78
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);
86     m_updater->setup();
87
88 }
89
90 nSoundStreamer::~nSoundStreamer()
91 {
92     if(!m_playlist->itemCount()) {
93         return;
94     }
95
96     QMutexLocker lock(&_mutex);
97
98     m_updater->_keepGoing = false;
99     alGetError();
100
101     m_source->stop();
102
103     int queuedBuffers;
104     alGetSourcei(m_source->openalHandle(), AL_BUFFERS_QUEUED, &queuedBuffers);
105     while(queuedBuffers--)
106     {
107         unsigned int buffer;
108         alSourceUnqueueBuffers(m_source->openalHandle(), 1, &buffer);
109         if(alGetError()!=AL_NO_ERROR)
110         {
111             qWarning("nSoundStreamer::~nSoundStreamer(): Failed to unqueue buffer");
112         }
113     }
114
115     unsigned int buffers[3];
116     buffers[0] = m_buffer0;
117     buffers[1] = m_buffer1;
118     buffers[2] = m_buffer2;
119     alDeleteBuffers(3, buffers);
120
121     if(alGetError()!=AL_NO_ERROR)
122         qWarning("nSoundStreamer::~nSoundStreamer(): Failed to destroy buffers.");
123
124     delete m_bag;
125 }
126
127 void nSoundStreamer::update(float frameTime)
128 {
129     Q_UNUSED(frameTime)
130
131     if(!m_playlist->itemCount()) {
132         return;
133     }
134
135     QMutexLocker lock(&_mutex);
136
137     if(m_keepStreaming)
138     {
139         unsigned int sourceHandle = m_source->openalHandle();
140
141         alGetError();
142         int processedBuffers;
143         alGetSourcei(sourceHandle, AL_BUFFERS_PROCESSED, &processedBuffers);
144
145         while(processedBuffers--)
146         {
147             unsigned int buffer;
148             alSourceUnqueueBuffers(sourceHandle, 1, &buffer);
149             if(alGetError()!=AL_NO_ERROR)
150                 throw QString("nSoundStreamer::update(...): Failed to unqueue buffer.");
151
152             if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(buffer);
153         }
154     }
155
156
157 }
158
159 void nSoundStreamer::rewind()
160 {
161     if(!m_playlist->itemCount()) {
162         return;
163     }
164
165     bool playing = m_source->state() == nSoundSource::SSS_PLAYING;
166     m_source->stop();
167     m_playlist->m_items[m_currentStream].m_soundStream->rewind();
168     m_currentStream = 0;
169
170     update(0);
171     if(playing) m_source->play();
172
173 }
174
175 bool nSoundStreamer::fillAndQueueBuffer(unsigned int buffer)
176 {
177     bool keep = true;
178
179     nSoundBag * bag = m_bag;
180     quint64 frames = bag->m_frames;
181     quint64 readFrames = 0;
182     int byteFactor = nSoundFormat_getFramesize(bag->m_format);
183
184     do
185     {
186         nSoundStream * stream = m_playlist->m_items[m_currentStream].m_soundStream;
187
188         readFrames += stream->read(m_bag->m_data+(readFrames*byteFactor), m_bag->m_frames - readFrames);
189
190         if(readFrames!=frames)
191         {
192             stream->rewind();
193             if( ! m_playlist->item(m_currentStream).m_loop)
194             {
195                 m_currentStream++;
196                 if(m_currentStream==m_playlist->m_items.size())
197                 {
198                     if(m_playlist->loopPlaylist())m_currentStream = 0;
199                     else keep = false;
200                 }
201             }
202         }
203     }while ( keep && (readFrames < frames));
204
205     alGetError();
206     alBufferData(buffer, openalFormat(m_bag->m_format), m_bag->m_data, readFrames*byteFactor, m_bag->m_frequency);
207     if(alGetError()!=AL_NO_ERROR)
208         throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to refill buffer.");
209
210     alSourceQueueBuffers(m_source->openalHandle(), 1, &buffer);
211     if(alGetError()!=AL_NO_ERROR)
212         throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to queue buffer.");
213
214     return keep;
215 }
216
217 int nSoundStreamer::openalFormat(nSoundFormat format)
218 {
219     switch(format)
220     {
221     case SF_8BIT_MONO:
222         return AL_FORMAT_MONO8;
223
224     case SF_8BIT_STEREO:
225         return AL_FORMAT_STEREO8;
226
227     case SF_16BIT_MONO:
228         return AL_FORMAT_MONO16;
229
230     case SF_16BIT_STEREO:
231         return AL_FORMAT_STEREO16;
232     case SF_WAVE_HEADER:
233     case SF_UNDEFINED:
234         return -1;
235     }
236
237     return -1;
238 }
239
240
241 nSoundStreamerUpdater::nSoundStreamerUpdater(nSoundStreamer *parent) : QObject(0),
242     _streamer(parent),
243     _keepGoing(true)
244 {
245     startTimer(static_cast<int>(nSS_BUFFER_SIZE / 44100.0 * 1000));
246 }
247
248 nSoundStreamerUpdater::~nSoundStreamerUpdater()
249 {
250
251 }
252
253 void nSoundStreamerUpdater::setup()
254 {
255 }
256
257 void nSoundStreamerUpdater::timerEvent(QTimerEvent * evt)
258 {
259     Q_UNUSED(evt)
260
261     if(_keepGoing)
262         _streamer->update(0);
263     else {
264         deleteLater();
265         QThread::currentThread()->deleteLater();
266         QThread::currentThread()->exit(0);
267     }
268 }