Initial commit
[camargo/neiasound.git] / src / nSoundStreamer.cpp
1 #include "nSoundStreamer.h"
2 #include "nSoundSystem.h"
3 #include "nSoundSource.h"
4 #include "nSoundBag.h"
5 #include "nSoundStream.h"
6 #include "nSoundStreamerPlaylist.h"
7 #include "AL/al.h"
8
9
10 const int nSS_BUFFER_SIZE = 4096;
11
12 nSoundStreamer::nSoundStreamer(QString name, nSoundSource * source, nSoundStreamerPlaylist * playlist, nSoundSystem * parent) :
13     QObject(parent)
14 {
15     setObjectName(name);
16     m_playlist = playlist;
17
18     if(!playlist->itemCount()) {
19         qWarning("nSoundStreamer has no items in playlist");
20         return;
21     }
22
23     // make sure we can stream to source
24     alGetError();
25     int sourceType;
26     alGetSourcei(source->openalHandle(), AL_SOURCE_TYPE, &sourceType);
27     if(sourceType == AL_STATIC)
28         throw QString("nSoundStreamer::nSoundStreamer(...): Tried to create stream \"")+name+("\" to an AL_STATIC source.");
29     m_source = source;
30
31     // create OpenAL buffers
32     alGetError();
33     unsigned int buffers[3];
34     alGenBuffers(3, buffers);
35     m_buffer0 = buffers[0];
36     m_buffer1 = buffers[1];
37     m_buffer2 = buffers[2];
38     if(alGetError()!=AL_NO_ERROR)
39         throw QString("nSoundStreamer::nSoundStreamer(...): Failed to create streaming buffers for \"")+name+QString("\".");
40
41
42     //reserve buffer memory
43     m_currentStream = 0;
44     nSoundStream * stream = m_playlist->m_items[m_currentStream].m_soundStream;
45     m_bag = new nSoundBag(stream->format(),
46         (stream->frames() < nSS_BUFFER_SIZE? stream->frames() : nSS_BUFFER_SIZE),
47         stream->frequency(), this);
48
49     //fill in initial data and queue buffers
50     m_keepStreaming = fillAndQueueBuffer(m_buffer0);
51     if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(m_buffer1);
52     if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(m_buffer2);
53
54     // start threaded updater
55     QThread * updaterThread = new QThread(0);
56     updaterThread->setObjectName(objectName() + "_THREAD");
57     m_updater = new nSoundStreamerUpdater(this);
58     m_updater->moveToThread(updaterThread);
59     updaterThread->start();
60     updaterThread->setPriority(QThread::LowPriority);
61     m_updater->setup();
62
63 }
64
65 nSoundStreamer::~nSoundStreamer()
66 {
67     if(!m_playlist->itemCount()) {
68         return;
69     }
70
71     QMutexLocker lock(&_mutex);
72
73     m_updater->_keepGoing = false;
74     alGetError();
75
76     m_source->stop();
77
78     int queuedBuffers;
79     alGetSourcei(m_source->openalHandle(), AL_BUFFERS_QUEUED, &queuedBuffers);
80     while(queuedBuffers--)
81     {
82         unsigned int buffer;
83         alSourceUnqueueBuffers(m_source->openalHandle(), 1, &buffer);
84         if(alGetError()!=AL_NO_ERROR)
85         {
86             qWarning("nSoundStreamer::~nSoundStreamer(): Failed to unqueue buffer");
87         }
88     }
89
90     unsigned int buffers[3];
91     buffers[0] = m_buffer0;
92     buffers[1] = m_buffer1;
93     buffers[2] = m_buffer2;
94     alDeleteBuffers(3, buffers);
95
96     if(alGetError()!=AL_NO_ERROR)
97         qWarning("nSoundStreamer::~nSoundStreamer(): Failed to destroy buffers.");
98
99     delete m_bag;
100 }
101
102 void nSoundStreamer::update(float frameTime)
103 {
104
105     if(!m_playlist->itemCount()) {
106         return;
107     }
108
109     QMutexLocker lock(&_mutex);
110
111     if(m_keepStreaming)
112     {
113         unsigned int sourceHandle = m_source->openalHandle();
114
115         alGetError();
116         int processedBuffers;
117         alGetSourcei(sourceHandle, AL_BUFFERS_PROCESSED, &processedBuffers);
118
119         while(processedBuffers--)
120         {
121             unsigned int buffer;
122             alSourceUnqueueBuffers(sourceHandle, 1, &buffer);
123             if(alGetError()!=AL_NO_ERROR)
124                 throw QString("nSoundStreamer::update(...): Failed to unqueue buffer.");
125
126             if(m_keepStreaming) m_keepStreaming = fillAndQueueBuffer(buffer);
127         }
128     }
129
130
131 }
132
133 void nSoundStreamer::rewind()
134 {
135     if(!m_playlist->itemCount()) {
136         return;
137     }
138
139     bool playing = m_source->state() == nSoundSource::SSS_PLAYING;
140     m_source->stop();
141     m_playlist->m_items[m_currentStream].m_soundStream->rewind();
142     m_currentStream = 0;
143
144     update(0);
145     if(playing) m_source->play();
146
147 }
148
149 bool nSoundStreamer::fillAndQueueBuffer(unsigned int buffer)
150 {
151     bool keep = true;
152
153     nSoundBag * bag = m_bag;
154     quint64 frames = bag->m_frames;
155     quint64 readFrames = 0;
156     int byteFactor = nSoundFormat_getFramesize(bag->m_format);
157
158     do
159     {
160         nSoundStream * stream = m_playlist->m_items[m_currentStream].m_soundStream;
161
162         readFrames += stream->read(m_bag->m_data+(readFrames*byteFactor), m_bag->m_frames - readFrames);
163
164         if(readFrames!=frames)
165         {
166             stream->rewind();
167             if( ! m_playlist->item(m_currentStream).m_loop)
168             {
169                 m_currentStream++;
170                 if(m_currentStream==m_playlist->m_items.size())
171                     if(m_playlist->loopPlaylist())m_currentStream = 0;
172                     else keep = false;
173             }
174         }
175     }while ( keep && (readFrames < frames));
176
177     alGetError();
178     alBufferData(buffer, openalFormat(m_bag->m_format), m_bag->m_data, readFrames*byteFactor, m_bag->m_frequency);
179     if(alGetError()!=AL_NO_ERROR)
180         throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to refill buffer.");
181
182     alSourceQueueBuffers(m_source->openalHandle(), 1, &buffer);
183     if(alGetError()!=AL_NO_ERROR)
184         throw QString("nSoundStreamer::fillAndQueueBuffer(...): Failed to queue buffer.");
185
186     return keep;
187 }
188
189 int nSoundStreamer::openalFormat(nSoundFormat format)
190 {
191     switch(format)
192     {
193     case SF_8BIT_MONO:
194         return AL_FORMAT_MONO8;
195
196     case SF_8BIT_STEREO:
197         return AL_FORMAT_STEREO8;
198
199     case SF_16BIT_MONO:
200         return AL_FORMAT_MONO16;
201
202     case SF_16BIT_STEREO:
203         return AL_FORMAT_STEREO16;
204     }
205
206     return -1;
207 }
208
209
210 nSoundStreamerUpdater::nSoundStreamerUpdater(nSoundStreamer *parent) : QObject(0),
211     _streamer(parent),
212     _keepGoing(true)
213 {
214     startTimer(static_cast<int>(nSS_BUFFER_SIZE / 44100.0 * 1000));
215 }
216
217 nSoundStreamerUpdater::~nSoundStreamerUpdater()
218 {
219
220 }
221
222 void nSoundStreamerUpdater::setup()
223 {
224 }
225
226 void nSoundStreamerUpdater::timerEvent(QTimerEvent * evt)
227 {
228     if(_keepGoing)
229         _streamer->update(0);
230     else {
231         deleteLater();
232         QThread::currentThread()->deleteLater();
233         QThread::currentThread()->exit(0);
234     }
235 }