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