PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/opensource.apple.com/source/WebCore/WebCore-1298.37/webaudio/AudioBufferSourceNode.cpp

#
C++ | 480 lines | 375 code | 84 blank | 21 comment | 21 complexity | eb2a44ab956f32fd4b25175b48ce5395 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0, BSD-3-Clause, GPL-3.0, MPL-2.0, LGPL-2.0, LGPL-2.1, CC-BY-SA-3.0, IPL-1.0, ISC, AGPL-1.0, AGPL-3.0, JSON, Apache-2.0, 0BSD
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  4. <head>
  5. <title>AudioBufferSourceNode.cpp</title>
  6. <style type="text/css">
  7. .enscript-comment { font-style: italic; color: rgb(178,34,34); }
  8. .enscript-function-name { font-weight: bold; color: rgb(0,0,255); }
  9. .enscript-variable-name { font-weight: bold; color: rgb(184,134,11); }
  10. .enscript-keyword { font-weight: bold; color: rgb(160,32,240); }
  11. .enscript-reference { font-weight: bold; color: rgb(95,158,160); }
  12. .enscript-string { font-weight: bold; color: rgb(188,143,143); }
  13. .enscript-builtin { font-weight: bold; color: rgb(218,112,214); }
  14. .enscript-type { font-weight: bold; color: rgb(34,139,34); }
  15. .enscript-highlight { text-decoration: underline; color: 0; }
  16. </style>
  17. </head>
  18. <body id="top">
  19. <h1 style="margin:8px;" id="f1">AudioBufferSourceNode.cpp&nbsp;&nbsp;&nbsp;<span style="font-weight: normal; font-size: 0.5em;">[<a href="?txt">plain text</a>]</span></h1>
  20. <hr/>
  21. <div></div>
  22. <pre>
  23. <span class="enscript-comment">/*
  24. * Copyright (C) 2010, Google Inc. All rights reserved.
  25. *
  26. * Redistribution and use in source and binary forms, with or without
  27. * modification, are permitted provided that the following conditions
  28. * are met:
  29. * 1. Redistributions of source code must retain the above copyright
  30. * notice, this list of conditions and the following disclaimer.
  31. * 2. Redistributions in binary form must reproduce the above copyright
  32. * notice, this list of conditions and the following disclaimer in the
  33. * documentation and/or other materials provided with the distribution.
  34. *
  35. * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
  36. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  37. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  39. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  40. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  42. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  43. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  44. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. */</span>
  46. #<span class="enscript-reference">include</span> <span class="enscript-string">&quot;config.h&quot;</span>
  47. #<span class="enscript-reference">if</span> <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">WEB_AUDIO</span>)
  48. #<span class="enscript-reference">include</span> <span class="enscript-string">&quot;AudioBufferSourceNode.h&quot;</span>
  49. #<span class="enscript-reference">include</span> <span class="enscript-string">&quot;AudioContext.h&quot;</span>
  50. #<span class="enscript-reference">include</span> <span class="enscript-string">&quot;AudioNodeOutput.h&quot;</span>
  51. #<span class="enscript-reference">include</span> <span class="enscript-string">&lt;algorithm&gt;</span>
  52. #<span class="enscript-reference">include</span> <span class="enscript-string">&lt;wtf/MathExtras.h&gt;</span>
  53. using namespace std;
  54. namespace WebCore {
  55. <span class="enscript-type">const</span> <span class="enscript-type">double</span> DefaultGrainDuration = 0.020; <span class="enscript-comment">// 20ms
  56. </span>
  57. PassRefPtr&lt;AudioBufferSourceNode&gt; AudioBufferSourceNode::create(AudioContext* context, <span class="enscript-type">double</span> sampleRate)
  58. {
  59. <span class="enscript-keyword">return</span> adoptRef(<span class="enscript-keyword">new</span> AudioBufferSourceNode(context, sampleRate));
  60. }
  61. <span class="enscript-function-name">AudioBufferSourceNode::AudioBufferSourceNode</span>(AudioContext* context, <span class="enscript-type">double</span> sampleRate)
  62. : AudioSourceNode(context, sampleRate)
  63. , m_buffer(0)
  64. , m_isPlaying(false)
  65. , m_isLooping(false)
  66. , m_hasFinished(false)
  67. , m_startTime(0.0)
  68. , m_schedulingFrameDelay(0)
  69. , m_readIndex(0)
  70. , m_isGrain(false)
  71. , m_grainOffset(0.0)
  72. , m_grainDuration(DefaultGrainDuration)
  73. , m_grainFrameCount(0)
  74. , m_lastGain(1.0)
  75. , m_pannerNode(0)
  76. {
  77. setType(NodeTypeAudioBufferSource);
  78. m_gain = AudioGain::create(<span class="enscript-string">&quot;gain&quot;</span>, 1.0, 0.0, 1.0);
  79. m_playbackRate = AudioParam::create(<span class="enscript-string">&quot;playbackRate&quot;</span>, 1.0, 0.0, AudioResampler::MaxRate);
  80. <span class="enscript-comment">// Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer.
  81. </span> addOutput(adoptPtr(<span class="enscript-keyword">new</span> AudioNodeOutput(<span class="enscript-keyword">this</span>, 1)));
  82. initialize();
  83. }
  84. <span class="enscript-function-name">AudioBufferSourceNode::~AudioBufferSourceNode</span>()
  85. {
  86. uninitialize();
  87. }
  88. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::process</span>(size_t framesToProcess)
  89. {
  90. AudioBus* outputBus = output(0)-&gt;bus();
  91. <span class="enscript-keyword">if</span> (!isInitialized()) {
  92. outputBus-&gt;zero();
  93. <span class="enscript-keyword">return</span>;
  94. }
  95. <span class="enscript-comment">// The audio thread can't block on this lock, so we call tryLock() instead.
  96. </span> <span class="enscript-comment">// Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return.
  97. </span> <span class="enscript-keyword">if</span> (m_processLock.tryLock()) {
  98. <span class="enscript-comment">// Check if it's time to start playing.
  99. </span> <span class="enscript-type">double</span> sampleRate = <span class="enscript-keyword">this</span>-&gt;sampleRate();
  100. <span class="enscript-type">double</span> pitchRate = totalPitchRate();
  101. <span class="enscript-type">double</span> quantumStartTime = context()-&gt;currentTime();
  102. <span class="enscript-type">double</span> quantumEndTime = quantumStartTime + framesToProcess / sampleRate;
  103. <span class="enscript-keyword">if</span> (!m_isPlaying || m_hasFinished || !buffer() || m_startTime &gt;= quantumEndTime) {
  104. <span class="enscript-comment">// FIXME: can optimize here by propagating silent hint instead of forcing the whole chain to process silence.
  105. </span> outputBus-&gt;zero();
  106. m_processLock.unlock();
  107. <span class="enscript-keyword">return</span>;
  108. }
  109. <span class="enscript-comment">// Handle sample-accurate scheduling so that buffer playback will happen at a very precise time.
  110. </span> m_schedulingFrameDelay = 0;
  111. <span class="enscript-keyword">if</span> (m_startTime &gt;= quantumStartTime) {
  112. <span class="enscript-comment">// m_schedulingFrameDelay is set here only the very first render quantum (because of above check: m_startTime &gt;= quantumEndTime)
  113. </span> <span class="enscript-comment">// So: quantumStartTime &lt;= m_startTime &lt; quantumEndTime
  114. </span> ASSERT(m_startTime &lt; quantumEndTime);
  115. <span class="enscript-type">double</span> startTimeInQuantum = m_startTime - quantumStartTime;
  116. <span class="enscript-type">double</span> startFrameInQuantum = startTimeInQuantum * sampleRate;
  117. <span class="enscript-comment">// m_schedulingFrameDelay is used in provideInput(), so factor in the current playback pitch rate.
  118. </span> m_schedulingFrameDelay = static_cast&lt;<span class="enscript-type">int</span>&gt;(pitchRate * startFrameInQuantum);
  119. }
  120. <span class="enscript-comment">// FIXME: optimization opportunity:
  121. </span> <span class="enscript-comment">// With a bit of work, it should be possible to avoid going through the resampler completely when the pitchRate == 1,
  122. </span> <span class="enscript-comment">// especially if the pitchRate has never deviated from 1 in the past.
  123. </span>
  124. <span class="enscript-comment">// Read the samples through the pitch resampler. Our provideInput() method will be called by the resampler.
  125. </span> m_resampler.setRate(pitchRate);
  126. m_resampler.process(<span class="enscript-keyword">this</span>, outputBus, framesToProcess);
  127. <span class="enscript-comment">// Apply the gain (in-place) to the output bus.
  128. </span> <span class="enscript-type">double</span> totalGain = gain()-&gt;value() * m_buffer-&gt;gain();
  129. outputBus-&gt;copyWithGainFrom(*outputBus, &amp;m_lastGain, totalGain);
  130. m_processLock.unlock();
  131. } <span class="enscript-keyword">else</span> {
  132. <span class="enscript-comment">// Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
  133. </span> outputBus-&gt;zero();
  134. }
  135. }
  136. <span class="enscript-comment">// The resampler calls us back here to get the input samples from our buffer.
  137. </span><span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::provideInput</span>(AudioBus* bus, size_t numberOfFrames)
  138. {
  139. ASSERT(context()-&gt;isAudioThread());
  140. <span class="enscript-comment">// Basic sanity checking
  141. </span> ASSERT(bus);
  142. ASSERT(buffer());
  143. <span class="enscript-keyword">if</span> (!bus || !buffer())
  144. <span class="enscript-keyword">return</span>;
  145. <span class="enscript-type">unsigned</span> numberOfChannels = <span class="enscript-keyword">this</span>-&gt;numberOfChannels();
  146. <span class="enscript-type">unsigned</span> busNumberOfChannels = bus-&gt;numberOfChannels();
  147. <span class="enscript-comment">// FIXME: we can add support for sources with more than two channels, but this is not a common case.
  148. </span> <span class="enscript-type">bool</span> channelCountGood = numberOfChannels == busNumberOfChannels &amp;&amp; (numberOfChannels == 1 || numberOfChannels == 2);
  149. ASSERT(channelCountGood);
  150. <span class="enscript-keyword">if</span> (!channelCountGood)
  151. <span class="enscript-keyword">return</span>;
  152. <span class="enscript-comment">// Get the destination pointers.
  153. </span> <span class="enscript-type">float</span>* destinationL = bus-&gt;channel(0)-&gt;data();
  154. ASSERT(destinationL);
  155. <span class="enscript-keyword">if</span> (!destinationL)
  156. <span class="enscript-keyword">return</span>;
  157. <span class="enscript-type">float</span>* destinationR = (numberOfChannels &lt; 2) ? 0 : bus-&gt;channel(1)-&gt;data();
  158. size_t bufferLength = buffer()-&gt;length();
  159. <span class="enscript-type">double</span> bufferSampleRate = buffer()-&gt;sampleRate();
  160. <span class="enscript-comment">// Calculate the start and end frames in our buffer that we want to play.
  161. </span> <span class="enscript-comment">// If m_isGrain is true, then we will be playing a portion of the total buffer.
  162. </span> <span class="enscript-type">unsigned</span> startFrame = m_isGrain ? static_cast&lt;<span class="enscript-type">unsigned</span>&gt;(m_grainOffset * bufferSampleRate) : 0;
  163. <span class="enscript-type">unsigned</span> endFrame = m_isGrain ? static_cast&lt;<span class="enscript-type">unsigned</span>&gt;(startFrame + m_grainDuration * bufferSampleRate) : bufferLength;
  164. <span class="enscript-comment">// This is a HACK to allow for HRTF tail-time - avoids glitch at end.
  165. </span> <span class="enscript-comment">// FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
  166. </span> <span class="enscript-keyword">if</span> (m_isGrain)
  167. endFrame += 512;
  168. <span class="enscript-comment">// Do some sanity checking.
  169. </span> <span class="enscript-keyword">if</span> (startFrame &gt;= bufferLength)
  170. startFrame = !bufferLength ? 0 : bufferLength - 1;
  171. <span class="enscript-keyword">if</span> (endFrame &gt; bufferLength)
  172. endFrame = bufferLength;
  173. <span class="enscript-keyword">if</span> (m_readIndex &gt;= endFrame)
  174. m_readIndex = startFrame; <span class="enscript-comment">// reset to start
  175. </span>
  176. <span class="enscript-type">int</span> framesToProcess = numberOfFrames;
  177. <span class="enscript-comment">// Handle sample-accurate scheduling so that we play the buffer at a very precise time.
  178. </span> <span class="enscript-comment">// m_schedulingFrameDelay will only be non-zero the very first time that provideInput() is called, which corresponds
  179. </span> <span class="enscript-comment">// with the very start of the buffer playback.
  180. </span> <span class="enscript-keyword">if</span> (m_schedulingFrameDelay &gt; 0) {
  181. ASSERT(m_schedulingFrameDelay &lt;= framesToProcess);
  182. <span class="enscript-keyword">if</span> (m_schedulingFrameDelay &lt;= framesToProcess) {
  183. <span class="enscript-comment">// Generate silence for the initial portion of the destination.
  184. </span> memset(destinationL, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * m_schedulingFrameDelay);
  185. destinationL += m_schedulingFrameDelay;
  186. <span class="enscript-keyword">if</span> (destinationR) {
  187. memset(destinationR, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * m_schedulingFrameDelay);
  188. destinationR += m_schedulingFrameDelay;
  189. }
  190. <span class="enscript-comment">// Since we just generated silence for the initial portion, we have fewer frames to provide.
  191. </span> framesToProcess -= m_schedulingFrameDelay;
  192. }
  193. }
  194. <span class="enscript-comment">// We have to generate a certain number of output sample-frames, but we need to handle the case where we wrap around
  195. </span> <span class="enscript-comment">// from the end of the buffer to the start if playing back with looping and also the case where we simply reach the
  196. </span> <span class="enscript-comment">// end of the sample data, but haven't yet rendered numberOfFrames worth of output.
  197. </span> <span class="enscript-keyword">while</span> (framesToProcess &gt; 0) {
  198. ASSERT(m_readIndex &lt;= endFrame);
  199. <span class="enscript-keyword">if</span> (m_readIndex &gt; endFrame)
  200. <span class="enscript-keyword">return</span>;
  201. <span class="enscript-comment">// Figure out how many frames we can process this time.
  202. </span> <span class="enscript-type">int</span> framesAvailable = endFrame - m_readIndex;
  203. <span class="enscript-type">int</span> framesThisTime = min(framesToProcess, framesAvailable);
  204. <span class="enscript-comment">// Create the destination bus for the part of the destination we're processing this time.
  205. </span> AudioBus currentDestinationBus(busNumberOfChannels, framesThisTime, false);
  206. currentDestinationBus.setChannelMemory(0, destinationL, framesThisTime);
  207. <span class="enscript-keyword">if</span> (busNumberOfChannels &gt; 1)
  208. currentDestinationBus.setChannelMemory(1, destinationR, framesThisTime);
  209. <span class="enscript-comment">// Generate output from the buffer.
  210. </span> readFromBuffer(&amp;currentDestinationBus, framesThisTime);
  211. <span class="enscript-comment">// Update the destination pointers.
  212. </span> destinationL += framesThisTime;
  213. <span class="enscript-keyword">if</span> (busNumberOfChannels &gt; 1)
  214. destinationR += framesThisTime;
  215. framesToProcess -= framesThisTime;
  216. <span class="enscript-comment">// Handle the case where we reach the end of the part of the sample data we're supposed to play for the buffer.
  217. </span> <span class="enscript-keyword">if</span> (m_readIndex &gt;= endFrame) {
  218. m_readIndex = startFrame;
  219. m_grainFrameCount = 0;
  220. <span class="enscript-keyword">if</span> (!looping()) {
  221. <span class="enscript-comment">// If we're not looping, then stop playing when we get to the end.
  222. </span> m_isPlaying = false;
  223. <span class="enscript-keyword">if</span> (framesToProcess &gt; 0) {
  224. <span class="enscript-comment">// We're not looping and we've reached the end of the sample data, but we still need to provide more output,
  225. </span> <span class="enscript-comment">// so generate silence for the remaining.
  226. </span> memset(destinationL, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
  227. <span class="enscript-keyword">if</span> (destinationR)
  228. memset(destinationR, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
  229. }
  230. <span class="enscript-keyword">if</span> (!m_hasFinished) {
  231. <span class="enscript-comment">// Let the context dereference this AudioNode.
  232. </span> context()-&gt;notifyNodeFinishedProcessing(<span class="enscript-keyword">this</span>);
  233. m_hasFinished = true;
  234. }
  235. <span class="enscript-keyword">return</span>;
  236. }
  237. }
  238. }
  239. }
  240. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::readFromBuffer</span>(AudioBus* destinationBus, size_t framesToProcess)
  241. {
  242. <span class="enscript-type">bool</span> isBusGood = destinationBus &amp;&amp; destinationBus-&gt;length() == framesToProcess &amp;&amp; destinationBus-&gt;numberOfChannels() == numberOfChannels();
  243. ASSERT(isBusGood);
  244. <span class="enscript-keyword">if</span> (!isBusGood)
  245. <span class="enscript-keyword">return</span>;
  246. <span class="enscript-type">unsigned</span> numberOfChannels = <span class="enscript-keyword">this</span>-&gt;numberOfChannels();
  247. <span class="enscript-comment">// FIXME: we can add support for sources with more than two channels, but this is not a common case.
  248. </span> <span class="enscript-type">bool</span> channelCountGood = numberOfChannels == 1 || numberOfChannels == 2;
  249. ASSERT(channelCountGood);
  250. <span class="enscript-keyword">if</span> (!channelCountGood)
  251. <span class="enscript-keyword">return</span>;
  252. <span class="enscript-comment">// Get pointers to the start of the sample buffer.
  253. </span> <span class="enscript-type">float</span>* sourceL = m_buffer-&gt;getChannelData(0)-&gt;data();
  254. <span class="enscript-type">float</span>* sourceR = m_buffer-&gt;numberOfChannels() == 2 ? m_buffer-&gt;getChannelData(1)-&gt;data() : 0;
  255. <span class="enscript-comment">// Sanity check buffer access.
  256. </span> <span class="enscript-type">bool</span> isSourceGood = sourceL &amp;&amp; (numberOfChannels == 1 || sourceR) &amp;&amp; m_readIndex + framesToProcess &lt;= m_buffer-&gt;length();
  257. ASSERT(isSourceGood);
  258. <span class="enscript-keyword">if</span> (!isSourceGood)
  259. <span class="enscript-keyword">return</span>;
  260. <span class="enscript-comment">// Offset the pointers to the current read position in the sample buffer.
  261. </span> sourceL += m_readIndex;
  262. sourceR += m_readIndex;
  263. <span class="enscript-comment">// Get pointers to the destination.
  264. </span> <span class="enscript-type">float</span>* destinationL = destinationBus-&gt;channel(0)-&gt;data();
  265. <span class="enscript-type">float</span>* destinationR = numberOfChannels == 2 ? destinationBus-&gt;channel(1)-&gt;data() : 0;
  266. <span class="enscript-type">bool</span> isDestinationGood = destinationL &amp;&amp; (numberOfChannels == 1 || destinationR);
  267. ASSERT(isDestinationGood);
  268. <span class="enscript-keyword">if</span> (!isDestinationGood)
  269. <span class="enscript-keyword">return</span>;
  270. <span class="enscript-keyword">if</span> (m_isGrain)
  271. readFromBufferWithGrainEnvelope(sourceL, sourceR, destinationL, destinationR, framesToProcess);
  272. <span class="enscript-keyword">else</span> {
  273. <span class="enscript-comment">// Simply copy the data from the source buffer to the destination.
  274. </span> memcpy(destinationL, sourceL, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
  275. <span class="enscript-keyword">if</span> (numberOfChannels == 2)
  276. memcpy(destinationR, sourceR, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
  277. }
  278. <span class="enscript-comment">// Advance the buffer's read index.
  279. </span> m_readIndex += framesToProcess;
  280. }
  281. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::readFromBufferWithGrainEnvelope</span>(<span class="enscript-type">float</span>* sourceL, <span class="enscript-type">float</span>* sourceR, <span class="enscript-type">float</span>* destinationL, <span class="enscript-type">float</span>* destinationR, size_t framesToProcess)
  282. {
  283. ASSERT(sourceL &amp;&amp; destinationL);
  284. <span class="enscript-keyword">if</span> (!sourceL || !destinationL)
  285. <span class="enscript-keyword">return</span>;
  286. <span class="enscript-type">int</span> grainFrameLength = static_cast&lt;<span class="enscript-type">int</span>&gt;(m_grainDuration * m_buffer-&gt;sampleRate());
  287. <span class="enscript-type">bool</span> isStereo = sourceR &amp;&amp; destinationR;
  288. <span class="enscript-type">int</span> n = framesToProcess;
  289. <span class="enscript-keyword">while</span> (n--) {
  290. <span class="enscript-comment">// Apply the grain envelope.
  291. </span> <span class="enscript-type">float</span> x = static_cast&lt;<span class="enscript-type">float</span>&gt;(m_grainFrameCount) / static_cast&lt;<span class="enscript-type">float</span>&gt;(grainFrameLength);
  292. m_grainFrameCount++;
  293. x = min(1.0f, x);
  294. <span class="enscript-type">float</span> grainEnvelope = sinf(piFloat * x);
  295. *destinationL++ = grainEnvelope * *sourceL++;
  296. <span class="enscript-keyword">if</span> (isStereo)
  297. *destinationR++ = grainEnvelope * *sourceR++;
  298. }
  299. }
  300. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::reset</span>()
  301. {
  302. m_resampler.reset();
  303. m_readIndex = 0;
  304. m_grainFrameCount = 0;
  305. m_lastGain = gain()-&gt;value();
  306. }
  307. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::setBuffer</span>(AudioBuffer* buffer)
  308. {
  309. ASSERT(isMainThread());
  310. <span class="enscript-comment">// The context must be locked since changing the buffer can re-configure the number of channels that are output.
  311. </span> <span class="enscript-reference">AudioContext</span>::AutoLocker contextLocker(context());
  312. <span class="enscript-comment">// This synchronizes with process().
  313. </span> MutexLocker processLocker(m_processLock);
  314. <span class="enscript-keyword">if</span> (buffer) {
  315. <span class="enscript-comment">// Do any necesssary re-configuration to the buffer's number of channels.
  316. </span> <span class="enscript-type">unsigned</span> numberOfChannels = buffer-&gt;numberOfChannels();
  317. m_resampler.configureChannels(numberOfChannels);
  318. output(0)-&gt;setNumberOfChannels(numberOfChannels);
  319. }
  320. m_readIndex = 0;
  321. m_buffer = buffer;
  322. }
  323. <span class="enscript-type">unsigned</span> <span class="enscript-function-name">AudioBufferSourceNode::numberOfChannels</span>()
  324. {
  325. <span class="enscript-keyword">return</span> output(0)-&gt;numberOfChannels();
  326. }
  327. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteOn</span>(<span class="enscript-type">double</span> when)
  328. {
  329. ASSERT(isMainThread());
  330. <span class="enscript-keyword">if</span> (m_isPlaying)
  331. <span class="enscript-keyword">return</span>;
  332. m_isGrain = false;
  333. m_startTime = when;
  334. m_readIndex = 0;
  335. m_isPlaying = true;
  336. }
  337. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteGrainOn</span>(<span class="enscript-type">double</span> when, <span class="enscript-type">double</span> grainOffset, <span class="enscript-type">double</span> grainDuration)
  338. {
  339. ASSERT(isMainThread());
  340. <span class="enscript-keyword">if</span> (m_isPlaying)
  341. <span class="enscript-keyword">return</span>;
  342. <span class="enscript-keyword">if</span> (!buffer())
  343. <span class="enscript-keyword">return</span>;
  344. <span class="enscript-comment">// Do sanity checking of grain parameters versus buffer size.
  345. </span> <span class="enscript-type">double</span> bufferDuration = buffer()-&gt;duration();
  346. <span class="enscript-keyword">if</span> (grainDuration &gt; bufferDuration)
  347. <span class="enscript-keyword">return</span>; <span class="enscript-comment">// FIXME: maybe should throw exception - consider in specification.
  348. </span>
  349. <span class="enscript-type">double</span> maxGrainOffset = bufferDuration - grainDuration;
  350. maxGrainOffset = max(0.0, maxGrainOffset);
  351. grainOffset = max(0.0, grainOffset);
  352. grainOffset = min(maxGrainOffset, grainOffset);
  353. m_grainOffset = grainOffset;
  354. m_grainDuration = grainDuration;
  355. m_grainFrameCount = 0;
  356. m_isGrain = true;
  357. m_startTime = when;
  358. m_readIndex = static_cast&lt;<span class="enscript-type">int</span>&gt;(m_grainOffset * buffer()-&gt;sampleRate());
  359. m_isPlaying = true;
  360. }
  361. <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteOff</span>(<span class="enscript-type">double</span>)
  362. {
  363. ASSERT(isMainThread());
  364. <span class="enscript-keyword">if</span> (!m_isPlaying)
  365. <span class="enscript-keyword">return</span>;
  366. <span class="enscript-comment">// FIXME: the &quot;when&quot; argument to this method is ignored.
  367. </span> m_isPlaying = false;
  368. m_readIndex = 0;
  369. }
  370. <span class="enscript-type">double</span> <span class="enscript-function-name">AudioBufferSourceNode::totalPitchRate</span>()
  371. {
  372. <span class="enscript-type">double</span> dopplerRate = 1.0;
  373. <span class="enscript-keyword">if</span> (m_pannerNode.get())
  374. dopplerRate = m_pannerNode-&gt;dopplerRate();
  375. <span class="enscript-comment">// Incorporate buffer's sample-rate versus AudioContext's sample-rate.
  376. </span> <span class="enscript-comment">// Normally it's not an issue because buffers are loaded at the AudioContext's sample-rate, but we can handle it in any case.
  377. </span> <span class="enscript-type">double</span> sampleRateFactor = 1.0;
  378. <span class="enscript-keyword">if</span> (buffer())
  379. sampleRateFactor = buffer()-&gt;sampleRate() / sampleRate();
  380. <span class="enscript-type">double</span> basePitchRate = playbackRate()-&gt;value();
  381. <span class="enscript-type">double</span> totalRate = dopplerRate * sampleRateFactor * basePitchRate;
  382. <span class="enscript-comment">// Sanity check the total rate. It's very important that the resampler not get any bad rate values.
  383. </span> totalRate = max(0.0, totalRate);
  384. totalRate = min(AudioResampler::MaxRate, totalRate);
  385. <span class="enscript-type">bool</span> isTotalRateValid = !isnan(totalRate) &amp;&amp; !isinf(totalRate);
  386. ASSERT(isTotalRateValid);
  387. <span class="enscript-keyword">if</span> (!isTotalRateValid)
  388. totalRate = 1.0;
  389. <span class="enscript-keyword">return</span> totalRate;
  390. }
  391. } <span class="enscript-comment">// namespace WebCore
  392. </span>
  393. #<span class="enscript-reference">endif</span> // <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">WEB_AUDIO</span>)
  394. </pre>
  395. <hr />
  396. </body></html>