OpenShot Audio Library | OpenShotAudio  0.6.0
juce_MemoryAudioSource.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 MemoryAudioSource::MemoryAudioSource (AudioBuffer<float>& bufferToUse, bool copyMemory, bool shouldLoop)
27  : isCurrentlyLooping (shouldLoop)
28 {
29  if (copyMemory)
30  buffer.makeCopyOf (bufferToUse);
31  else
32  buffer.setDataToReferTo (bufferToUse.getArrayOfWritePointers(),
33  bufferToUse.getNumChannels(),
34  bufferToUse.getNumSamples());
35 }
36 
37 //==============================================================================
38 void MemoryAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double /*sampleRate*/)
39 {
40  position = 0;
41 }
42 
44 
46 {
47  if (buffer.getNumSamples() == 0)
48  {
49  bufferToFill.clearActiveBufferRegion();
50  return;
51  }
52 
53  auto& dst = *bufferToFill.buffer;
54  auto channels = jmin (dst.getNumChannels(), buffer.getNumChannels());
55  int max = 0, pos = 0;
56  auto n = buffer.getNumSamples();
57  auto m = bufferToFill.numSamples;
58 
59  int i = position;
60  for (; (i < n || isCurrentlyLooping) && (pos < m); i += max)
61  {
62  max = jmin (m - pos, n - (i % n));
63 
64  int ch = 0;
65  for (; ch < channels; ++ch)
66  dst.copyFrom (ch, bufferToFill.startSample + pos, buffer, ch, i % n, max);
67 
68  for (; ch < dst.getNumChannels(); ++ch)
69  dst.clear (ch, bufferToFill.startSample + pos, max);
70 
71  pos += max;
72  }
73 
74  if (pos < m)
75  dst.clear (bufferToFill.startSample + pos, m - pos);
76 
77  position = i;
78 }
79 
80 //==============================================================================
82 {
83  position = (int) newPosition;
84 }
85 
87 {
88  return position;
89 }
90 
92 {
93  return buffer.getNumSamples();
94 }
95 
96 //==============================================================================
98 {
99  return isCurrentlyLooping;
100 }
101 
102 void MemoryAudioSource::setLooping (bool shouldLoop)
103 {
104  isCurrentlyLooping = shouldLoop;
105 }
106 
107 //==============================================================================
108 //==============================================================================
109 #if JUCE_UNIT_TESTS
110 
111 struct MemoryAudioSourceTests final : public UnitTest
112 {
113  MemoryAudioSourceTests() : UnitTest ("MemoryAudioSource", UnitTestCategories::audio) {}
114 
115  void runTest() override
116  {
117  constexpr int blockSize = 512;
118  AudioBuffer<float> bufferToFill { 2, blockSize };
119  AudioSourceChannelInfo channelInfo { bufferToFill };
120 
121  beginTest ("A zero-length buffer produces silence, whether or not looping is enabled");
122  {
123  for (const bool enableLooping : { false, true })
124  {
125  AudioBuffer<float> buffer;
126  MemoryAudioSource source { buffer, true, false };
127  source.setLooping (enableLooping);
128  source.prepareToPlay (blockSize, 44100.0);
129 
130  for (int i = 0; i < 2; ++i)
131  {
132  play (source, channelInfo);
133  expect (isSilent (bufferToFill));
134  }
135  }
136  }
137 
138  beginTest ("A short buffer without looping is played once and followed by silence");
139  {
140  auto buffer = getShortBuffer();
141  MemoryAudioSource source { buffer, true, false };
142  source.setLooping (false);
143  source.prepareToPlay (blockSize, 44100.0);
144 
145  play (source, channelInfo);
146 
147  auto copy = buffer;
148  copy.setSize (buffer.getNumChannels(), blockSize, true, true, false);
149 
150  expect (bufferToFill == copy);
151 
152  play (source, channelInfo);
153 
154  expect (isSilent (bufferToFill));
155  }
156 
157  beginTest ("A short buffer with looping is played multiple times");
158  {
159  auto buffer = getShortBuffer();
160  MemoryAudioSource source { buffer, true, false };
161  source.setLooping (true);
162  source.prepareToPlay (blockSize, 44100.0);
163 
164  play (source, channelInfo);
165 
166  for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
167  expectEquals (bufferToFill.getSample (0, sample + buffer.getNumSamples()), buffer.getSample (0, sample));
168 
169  expect (! isSilent (bufferToFill));
170  }
171 
172  beginTest ("A long buffer without looping is played once");
173  {
174  auto buffer = getLongBuffer();
175  MemoryAudioSource source { buffer, true, false };
176  source.setLooping (false);
177  source.prepareToPlay (blockSize, 44100.0);
178 
179  play (source, channelInfo);
180 
181  auto copy = buffer;
182  copy.setSize (buffer.getNumChannels(), blockSize, true, true, false);
183 
184  expect (bufferToFill == copy);
185 
186  for (int i = 0; i < 10; ++i)
187  play (source, channelInfo);
188 
189  expect (isSilent (bufferToFill));
190  }
191 
192  beginTest ("A long buffer with looping is played multiple times");
193  {
194  auto buffer = getLongBuffer();
195  MemoryAudioSource source { buffer, true, false };
196  source.setLooping (true);
197  source.prepareToPlay (blockSize, 44100.0);
198 
199  for (int i = 0; i < 100; ++i)
200  {
201  play (source, channelInfo);
202  expectEquals (bufferToFill.getSample (0, 0), buffer.getSample (0, (i * blockSize) % buffer.getNumSamples()));
203  }
204  }
205  }
206 
207  static AudioBuffer<float> getTestBuffer (int length)
208  {
209  AudioBuffer<float> buffer { 2, length };
210 
211  for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
212  for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
213  buffer.setSample (channel, sample, jmap ((float) sample, 0.0f, (float) length, -1.0f, 1.0f));
214 
215  return buffer;
216  }
217 
218  static AudioBuffer<float> getShortBuffer() { return getTestBuffer (5); }
219  static AudioBuffer<float> getLongBuffer() { return getTestBuffer (1000); }
220 
221  static void play (MemoryAudioSource& source, AudioSourceChannelInfo& info)
222  {
223  info.clearActiveBufferRegion();
224  source.getNextAudioBlock (info);
225  }
226 
227  static bool isSilent (const AudioBuffer<float>& b)
228  {
229  for (int channel = 0; channel < b.getNumChannels(); ++channel)
230  if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float>{})
231  return false;
232 
233  return true;
234  }
235 };
236 
237 static MemoryAudioSourceTests memoryAudioSourceTests;
238 
239 #endif
240 
241 } // namespace juce
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
int getNumChannels() const noexcept
int getNumSamples() const noexcept
Type *const * getArrayOfWritePointers() noexcept
void setDataToReferTo(Type *const *dataToReferTo, int newNumChannels, int newStartSample, int newNumSamples)
bool isLooping() const override
int64 getNextReadPosition() const override
MemoryAudioSource(AudioBuffer< float > &audioBuffer, bool copyMemory, bool shouldLoop=false)
void setNextReadPosition(int64 newPosition) override
int64 getTotalLength() const override
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
void setLooping(bool shouldLoop) override
void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill) override
AudioBuffer< float > * buffer