1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
BufferingAudioReader(AudioFormatReader * sourceReader,TimeSliceThread & timeSliceThread,int samplesToBuffer)29 BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader,
30                                             TimeSliceThread& timeSliceThread,
31                                             int samplesToBuffer)
32     : AudioFormatReader (nullptr, sourceReader->getFormatName()),
33       source (sourceReader), thread (timeSliceThread),
34       numBlocks (1 + (samplesToBuffer / samplesPerBlock))
35 {
36     sampleRate            = source->sampleRate;
37     lengthInSamples       = source->lengthInSamples;
38     numChannels           = source->numChannels;
39     metadataValues        = source->metadataValues;
40     bitsPerSample         = 32;
41     usesFloatingPointData = true;
42 
43     for (int i = 3; --i >= 0;)
44         readNextBufferChunk();
45 
46     timeSliceThread.addTimeSliceClient (this);
47 }
48 
~BufferingAudioReader()49 BufferingAudioReader::~BufferingAudioReader()
50 {
51     thread.removeTimeSliceClient (this);
52 }
53 
setReadTimeout(int timeoutMilliseconds)54 void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept
55 {
56     timeoutMs = timeoutMilliseconds;
57 }
58 
readSamples(int ** destSamples,int numDestChannels,int startOffsetInDestBuffer,int64 startSampleInFile,int numSamples)59 bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
60                                         int64 startSampleInFile, int numSamples)
61 {
62     auto startTime = Time::getMillisecondCounter();
63     clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
64                                        startSampleInFile, numSamples, lengthInSamples);
65 
66     const ScopedLock sl (lock);
67     nextReadPosition = startSampleInFile;
68 
69     while (numSamples > 0)
70     {
71         if (auto block = getBlockContaining (startSampleInFile))
72         {
73             auto offset = (int) (startSampleInFile - block->range.getStart());
74             auto numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile));
75 
76             for (int j = 0; j < numDestChannels; ++j)
77             {
78                 if (auto dest = (float*) destSamples[j])
79                 {
80                     dest += startOffsetInDestBuffer;
81 
82                     if (j < (int) numChannels)
83                         FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
84                     else
85                         FloatVectorOperations::clear (dest, numToDo);
86                 }
87             }
88 
89             startOffsetInDestBuffer += numToDo;
90             startSampleInFile += numToDo;
91             numSamples -= numToDo;
92         }
93         else
94         {
95             if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs)
96             {
97                 for (int j = 0; j < numDestChannels; ++j)
98                     if (auto dest = (float*) destSamples[j])
99                         FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
100 
101                 break;
102             }
103             else
104             {
105                 ScopedUnlock ul (lock);
106                 Thread::yield();
107             }
108         }
109     }
110 
111     return true;
112 }
113 
BufferedBlock(AudioFormatReader & reader,int64 pos,int numSamples)114 BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples)
115     : range (pos, pos + numSamples),
116       buffer ((int) reader.numChannels, numSamples)
117 {
118     reader.read (&buffer, 0, numSamples, pos, true, true);
119 }
120 
getBlockContaining(int64 pos) const121 BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept
122 {
123     for (auto* b : blocks)
124         if (b->range.contains (pos))
125             return b;
126 
127     return nullptr;
128 }
129 
useTimeSlice()130 int BufferingAudioReader::useTimeSlice()
131 {
132     return readNextBufferChunk() ? 1 : 100;
133 }
134 
readNextBufferChunk()135 bool BufferingAudioReader::readNextBufferChunk()
136 {
137     auto pos = nextReadPosition.load();
138     auto startPos = ((pos - 1024) / samplesPerBlock) * samplesPerBlock;
139     auto endPos = startPos + numBlocks * samplesPerBlock;
140 
141     OwnedArray<BufferedBlock> newBlocks;
142 
143     for (int i = blocks.size(); --i >= 0;)
144         if (blocks.getUnchecked(i)->range.intersects (Range<int64> (startPos, endPos)))
145             newBlocks.add (blocks.getUnchecked(i));
146 
147     if (newBlocks.size() == numBlocks)
148     {
149         newBlocks.clear (false);
150         return false;
151     }
152 
153     for (auto p = startPos; p < endPos; p += samplesPerBlock)
154     {
155         if (getBlockContaining (p) == nullptr)
156         {
157             newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock));
158             break; // just do one block
159         }
160     }
161 
162     {
163         const ScopedLock sl (lock);
164         newBlocks.swapWith (blocks);
165     }
166 
167     for (int i = blocks.size(); --i >= 0;)
168         newBlocks.removeObject (blocks.getUnchecked(i), false);
169 
170     return true;
171 }
172 
173 } // namespace juce
174