Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / extras / JuceDemo / Source / demos / AudioDemoLatencyPage.cpp
blobbc9822f8d32774b90ff6c983ac4480d73cef76f9
1 /*
2 ==============================================================================
4 This is an automatically generated file created by the Jucer!
6 Creation date: 1 May 2011 1:54:50pm
8 Be careful when adding custom code to these files, as only the code within
9 the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
10 and re-saved.
12 Jucer version: 1.12
14 ------------------------------------------------------------------------------
16 The Jucer is part of the JUCE library - "Jules' Utility Class Extensions"
17 Copyright 2004-6 by Raw Material Software ltd.
19 ==============================================================================
22 //[Headers] You can add your own extra header files here...
23 //[/Headers]
25 #include "AudioDemoLatencyPage.h"
28 //[MiscUserDefs] You can add your own user definitions and misc code here...
30 //==============================================================================
31 class LatencyTester : public AudioIODeviceCallback,
32 public Timer
34 public:
35 LatencyTester (TextEditor* resultsBox_)
36 : testSound (1, 1),
37 recordedSound (1, 1),
38 playingSampleNum (0),
39 recordedSampleNum (-1),
40 sampleRate (0),
41 isRunning (false),
42 resultsBox (resultsBox_)
46 ~LatencyTester()
50 //==============================================================================
51 void beginTest()
53 startTimer (50);
55 const ScopedLock sl (lock);
56 createTestSound();
57 recordedSound.clear();
58 playingSampleNum = recordedSampleNum = 0;
59 isRunning = true;
62 void timerCallback()
64 if (isRunning && recordedSampleNum >= recordedSound.getNumSamples())
66 isRunning = false;
67 stopTimer();
69 // Test has finished, so calculate the result..
70 String message;
72 const int latencySamples = calculateLatencySamples();
74 if (latencySamples >= 0)
76 message << "\n\nLatency test results:\n"
77 << latencySamples << " samples (" << String (latencySamples * 1000.0 / sampleRate, 1) << " milliseconds)\n"
78 << "The audio device reports an input latency of "
79 << deviceInputLatency << " samples, output latency of "
80 << deviceOutputLatency << " samples."
81 << "\nSo the corrected latency = "
82 << (latencySamples - deviceInputLatency - deviceOutputLatency)
83 << " samples (" << String ((latencySamples - deviceInputLatency - deviceOutputLatency) * 1000.0 / sampleRate, 2)
84 << " milliseconds)";
86 else
88 message = "\n\nCouldn't detect the test signal!!\nMake sure there's no background noise that might be confusing it..";
91 resultsBox->setCaretPosition (INT_MAX);
92 resultsBox->insertTextAtCaret (message);
93 resultsBox->setCaretPosition (INT_MAX);
97 //==============================================================================
98 void audioDeviceAboutToStart (AudioIODevice* device)
100 isRunning = false;
101 sampleRate = device->getCurrentSampleRate();
102 deviceInputLatency = device->getInputLatencyInSamples();
103 deviceOutputLatency = device->getOutputLatencyInSamples();
104 playingSampleNum = recordedSampleNum = 0;
106 recordedSound.setSize (1, (int) (1.5 * sampleRate));
107 recordedSound.clear();
110 void audioDeviceStopped()
114 void audioDeviceIOCallback (const float** inputChannelData,
115 int numInputChannels,
116 float** outputChannelData,
117 int numOutputChannels,
118 int numSamples)
120 const ScopedLock sl (lock);
122 if (isRunning)
124 float* const recordingBuffer = recordedSound.getSampleData (0, 0);
125 const float* const playBuffer = testSound.getSampleData (0, 0);
127 for (int i = 0; i < numSamples; ++i)
129 if (recordedSampleNum < recordedSound.getNumSamples())
131 float inputSamp = 0;
132 for (int j = numInputChannels; --j >= 0;)
133 if (inputChannelData[j] != 0)
134 inputSamp += inputChannelData[j][i];
136 recordingBuffer [recordedSampleNum] = inputSamp;
139 ++recordedSampleNum;
141 float outputSamp = (playingSampleNum < testSound.getNumSamples()) ? playBuffer [playingSampleNum] : 0;
143 for (int j = numOutputChannels; --j >= 0;)
144 if (outputChannelData[j] != 0)
145 outputChannelData[j][i] = outputSamp;
147 ++playingSampleNum;
150 else
152 // We need to clear the output buffers, in case they're full of junk..
153 for (int i = 0; i < numOutputChannels; ++i)
154 if (outputChannelData[i] != 0)
155 zeromem (outputChannelData[i], sizeof (float) * numSamples);
159 private:
160 AudioSampleBuffer testSound, recordedSound;
161 int playingSampleNum, recordedSampleNum;
162 CriticalSection lock;
163 double sampleRate;
164 bool isRunning;
165 TextEditor* resultsBox;
166 int deviceInputLatency, deviceOutputLatency;
168 Array <int> spikes;
170 void createTestSound()
172 const int length = ((int) sampleRate) / 4;
173 testSound.setSize (1, length);
174 testSound.clear();
175 float* s = testSound.getSampleData (0, 0);
177 Random rand (0);
178 rand.setSeedRandomly();
180 for (int i = 0; i < length; ++i)
181 s[i] = (rand.nextFloat() - rand.nextFloat() + rand.nextFloat() - rand.nextFloat()) * 0.06f;
183 spikes.clear();
185 int spikePos = 0;
186 int spikeDelta = 50;
188 while (spikePos < length)
190 spikes.add (spikePos);
192 s [spikePos] = 0.99f;
193 s [spikePos + 1] = -0.99f;
195 spikePos += spikeDelta;
196 spikeDelta += spikeDelta / 6 + rand.nextInt (5);
200 // Searches a buffer for a set of spikes that matches those in the test sound
201 int findOffsetOfSpikes (const AudioSampleBuffer& buffer) const
203 const float minSpikeLevel = 5.0f;
204 const double smooth = 0.975;
205 const float* s = buffer.getSampleData (0, 0);
206 const int spikeDriftAllowed = 5;
208 Array <int> spikesFound;
209 spikesFound.ensureStorageAllocated (100);
210 double runningAverage = 0;
211 int lastSpike = 0;
213 for (int i = 0; i < buffer.getNumSamples() - 10; ++i)
215 const float samp = fabsf (s[i]);
217 if (samp > runningAverage * minSpikeLevel && i > lastSpike + 20)
219 lastSpike = i;
220 spikesFound.add (i);
223 runningAverage = runningAverage * smooth + (1.0 - smooth) * samp;
226 int bestMatch = -1;
227 int bestNumMatches = spikes.size() / 3; // the minimum number of matches required
229 if (spikesFound.size() < bestNumMatches)
230 return -1;
232 for (int offsetToTest = 0; offsetToTest < buffer.getNumSamples() - 2048; ++offsetToTest)
234 int numMatchesHere = 0;
235 int foundIndex = 0;
237 for (int refIndex = 0; refIndex < spikes.size(); ++refIndex)
239 const int referenceSpike = spikes.getUnchecked (refIndex) + offsetToTest;
240 int spike = 0;
242 while ((spike = spikesFound.getUnchecked (foundIndex)) < referenceSpike - spikeDriftAllowed
243 && foundIndex < spikesFound.size() - 1)
244 ++foundIndex;
246 if (spike >= referenceSpike - spikeDriftAllowed && spike <= referenceSpike + spikeDriftAllowed)
247 ++numMatchesHere;
250 if (numMatchesHere > bestNumMatches)
252 bestNumMatches = numMatchesHere;
253 bestMatch = offsetToTest;
255 if (numMatchesHere == spikes.size())
256 break;
260 return bestMatch;
263 int calculateLatencySamples() const
265 // Detect the sound in both our test sound and the recording of it, and measure the difference
266 // in their start times..
267 const int referenceStart = findOffsetOfSpikes (testSound);
268 jassert (referenceStart >= 0);
270 const int recordedStart = findOffsetOfSpikes (recordedSound);
272 return (recordedStart < 0) ? -1 : (recordedStart - referenceStart);
275 LatencyTester (const LatencyTester&);
276 LatencyTester& operator= (const LatencyTester&);
280 //[/MiscUserDefs]
282 //==============================================================================
283 AudioDemoLatencyPage::AudioDemoLatencyPage (AudioDeviceManager& deviceManager_)
284 : deviceManager (deviceManager_),
285 liveAudioDisplayComp (0),
286 startTestButton (0),
287 testResultsBox (0)
289 addAndMakeVisible (liveAudioDisplayComp = new LiveAudioInputDisplayComp());
291 addAndMakeVisible (startTestButton = new TextButton (String::empty));
292 startTestButton->setButtonText (L"Test Latency");
293 startTestButton->addListener (this);
295 addAndMakeVisible (testResultsBox = new TextEditor (String::empty));
296 testResultsBox->setMultiLine (true);
297 testResultsBox->setReturnKeyStartsNewLine (true);
298 testResultsBox->setReadOnly (true);
299 testResultsBox->setScrollbarsShown (true);
300 testResultsBox->setCaretVisible (false);
301 testResultsBox->setPopupMenuEnabled (true);
302 testResultsBox->setColour (TextEditor::backgroundColourId, Colour (0x32ffffff));
303 testResultsBox->setColour (TextEditor::outlineColourId, Colour (0x1c000000));
304 testResultsBox->setColour (TextEditor::shadowColourId, Colour (0x16000000));
305 testResultsBox->setText (L"Running this test measures the round-trip latency between the audio output and input devices you\'ve got selected.\n\nIt\'ll play a sound, then try to measure the time at which the sound arrives back at the audio input. Obviously for this to work you need to have your microphone somewhere near your speakers...");
308 //[UserPreSize]
309 //[/UserPreSize]
311 setSize (600, 400);
314 //[Constructor] You can add your own custom stuff here..
315 deviceManager.addAudioCallback (liveAudioDisplayComp);
317 latencyTester = new LatencyTester (testResultsBox);
318 deviceManager.addAudioCallback (latencyTester);
319 //[/Constructor]
322 AudioDemoLatencyPage::~AudioDemoLatencyPage()
324 //[Destructor_pre]. You can add your own custom destruction code here..
325 deviceManager.removeAudioCallback (liveAudioDisplayComp);
327 deviceManager.removeAudioCallback (latencyTester);
328 //[/Destructor_pre]
330 deleteAndZero (liveAudioDisplayComp);
331 deleteAndZero (startTestButton);
332 deleteAndZero (testResultsBox);
335 //[Destructor]. You can add your own custom destruction code here..
336 //[/Destructor]
339 //==============================================================================
340 void AudioDemoLatencyPage::paint (Graphics& g)
342 //[UserPrePaint] Add your own custom painting code here..
343 //[/UserPrePaint]
345 g.fillAll (Colours::lightgrey);
347 //[UserPaint] Add your own custom painting code here..
348 //[/UserPaint]
351 void AudioDemoLatencyPage::resized()
353 liveAudioDisplayComp->setBounds (8, 8, getWidth() - 16, 64);
354 startTestButton->setBounds (8, getHeight() - 41, 168, 32);
355 testResultsBox->setBounds (8, 88, getWidth() - 16, getHeight() - 137);
356 //[UserResized] Add your own custom resize handling here..
357 //[/UserResized]
360 void AudioDemoLatencyPage::buttonClicked (Button* buttonThatWasClicked)
362 //[UserbuttonClicked_Pre]
363 //[/UserbuttonClicked_Pre]
365 if (buttonThatWasClicked == startTestButton)
367 //[UserButtonCode_startTestButton] -- add your button handler code here..
368 latencyTester->beginTest();
369 //[/UserButtonCode_startTestButton]
372 //[UserbuttonClicked_Post]
373 //[/UserbuttonClicked_Post]
378 //[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
379 //[/MiscUserCode]
382 //==============================================================================
383 #if 0
384 /* -- Jucer information section --
386 This is where the Jucer puts all of its metadata, so don't change anything in here!
388 BEGIN_JUCER_METADATA
390 <JUCER_COMPONENT documentType="Component" className="AudioDemoLatencyPage" componentName=""
391 parentClasses="public Component" constructorParams="AudioDeviceManager&amp; deviceManager_"
392 variableInitialisers="deviceManager (deviceManager_)" snapPixels="8"
393 snapActive="1" snapShown="1" overlayOpacity="0.330000013" fixedSize="0"
394 initialWidth="600" initialHeight="400">
395 <BACKGROUND backgroundColour="ffd3d3d3"/>
396 <GENERICCOMPONENT name="" id="7d70eb2617f56220" memberName="liveAudioDisplayComp"
397 virtualName="" explicitFocusOrder="0" pos="8 8 16M 64" class="LiveAudioInputDisplayComp"
398 params=""/>
399 <TEXTBUTTON name="" id="83ed8fcbf36419df" memberName="startTestButton" virtualName=""
400 explicitFocusOrder="0" pos="8 41R 168 32" buttonText="Test Latency"
401 connectedEdges="0" needsCallback="1" radioGroupId="0"/>
402 <TEXTEDITOR name="" id="95d03a8403bc35da" memberName="testResultsBox" virtualName=""
403 explicitFocusOrder="0" pos="8 88 16M 137M" bkgcol="32ffffff"
404 outlinecol="1c000000" shadowcol="16000000" initialText="Running this test measures the round-trip latency between the audio output and input devices you've got selected.&#10;&#10;It'll play a sound, then try to measure the time at which the sound arrives back at the audio input. Obviously for this to work you need to have your microphone somewhere near your speakers..."
405 multiline="1" retKeyStartsLine="1" readonly="1" scrollbars="1"
406 caret="0" popupmenu="1"/>
407 </JUCER_COMPONENT>
409 END_JUCER_METADATA
411 #endif