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.

316 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../JuceDemoHeader.h"
  18. #include "AudioLiveScrollingDisplay.h"
  19. //==============================================================================
  20. /** Our demo synth sound is just a basic sine wave.. */
  21. struct SineWaveSound : public SynthesiserSound
  22. {
  23. SineWaveSound() {}
  24. bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
  25. bool appliesToChannel (int /*midiChannel*/) override { return true; }
  26. };
  27. //==============================================================================
  28. /** Our demo synth voice just plays a sine wave.. */
  29. struct SineWaveVoice : public SynthesiserVoice
  30. {
  31. SineWaveVoice() : currentAngle (0), angleDelta (0), level (0), tailOff (0)
  32. {
  33. }
  34. bool canPlaySound (SynthesiserSound* sound) override
  35. {
  36. return dynamic_cast<SineWaveSound*> (sound) != nullptr;
  37. }
  38. void startNote (int midiNoteNumber, float velocity,
  39. SynthesiserSound*, int /*currentPitchWheelPosition*/) override
  40. {
  41. currentAngle = 0.0;
  42. level = velocity * 0.15;
  43. tailOff = 0.0;
  44. double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  45. double cyclesPerSample = cyclesPerSecond / getSampleRate();
  46. angleDelta = cyclesPerSample * 2.0 * double_Pi;
  47. }
  48. void stopNote (float /*velocity*/, bool allowTailOff) override
  49. {
  50. if (allowTailOff)
  51. {
  52. // start a tail-off by setting this flag. The render callback will pick up on
  53. // this and do a fade out, calling clearCurrentNote() when it's finished.
  54. if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
  55. // stopNote method could be called more than once.
  56. tailOff = 1.0;
  57. }
  58. else
  59. {
  60. // we're being told to stop playing immediately, so reset everything..
  61. clearCurrentNote();
  62. angleDelta = 0.0;
  63. }
  64. }
  65. void pitchWheelMoved (int /*newValue*/) override
  66. {
  67. // can't be bothered implementing this for the demo!
  68. }
  69. void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
  70. {
  71. // not interested in controllers in this case.
  72. }
  73. void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
  74. {
  75. if (angleDelta != 0.0)
  76. {
  77. if (tailOff > 0)
  78. {
  79. while (--numSamples >= 0)
  80. {
  81. const float currentSample = (float) (std::sin (currentAngle) * level * tailOff);
  82. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  83. outputBuffer.addSample (i, startSample, currentSample);
  84. currentAngle += angleDelta;
  85. ++startSample;
  86. tailOff *= 0.99;
  87. if (tailOff <= 0.005)
  88. {
  89. clearCurrentNote();
  90. angleDelta = 0.0;
  91. break;
  92. }
  93. }
  94. }
  95. else
  96. {
  97. while (--numSamples >= 0)
  98. {
  99. const float currentSample = (float) (std::sin (currentAngle) * level);
  100. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  101. outputBuffer.addSample (i, startSample, currentSample);
  102. currentAngle += angleDelta;
  103. ++startSample;
  104. }
  105. }
  106. }
  107. }
  108. private:
  109. double currentAngle, angleDelta, level, tailOff;
  110. };
  111. //==============================================================================
  112. // This is an audio source that streams the output of our demo synth.
  113. struct SynthAudioSource : public AudioSource
  114. {
  115. SynthAudioSource (MidiKeyboardState& keyState) : keyboardState (keyState)
  116. {
  117. // Add some voices to our synth, to play the sounds..
  118. for (int i = 4; --i >= 0;)
  119. {
  120. synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
  121. synth.addVoice (new SamplerVoice()); // and these ones play the sampled sounds
  122. }
  123. // ..and add a sound for them to play...
  124. setUsingSineWaveSound();
  125. }
  126. void setUsingSineWaveSound()
  127. {
  128. synth.clearSounds();
  129. synth.addSound (new SineWaveSound());
  130. }
  131. void setUsingSampledSound()
  132. {
  133. WavAudioFormat wavFormat;
  134. ScopedPointer<AudioFormatReader> audioReader (wavFormat.createReaderFor (new MemoryInputStream (BinaryData::cello_wav,
  135. BinaryData::cello_wavSize,
  136. false),
  137. true));
  138. BigInteger allNotes;
  139. allNotes.setRange (0, 128, true);
  140. synth.clearSounds();
  141. synth.addSound (new SamplerSound ("demo sound",
  142. *audioReader,
  143. allNotes,
  144. 74, // root midi note
  145. 0.1, // attack time
  146. 0.1, // release time
  147. 10.0 // maximum sample length
  148. ));
  149. }
  150. void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
  151. {
  152. midiCollector.reset (sampleRate);
  153. synth.setCurrentPlaybackSampleRate (sampleRate);
  154. }
  155. void releaseResources() override
  156. {
  157. }
  158. void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
  159. {
  160. // the synth always adds its output to the audio buffer, so we have to clear it
  161. // first..
  162. bufferToFill.clearActiveBufferRegion();
  163. // fill a midi buffer with incoming messages from the midi input.
  164. MidiBuffer incomingMidi;
  165. midiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
  166. // pass these messages to the keyboard state so that it can update the component
  167. // to show on-screen which keys are being pressed on the physical midi keyboard.
  168. // This call will also add midi messages to the buffer which were generated by
  169. // the mouse-clicking on the on-screen keyboard.
  170. keyboardState.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);
  171. // and now get the synth to process the midi events and generate its output.
  172. synth.renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);
  173. }
  174. //==============================================================================
  175. // this collects real-time midi messages from the midi input device, and
  176. // turns them into blocks that we can process in our audio callback
  177. MidiMessageCollector midiCollector;
  178. // this represents the state of which keys on our on-screen keyboard are held
  179. // down. When the mouse is clicked on the keyboard component, this object also
  180. // generates midi messages for this, which we can pass on to our synth.
  181. MidiKeyboardState& keyboardState;
  182. // the synth itself!
  183. Synthesiser synth;
  184. };
  185. //==============================================================================
  186. class AudioSynthesiserDemo : public Component,
  187. private Button::Listener
  188. {
  189. public:
  190. AudioSynthesiserDemo()
  191. : deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
  192. synthAudioSource (keyboardState),
  193. keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard)
  194. {
  195. addAndMakeVisible (keyboardComponent);
  196. addAndMakeVisible (sineButton);
  197. sineButton.setButtonText ("Use sine wave");
  198. sineButton.setRadioGroupId (321);
  199. sineButton.addListener (this);
  200. sineButton.setToggleState (true, dontSendNotification);
  201. addAndMakeVisible (sampledButton);
  202. sampledButton.setButtonText ("Use sampled sound");
  203. sampledButton.setRadioGroupId (321);
  204. sampledButton.addListener (this);
  205. addAndMakeVisible (liveAudioDisplayComp);
  206. deviceManager.addAudioCallback (&liveAudioDisplayComp);
  207. audioSourcePlayer.setSource (&synthAudioSource);
  208. deviceManager.addAudioCallback (&audioSourcePlayer);
  209. deviceManager.addMidiInputCallback (String(), &(synthAudioSource.midiCollector));
  210. setOpaque (true);
  211. setSize (640, 480);
  212. }
  213. ~AudioSynthesiserDemo()
  214. {
  215. audioSourcePlayer.setSource (nullptr);
  216. deviceManager.removeMidiInputCallback (String(), &(synthAudioSource.midiCollector));
  217. deviceManager.removeAudioCallback (&audioSourcePlayer);
  218. deviceManager.removeAudioCallback (&liveAudioDisplayComp);
  219. }
  220. //==============================================================================
  221. void paint (Graphics& g) override
  222. {
  223. fillStandardDemoBackground (g);
  224. }
  225. void resized() override
  226. {
  227. keyboardComponent.setBounds (8, 96, getWidth() - 16, 64);
  228. sineButton.setBounds (16, 176, 150, 24);
  229. sampledButton.setBounds (16, 200, 150, 24);
  230. liveAudioDisplayComp.setBounds (8, 8, getWidth() - 16, 64);
  231. }
  232. private:
  233. AudioDeviceManager& deviceManager;
  234. MidiKeyboardState keyboardState;
  235. AudioSourcePlayer audioSourcePlayer;
  236. SynthAudioSource synthAudioSource;
  237. MidiKeyboardComponent keyboardComponent;
  238. ToggleButton sineButton;
  239. ToggleButton sampledButton;
  240. LiveScrollingAudioDisplay liveAudioDisplayComp;
  241. //==============================================================================
  242. void buttonClicked (Button* buttonThatWasClicked) override
  243. {
  244. if (buttonThatWasClicked == &sineButton)
  245. synthAudioSource.setUsingSineWaveSound();
  246. else if (buttonThatWasClicked == &sampledButton)
  247. synthAudioSource.setUsingSampledSound();
  248. }
  249. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSynthesiserDemo)
  250. };
  251. // This static object will register this demo type in a global list of demos..
  252. static JuceDemoType<AudioSynthesiserDemo> demo ("31 Audio: Synthesisers");