3030
3131FileReader::FileReader ()
3232 : GenericProcessor (" File Reader" )
33+ , Thread (" filereader_Async_Reader" )
3334 , timestamp (0 )
3435 , currentSampleRate (0 )
3536 , currentNumChannels (0 )
@@ -39,6 +40,7 @@ FileReader::FileReader()
3940 , stopSample (0 )
4041 , counter (0 )
4142 , bufferCacheWindow (0 )
43+ , m_shouldFillBackBuffer(false )
4244{
4345 setProcessorType (PROCESSOR_TYPE_SOURCE);
4446
@@ -63,6 +65,8 @@ FileReader::FileReader()
6365
6466FileReader::~FileReader ()
6567{
68+ signalThreadShouldExit ();
69+ notify ();
6670}
6771
6872
@@ -180,13 +184,19 @@ bool FileReader::setFile (String fullpath)
180184
181185 static_cast <FileReaderEditor*> (getEditor ())->populateRecordings (input);
182186 setActiveRecording (0 );
187+
188+ m_samplesPerBuffer.set (float (BUFFER_SIZE) * (getDefaultSampleRate () / 44100 .0f ));
189+
190+ readAndFillBufferCache (bufferA); // pre-fill the front buffer with a blocking read
191+
192+ startThread (); // start async file reader thread
183193
184194 return true ;
185195}
186196
187197
188198void FileReader::setActiveRecording (int index)
189- {
199+ {
190200 input->setActiveRecord (index);
191201
192202 currentNumChannels = input->getActiveNumChannels ();
@@ -205,7 +215,13 @@ void FileReader::setActiveRecording (int index)
205215
206216 static_cast <FileReaderEditor*> (getEditor ())->setTotalTime (samplesToMilliseconds (currentNumSamples));
207217
208- readBuffer.malloc (currentNumChannels * BUFFER_SIZE * BUFFER_WINDOW_CACHE_SIZE);
218+ bufferA.malloc (currentNumChannels * BUFFER_SIZE * BUFFER_WINDOW_CACHE_SIZE);
219+ bufferB.malloc (currentNumChannels * BUFFER_SIZE * BUFFER_WINDOW_CACHE_SIZE);
220+
221+ // set the backbuffer so that on the next call to process() we start with bufferA and buffer
222+ // cache window id = 0
223+ readBuffer = &bufferB;
224+ bufferCacheWindow = 0 ;
209225}
210226
211227
@@ -233,47 +249,20 @@ void FileReader::updateSettings()
233249void FileReader::process (AudioSampleBuffer& buffer)
234250{
235251 const int samplesNeededPerBuffer = int (float (buffer.getNumSamples ()) * (getDefaultSampleRate () / 44100 .0f ));
236- const int samplesNeeded = samplesNeededPerBuffer * BUFFER_WINDOW_CACHE_SIZE ;
252+ m_samplesPerBuffer. set ( samplesNeededPerBuffer) ;
237253 // FIXME: needs to account for the fact that the ratio might not be an exact
238254 // integer value
239255
240- int samplesRead = 0 ;
241-
242- // if window id == 0, we need to read and cache BUFFER_WINDOW_CACHE_SIZE more buffer windows
256+ // if cache window id == 0, we need to read and cache BUFFER_WINDOW_CACHE_SIZE more buffer windows
243257 if (bufferCacheWindow == 0 )
244258 {
245-
246- // should only loop if reached end of file and resuming from start
247- while (samplesRead < samplesNeeded)
248- {
249- int samplesToRead = samplesNeeded - samplesRead;
250-
251- // if reached end of file stream
252- if ( (currentSample + samplesToRead) > stopSample)
253- {
254- samplesToRead = stopSample - currentSample;
255- if (samplesToRead > 0 )
256- input->readData (readBuffer + samplesRead * currentNumChannels, samplesToRead);
257-
258- // reset stream to beginning
259- input->seekTo (startSample);
260- currentSample = startSample;
261- }
262- else // else read the block needed
263- {
264- input->readData (readBuffer + samplesRead * currentNumChannels, samplesToRead);
265-
266- currentSample += samplesToRead;
267- }
268-
269- samplesRead += samplesToRead;
270- }
259+ switchBuffer ();
271260 }
272261
273262 for (int i = 0 ; i < currentNumChannels; ++i)
274263 {
275264 // offset readBuffer index by current cache window count * buffer window size * num channels
276- input->processChannelData (readBuffer + (samplesNeededPerBuffer * currentNumChannels * bufferCacheWindow),
265+ input->processChannelData (* readBuffer + (samplesNeededPerBuffer * currentNumChannels * bufferCacheWindow),
277266 buffer.getWritePointer (i, 0 ),
278267 i,
279268 samplesNeededPerBuffer);
@@ -325,3 +314,73 @@ int64 FileReader::millisecondsToSamples (unsigned int ms) const
325314{
326315 return (int64) (currentSampleRate * float (ms) / 1000 .f );
327316}
317+
318+ void FileReader::switchBuffer ()
319+ {
320+ if (readBuffer == &bufferA)
321+ readBuffer = &bufferB;
322+ else
323+ readBuffer = &bufferA;
324+
325+ m_shouldFillBackBuffer.set (true );
326+ notify ();
327+ }
328+
329+ HeapBlock<int16>* FileReader::getFrontBuffer ()
330+ {
331+ return readBuffer;
332+ }
333+
334+ HeapBlock<int16>* FileReader::getBackBuffer ()
335+ {
336+ if (readBuffer == &bufferA) return &bufferB;
337+
338+ return &bufferA;
339+ }
340+
341+ void FileReader::run ()
342+ {
343+ while (!threadShouldExit ())
344+ {
345+ if (m_shouldFillBackBuffer.compareAndSetBool (false , true ))
346+ {
347+ readAndFillBufferCache (*getBackBuffer ());
348+ }
349+
350+ wait (30 );
351+ }
352+ }
353+
354+ void FileReader::readAndFillBufferCache (HeapBlock<int16> &cacheBuffer)
355+ {
356+ const int samplesNeededPerBuffer = m_samplesPerBuffer.get ();
357+ const int samplesNeeded = samplesNeededPerBuffer * BUFFER_WINDOW_CACHE_SIZE;
358+
359+ int samplesRead = 0 ;
360+
361+ // should only loop if reached end of file and resuming from start
362+ while (samplesRead < samplesNeeded)
363+ {
364+ int samplesToRead = samplesNeeded - samplesRead;
365+
366+ // if reached end of file stream
367+ if ( (currentSample + samplesToRead) > stopSample)
368+ {
369+ samplesToRead = stopSample - currentSample;
370+ if (samplesToRead > 0 )
371+ input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
372+
373+ // reset stream to beginning
374+ input->seekTo (startSample);
375+ currentSample = startSample;
376+ }
377+ else // else read the block needed
378+ {
379+ input->readData (cacheBuffer + samplesRead * currentNumChannels, samplesToRead);
380+
381+ currentSample += samplesToRead;
382+ }
383+
384+ samplesRead += samplesToRead;
385+ }
386+ }
0 commit comments