Advertisement
Guest User

Untitled

a guest
Feb 23rd, 2021
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.64 KB | None | 0 0
  1. // ======= MAINCOMPONENT.H =================================================================================================
  2. #pragma once
  3.  
  4. #include <JuceHeader.h>
  5.  
  6. //==============================================================================
  7. /*
  8.     This component lives inside our window, and this is where you should put all
  9.     your controls and content.
  10. */
  11. class MainComponent  : public juce::AudioAppComponent, private juce::Timer
  12. {
  13. public:
  14.     //==============================================================================
  15.     MainComponent();
  16.     ~MainComponent() override;
  17.  
  18.     //==============================================================================
  19.     void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
  20.     void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
  21.     void releaseResources() override;
  22.     void timerCallback() override;
  23.     void pushNextSampleIntoFifo(float sample);
  24.  
  25.     //==============================================================================
  26.     void paint (juce::Graphics& g) override;
  27.     void resized() override;
  28.     void drawNextLineOfSpectrogram();
  29.  
  30.     enum
  31.     {
  32.         fftOrder = 11,
  33.         fftSize = 1 << fftOrder,
  34.         scopeSize = 512
  35.     };
  36.  
  37. private:
  38.     //==============================================================================
  39.     static constexpr unsigned int numInputChannels{ 1 };
  40.     static constexpr unsigned int numOutputChannels{ 2 };
  41.  
  42.     const int midiChannels = 10;
  43.     juce::TextButton createMidiButton;
  44.     juce::Slider velocitySlider;
  45.     juce::Slider gainSlider;
  46.     juce::TextEditor noteInput;
  47.     juce::TextEditor midiOutputBox;
  48.  
  49.     juce::AudioDeviceSelectorComponent audioSetupComp;
  50.  
  51.     void setNoteNum(const unsigned int& noteNum, const juce::uint8& velocity);
  52.     void addToOutputList(const juce::MidiMessage& midiMessage);
  53.     void addToOutputList(juce::String msg);
  54.  
  55.     // FFT stuff
  56.     juce::dsp::FFT forwardFFT;
  57.     juce::Image spectrogramImage;
  58.  
  59.     std::array<float, fftSize> fifo;
  60.     std::array<float, fftSize * 2> fftData;
  61.     int fifoIndex = 0;
  62.     bool nextFFTBlockReady = false;
  63.  
  64.     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
  65. };
  66.  
  67. // ======== MAINCOMPONENT.H END ============================================================================================
  68.  
  69. // ======== MAINCOMPONENT.CPP ==============================================================================================
  70. #include "MainComponent.h"
  71.  
  72. //==============================================================================
  73. MainComponent::MainComponent() :
  74.     forwardFFT(fftOrder),
  75.     spectrogramImage(juce::Image::RGB, 512, 512, true),
  76.     audioSetupComp (
  77.         deviceManager,
  78.         0,      // min input ch
  79.         256,    // max input ch
  80.         0,      // min output ch
  81.         256,    // max output ch
  82.         true,  // can select midi inputs?
  83.         true,  // can select midi output device?
  84.         false,  // treat channels as stereo pairs
  85.         false)  // hide advanced options?
  86. {
  87.     // Some platforms require permissions to open input channels so request that here
  88.     if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
  89.         && ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
  90.     {
  91.         juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
  92.                                            [&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
  93.     }
  94.     else
  95.     {
  96.         // Specify the number of input and output channels that we want to open
  97.         setAudioChannels (numInputChannels, numOutputChannels);
  98.     }
  99.    
  100.     addAndMakeVisible(audioSetupComp);
  101.  
  102.     addAndMakeVisible(createMidiButton);
  103.     createMidiButton.setButtonText("Create MIDI note");
  104.     createMidiButton.onClick = [this] {
  105.         constexpr unsigned int offset { 12 }; // For some reason, JUCE's Midi Messages are off by one octave.
  106.         int noteVal = noteInput.getTextValue().getValue();
  107.         setNoteNum((noteVal + offset), velocitySlider.getValue());
  108.     };
  109.  
  110.     addAndMakeVisible(velocitySlider);
  111.     velocitySlider.setRange(0, 127, 1);
  112.  
  113.     addAndMakeVisible(gainSlider);
  114.     gainSlider.setRange(0, 1, 0.01);
  115.  
  116.     addAndMakeVisible(noteInput);
  117.     noteInput.setMultiLine(false);
  118.     noteInput.setCaretVisible(true);
  119.     noteInput.setReadOnly(false);
  120.  
  121.     addAndMakeVisible(midiOutputBox);
  122.     midiOutputBox.setMultiLine(true);
  123.     midiOutputBox.setReturnKeyStartsNewLine(true);
  124.     midiOutputBox.setReadOnly(true);
  125.     midiOutputBox.setScrollbarsShown(true);
  126.     midiOutputBox.setCaretVisible(false);
  127.     midiOutputBox.setPopupMenuEnabled(true);
  128.     midiOutputBox.setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x32ffffff));
  129.     midiOutputBox.setColour(juce::TextEditor::outlineColourId, juce::Colour(0x1c000000));
  130.     midiOutputBox.setColour(juce::TextEditor::shadowColourId, juce::Colour(0x16000000));
  131.  
  132.     // Make sure you set the size of the component after
  133.     // you add any child components.
  134.     startTimerHz(60);
  135.  
  136.     setSize(1000, 600);
  137. }
  138.  
  139. MainComponent::~MainComponent()
  140. {
  141.     // This shuts down the audio device and clears the audio source.
  142.     shutdownAudio();
  143. }
  144.  
  145. //==============================================================================
  146. void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
  147. {
  148.     // This function will be called when the audio device is started, or when
  149.     // its settings (i.e. sample rate, block size, etc) are changed.
  150.  
  151.     // You can use this function to initialise any resources you might need,
  152.     // but be careful - it will be called on the audio thread, not the GUI thread.
  153.  
  154.     // For more details, see the help for AudioProcessor::prepareToPlay()
  155. }
  156.  
  157. void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
  158. {
  159.     // Your audio-processing code goes here!
  160.  
  161.     // For more details, see the help for AudioProcessor::getNextAudioBlock()
  162.  
  163.     // Right now we are not producing any data, in which case we need to clear the buffer
  164.     // (to prevent the output of random noise)
  165.     if (bufferToFill.buffer->getNumChannels() > 0)
  166.     {
  167.         auto* channelData = bufferToFill.buffer->getReadPointer(0, bufferToFill.startSample);
  168.         auto* outBuffer_1 = bufferToFill.buffer->getWritePointer(0, bufferToFill.startSample);
  169.         auto* outBuffer_2 = bufferToFill.buffer->getWritePointer(1, bufferToFill.startSample);
  170.  
  171.         for (unsigned int i = 0; i < bufferToFill.numSamples; ++i)
  172.         {
  173.             pushNextSampleIntoFifo(channelData[i]);
  174.             outBuffer_1[i] = channelData[i] * gainSlider.getValue();
  175.             outBuffer_2[i] = channelData[i] * gainSlider.getValue();
  176.         }
  177.     }
  178. }
  179.  
  180. void MainComponent::releaseResources()
  181. {
  182.     // This will be called when the audio device stops, or when it is being
  183.     // restarted due to a setting change.
  184.  
  185.     // For more details, see the help for AudioProcessor::releaseResources()
  186. }
  187.  
  188. void MainComponent::timerCallback()
  189. {
  190.     if (nextFFTBlockReady)
  191.     {
  192.         drawNextLineOfSpectrogram();
  193.         nextFFTBlockReady = false;
  194.         repaint();
  195.     }
  196. }
  197.  
  198. void MainComponent::pushNextSampleIntoFifo(float sample)
  199. {
  200.     // When fifo contains enough data, flag is set to say next frame should be rendered.
  201.     if (fifoIndex == fftSize)
  202.     {
  203.         if (!nextFFTBlockReady)
  204.         {
  205.             std::fill(fftData.begin(), fftData.end(), 0.0f);
  206.             std::copy(fifo.begin(), fifo.end(), fftData.begin());
  207.             nextFFTBlockReady = true;
  208.         }
  209.  
  210.         fifoIndex = 0;
  211.     }
  212.  
  213.     fifo[fifoIndex++] = sample;
  214. }
  215.  
  216. //==============================================================================
  217. void MainComponent::paint (juce::Graphics& g)
  218. {
  219.     // (Our component is opaque, so we must completely fill the background with a solid colour)
  220.     g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
  221.  
  222.     g.drawImage(spectrogramImage, getLocalBounds().toFloat());
  223.     // You can add your drawing code here!
  224.  
  225. }
  226.  
  227. void MainComponent::resized()
  228. {
  229.     // This is called when the MainContentComponent is resized.
  230.     // If you add any child components, this is where you should
  231.     // update their positions.
  232.     auto rect = getLocalBounds();
  233.    
  234.     auto halfWidth = getWidth() / 2;
  235.     auto halfHeight = getHeight() / 2;
  236.  
  237.     audioSetupComp.setBounds(rect.withWidth(halfWidth));
  238.    
  239.     createMidiButton.setBounds(rect.getCentreX(), 10, halfWidth/2 - 10, 20);
  240.     noteInput.setBounds(rect.getCentreX(), 40, halfWidth/2 - 10, 20);
  241.     velocitySlider.setBounds(rect.getCentreX(), 70, halfWidth/2 - 10, 20);
  242.     gainSlider.setBounds(rect.getCentreX(), 100, halfWidth / 2 - 10, 20);
  243.  
  244.     midiOutputBox.setBounds(halfWidth, halfHeight, halfWidth - 10, halfHeight - 10);
  245. }
  246.  
  247. void MainComponent::drawNextLineOfSpectrogram()
  248. {
  249.     auto rightHandEdge = spectrogramImage.getWidth() - 1;
  250.     auto imageHeight = spectrogramImage.getHeight();
  251.  
  252.     spectrogramImage.moveImageSection(0, 0, 1, 0, rightHandEdge, imageHeight);
  253.  
  254.     forwardFFT.performRealOnlyForwardTransform(fftData.data());
  255.  
  256.     auto maxLevel = juce::FloatVectorOperations::findMinAndMax(fftData.data(), fftSize / 2);
  257.  
  258.     for (auto y = 1; y < imageHeight; ++y)
  259.     {
  260.         auto skewedProportionY = 1.0f - std::exp(std::log((float)y / (float)imageHeight) * 0.2f);
  261.         auto fftDataIndex = (size_t)juce::jlimit(0, fftSize / 2, (int)(skewedProportionY * fftSize / 2));
  262.         auto level = juce::jmap(fftData[fftDataIndex], 0.0f, juce::jmax(maxLevel.getEnd(), 1e-5f), 0.0f, 1.0f);
  263.  
  264.         spectrogramImage.setPixelAt(rightHandEdge, y, juce::Colour::fromHSV(level, 1.0f, level, 1.0f));
  265.     }
  266. }
  267.  
  268. void MainComponent::setNoteNum(const unsigned int& noteNum, const juce::uint8& velocity)
  269. {
  270.     // Creates MIDI message based on inputed note number and velocity,
  271.     // and sends it to the output list.
  272.  
  273.     auto midiMessage{ juce::MidiMessage::noteOn(midiChannels, noteNum, velocity) };
  274.     addToOutputList(midiMessage);
  275. }
  276.  
  277. void MainComponent::addToOutputList(const juce::MidiMessage& midiMessage)
  278. {
  279.     // Displays midi messages to the output list.
  280.    
  281.     midiOutputBox.moveCaretToEnd();
  282.     midiOutputBox.insertTextAtCaret(midiMessage.getDescription() + juce::newLine);
  283. }
  284.  
  285. void MainComponent::addToOutputList(juce::String msg)
  286. {
  287.     // Displays general messages (debugging) to the output list.
  288.  
  289.     midiOutputBox.moveCaretToEnd();
  290.     midiOutputBox.insertTextAtCaret(msg);
  291. }
  292. // ======= MAINCOMPONENT.CPP END ===========================================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement