The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
13KB

  1. /*
  2. ==============================================================================
  3. This is an automatically generated file created by the Jucer!
  4. Creation date: 13 Nov 2009 3:52:50 pm
  5. Be careful when adding custom code to these files, as only the code within
  6. the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
  7. and re-saved.
  8. Jucer version: 1.12
  9. ------------------------------------------------------------------------------
  10. The Jucer is part of the JUCE library - "Jules' Utility Class Extensions"
  11. Copyright 2004-6 by Raw Material Software ltd.
  12. ==============================================================================
  13. */
  14. //[Headers] You can add your own extra header files here...
  15. //[/Headers]
  16. #include "AudioDemoRecordPage.h"
  17. //[MiscUserDefs] You can add your own user definitions and misc code here...
  18. //==============================================================================
  19. /* This is a rough-and-ready circular buffer, used to allow the audio thread to
  20. push data quickly into a queue, allowing a background thread to come along and
  21. write it to disk later.
  22. */
  23. class CircularAudioBuffer
  24. {
  25. public:
  26. CircularAudioBuffer (const int numChannels, const int numSamples)
  27. : buffer (numChannels, numSamples)
  28. {
  29. clear();
  30. }
  31. ~CircularAudioBuffer()
  32. {
  33. }
  34. void clear()
  35. {
  36. buffer.clear();
  37. const ScopedLock sl (bufferLock);
  38. bufferValidStart = bufferValidEnd = 0;
  39. }
  40. void addSamplesToBuffer (const AudioSampleBuffer& sourceBuffer, int numSamples)
  41. {
  42. const int bufferSize = buffer.getNumSamples();
  43. bufferLock.enter();
  44. int newDataStart = bufferValidEnd;
  45. int newDataEnd = newDataStart + numSamples;
  46. const int actualNewDataEnd = newDataEnd;
  47. bufferValidStart = jmax (bufferValidStart, newDataEnd - bufferSize);
  48. bufferLock.exit();
  49. newDataStart %= bufferSize;
  50. newDataEnd %= bufferSize;
  51. if (newDataEnd < newDataStart)
  52. {
  53. for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;)
  54. {
  55. buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, bufferSize - newDataStart);
  56. buffer.copyFrom (i, 0, sourceBuffer, i, bufferSize - newDataStart, newDataEnd);
  57. }
  58. }
  59. else
  60. {
  61. for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;)
  62. buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, newDataEnd - newDataStart);
  63. }
  64. const ScopedLock sl (bufferLock);
  65. bufferValidEnd = actualNewDataEnd;
  66. }
  67. int readSamplesFromBuffer (AudioSampleBuffer& destBuffer, int numSamples)
  68. {
  69. const int bufferSize = buffer.getNumSamples();
  70. bufferLock.enter();
  71. int availableDataStart = bufferValidStart;
  72. const int numSamplesDone = jmin (numSamples, bufferValidEnd - availableDataStart);
  73. int availableDataEnd = availableDataStart + numSamplesDone;
  74. bufferValidStart = availableDataEnd;
  75. bufferLock.exit();
  76. availableDataStart %= bufferSize;
  77. availableDataEnd %= bufferSize;
  78. if (availableDataEnd < availableDataStart)
  79. {
  80. for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;)
  81. {
  82. destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, bufferSize - availableDataStart);
  83. destBuffer.copyFrom (i, bufferSize - availableDataStart, buffer, i, 0, availableDataEnd);
  84. }
  85. }
  86. else
  87. {
  88. for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;)
  89. destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, numSamplesDone);
  90. }
  91. return numSamplesDone;
  92. }
  93. private:
  94. CriticalSection bufferLock;
  95. AudioSampleBuffer buffer;
  96. int bufferValidStart, bufferValidEnd;
  97. };
  98. //==============================================================================
  99. class AudioRecorder : public AudioIODeviceCallback,
  100. public Thread
  101. {
  102. public:
  103. AudioRecorder()
  104. : Thread ("audio recorder"),
  105. circularBuffer (2, 48000),
  106. recording (false),
  107. sampleRate (0)
  108. {
  109. }
  110. ~AudioRecorder()
  111. {
  112. stop();
  113. }
  114. //==============================================================================
  115. void startRecording (const File& file)
  116. {
  117. stop();
  118. if (sampleRate > 0)
  119. {
  120. fileToRecord = file;
  121. startThread();
  122. circularBuffer.clear();
  123. recording = true;
  124. }
  125. }
  126. void stop()
  127. {
  128. recording = false;
  129. stopThread (5000);
  130. }
  131. bool isRecording() const
  132. {
  133. return isThreadRunning() && recording;
  134. }
  135. //==============================================================================
  136. void audioDeviceAboutToStart (AudioIODevice* device)
  137. {
  138. sampleRate = device->getCurrentSampleRate();
  139. }
  140. void audioDeviceStopped()
  141. {
  142. sampleRate = 0;
  143. }
  144. void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
  145. float** outputChannelData, int numOutputChannels,
  146. int numSamples)
  147. {
  148. if (recording)
  149. {
  150. const AudioSampleBuffer incomingData ((float**) inputChannelData, numInputChannels, numSamples);
  151. circularBuffer.addSamplesToBuffer (incomingData, numSamples);
  152. }
  153. // We need to clear the output buffers, in case they're full of junk..
  154. for (int i = 0; i < numOutputChannels; ++i)
  155. if (outputChannelData[i] != 0)
  156. zeromem (outputChannelData[i], sizeof (float) * numSamples);
  157. }
  158. //==============================================================================
  159. void run()
  160. {
  161. fileToRecord.deleteFile();
  162. OutputStream* outStream = fileToRecord.createOutputStream();
  163. if (outStream == 0)
  164. return;
  165. WavAudioFormat wavFormat;
  166. AudioFormatWriter* writer = wavFormat.createWriterFor (outStream, sampleRate, 1, 16, StringPairArray(), 0);
  167. if (writer == 0)
  168. {
  169. delete outStream;
  170. return;
  171. }
  172. AudioSampleBuffer tempBuffer (2, 8192);
  173. while (! threadShouldExit())
  174. {
  175. int numSamplesReady = circularBuffer.readSamplesFromBuffer (tempBuffer, tempBuffer.getNumSamples());
  176. if (numSamplesReady > 0)
  177. tempBuffer.writeToAudioWriter (writer, 0, numSamplesReady);
  178. Thread::sleep (1);
  179. }
  180. delete writer;
  181. }
  182. File fileToRecord;
  183. double sampleRate;
  184. bool recording;
  185. CircularAudioBuffer circularBuffer;
  186. };
  187. //[/MiscUserDefs]
  188. //==============================================================================
  189. AudioDemoRecordPage::AudioDemoRecordPage (AudioDeviceManager& deviceManager_)
  190. : deviceManager (deviceManager_),
  191. liveAudioDisplayComp (0),
  192. explanationLabel (0),
  193. recordButton (0)
  194. {
  195. addAndMakeVisible (liveAudioDisplayComp = new LiveAudioInputDisplayComp());
  196. addAndMakeVisible (explanationLabel = new Label (String::empty,
  197. T("This page demonstrates how to record a wave file from the live audio input..\n\nPressing record will start recording a file in your \"Documents\" folder.")));
  198. explanationLabel->setFont (Font (15.0000f, Font::plain));
  199. explanationLabel->setJustificationType (Justification::topLeft);
  200. explanationLabel->setEditable (false, false, false);
  201. explanationLabel->setColour (TextEditor::textColourId, Colours::black);
  202. explanationLabel->setColour (TextEditor::backgroundColourId, Colour (0x0));
  203. addAndMakeVisible (recordButton = new TextButton (String::empty));
  204. recordButton->setButtonText (T("Record"));
  205. recordButton->addButtonListener (this);
  206. recordButton->setColour (TextButton::buttonColourId, Colour (0xffff5c5c));
  207. recordButton->setColour (TextButton::textColourOffId, Colours::black);
  208. //[UserPreSize]
  209. //[/UserPreSize]
  210. setSize (600, 400);
  211. //[Constructor] You can add your own custom stuff here..
  212. recorder = new AudioRecorder();
  213. deviceManager.addAudioCallback (recorder);
  214. deviceManager.addAudioCallback (liveAudioDisplayComp);
  215. //[/Constructor]
  216. }
  217. AudioDemoRecordPage::~AudioDemoRecordPage()
  218. {
  219. //[Destructor_pre]. You can add your own custom destruction code here..
  220. recorder->stop();
  221. deviceManager.removeAudioCallback (recorder);
  222. delete recorder;
  223. deviceManager.removeAudioCallback (liveAudioDisplayComp);
  224. //[/Destructor_pre]
  225. deleteAndZero (liveAudioDisplayComp);
  226. deleteAndZero (explanationLabel);
  227. deleteAndZero (recordButton);
  228. //[Destructor]. You can add your own custom destruction code here..
  229. //[/Destructor]
  230. }
  231. //==============================================================================
  232. void AudioDemoRecordPage::paint (Graphics& g)
  233. {
  234. //[UserPrePaint] Add your own custom painting code here..
  235. //[/UserPrePaint]
  236. g.fillAll (Colours::lightgrey);
  237. //[UserPaint] Add your own custom painting code here..
  238. //[/UserPaint]
  239. }
  240. void AudioDemoRecordPage::resized()
  241. {
  242. liveAudioDisplayComp->setBounds (8, 8, getWidth() - 16, 64);
  243. explanationLabel->setBounds (160, 88, getWidth() - 169, 216);
  244. recordButton->setBounds (8, 88, 136, 40);
  245. //[UserResized] Add your own custom resize handling here..
  246. //[/UserResized]
  247. }
  248. void AudioDemoRecordPage::buttonClicked (Button* buttonThatWasClicked)
  249. {
  250. //[UserbuttonClicked_Pre]
  251. //[/UserbuttonClicked_Pre]
  252. if (buttonThatWasClicked == recordButton)
  253. {
  254. //[UserButtonCode_recordButton] -- add your button handler code here..
  255. if (recorder->isRecording())
  256. {
  257. recorder->stop();
  258. }
  259. else
  260. {
  261. File file (File::getSpecialLocation (File::userDocumentsDirectory)
  262. .getNonexistentChildFile ("Juce Demo Audio Recording", ".wav"));
  263. recorder->startRecording (file);
  264. }
  265. if (recorder->isRecording())
  266. recordButton->setButtonText ("Stop");
  267. else
  268. recordButton->setButtonText ("Record");
  269. //[/UserButtonCode_recordButton]
  270. }
  271. //[UserbuttonClicked_Post]
  272. //[/UserbuttonClicked_Post]
  273. }
  274. void AudioDemoRecordPage::visibilityChanged()
  275. {
  276. //[UserCode_visibilityChanged] -- Add your code here...
  277. recorder->stop();
  278. recordButton->setButtonText ("Record");
  279. //[/UserCode_visibilityChanged]
  280. }
  281. //[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
  282. //[/MiscUserCode]
  283. //==============================================================================
  284. #if 0
  285. /* -- Jucer information section --
  286. This is where the Jucer puts all of its metadata, so don't change anything in here!
  287. BEGIN_JUCER_METADATA
  288. <JUCER_COMPONENT documentType="Component" className="AudioDemoRecordPage" componentName=""
  289. parentClasses="public Component" constructorParams="AudioDeviceManager&amp; deviceManager_"
  290. variableInitialisers="deviceManager (deviceManager_)" snapPixels="8"
  291. snapActive="1" snapShown="1" overlayOpacity="0.330000013" fixedSize="0"
  292. initialWidth="600" initialHeight="400">
  293. <METHODS>
  294. <METHOD name="visibilityChanged()"/>
  295. </METHODS>
  296. <BACKGROUND backgroundColour="ffd3d3d3"/>
  297. <GENERICCOMPONENT name="" id="7d70eb2617f56220" memberName="liveAudioDisplayComp"
  298. virtualName="" explicitFocusOrder="0" pos="8 8 16M 64" class="LiveAudioInputDisplayComp"
  299. params=""/>
  300. <LABEL name="" id="1162fb2599a768b4" memberName="explanationLabel" virtualName=""
  301. explicitFocusOrder="0" pos="160 88 169M 216" edTextCol="ff000000"
  302. edBkgCol="0" labelText="This page demonstrates how to record a wave file from the live audio input..&#10;&#10;Pressing record will start recording a file in your &quot;Documents&quot; folder."
  303. editableSingleClick="0" editableDoubleClick="0" focusDiscardsChanges="0"
  304. fontname="Default font" fontsize="15" bold="0" italic="0" justification="9"/>
  305. <TEXTBUTTON name="" id="2c10a0ba9fad39da" memberName="recordButton" virtualName=""
  306. explicitFocusOrder="0" pos="8 88 136 40" bgColOff="ffff5c5c"
  307. textCol="ff000000" buttonText="Record" connectedEdges="0" needsCallback="1"
  308. radioGroupId="0"/>
  309. </JUCER_COMPONENT>
  310. END_JUCER_METADATA
  311. */
  312. #endif