/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once #include "../JuceLibraryCode/JuceHeader.h" //============================================================================== class AudioThumbnailComponent : public Component, public FileDragAndDropTarget, public ChangeBroadcaster, private ChangeListener, private Timer { public: AudioThumbnailComponent (AudioDeviceManager& adm, AudioFormatManager& afm) : audioDeviceManager (adm), thumbnailCache (5), thumbnail (128, afm, thumbnailCache) { thumbnail.addChangeListener (this); } ~AudioThumbnailComponent() { thumbnail.removeChangeListener (this); } void paint (Graphics& g) override { g.fillAll (Colour (0xff495358)); g.setColour (Colours::white); if (thumbnail.getTotalLength() > 0.0) { thumbnail.drawChannels (g, getLocalBounds().reduced (2), 0.0, thumbnail.getTotalLength(), 1.0f); g.setColour (Colours::black); g.fillRect (static_cast (currentPosition * getWidth()), 0.0f, 1.0f, static_cast (getHeight())); } else { g.drawFittedText ("No audio file loaded.\nDrop a file here or click the \"Load File...\" button.", getLocalBounds(), Justification::centred, 2); } } bool isInterestedInFileDrag (const StringArray&) override { return true; } void filesDropped (const StringArray& files, int, int) override { loadFile (File (files[0]), true); } void setCurrentFile (const File& f) { if (currentFile == f) return; loadFile (f); } File getCurrentFile() { return currentFile; } void setTransportSource (AudioTransportSource* newSource) { transportSource = newSource; struct ResetCallback : public CallbackMessage { ResetCallback (AudioThumbnailComponent& o) : owner (o) {} void messageCallback() override { owner.reset(); } AudioThumbnailComponent& owner; }; (new ResetCallback (*this))->post(); } private: AudioDeviceManager& audioDeviceManager; AudioThumbnailCache thumbnailCache; AudioThumbnail thumbnail; AudioTransportSource* transportSource = nullptr; File currentFile; double currentPosition = 0.0; //============================================================================== void changeListenerCallback (ChangeBroadcaster*) override { repaint(); } void reset() { currentPosition = 0.0; repaint(); if (transportSource == nullptr) stopTimer(); else startTimerHz (25); } void loadFile (const File& f, bool notify = false) { if (currentFile == f || ! f.existsAsFile()) return; currentFile = f; thumbnail.setSource (new FileInputSource (f)); if (notify) sendChangeMessage(); } void timerCallback() override { if (transportSource != nullptr) { currentPosition = transportSource->getCurrentPosition() / thumbnail.getTotalLength(); repaint(); } } void mouseDrag (const MouseEvent& e) override { if (transportSource != nullptr) { const ScopedLock sl (audioDeviceManager.getAudioCallbackLock()); transportSource->setPosition ((jmax (static_cast (e.x), 0.0) / getWidth()) * thumbnail.getTotalLength()); } } }; //============================================================================== class AudioPlayerHeader : public Component, private Button::Listener, private ChangeListener, private Value::Listener { public: AudioPlayerHeader(); ~AudioPlayerHeader(); void paint (Graphics&) override; void resized() override; AudioThumbnailComponent thumbnailComp; private: //============================================================================== void buttonClicked (Button*) override; void changeListenerCallback (ChangeBroadcaster*) override; void valueChanged (Value& value) override; //============================================================================== TextButton loadButton { "Load File..." }, playButton { "Play" }; ToggleButton loopButton { "Loop File" }; }; //============================================================================== class DemoParametersComponent : public Component { public: DemoParametersComponent (const std::vector& demoParams) { parameters = demoParams; for (auto demoParameter : parameters) { addAndMakeVisible (demoParameter->getComponent()); auto* paramLabel = new Label ({}, demoParameter->name); paramLabel->attachToComponent (demoParameter->getComponent(), true); paramLabel->setJustificationType (Justification::centredLeft); addAndMakeVisible (paramLabel); labels.add (paramLabel); } } void resized() override { auto bounds = getLocalBounds(); bounds.removeFromLeft (100); for (auto* p : parameters) { auto* comp = p->getComponent(); comp->setSize (jmin (bounds.getWidth(), p->getPreferredWidth()), p->getPreferredHeight()); auto compBounds = bounds.removeFromTop (p->getPreferredHeight()); comp->setCentrePosition (compBounds.getCentre()); } } int getHeightNeeded() { auto height = 0; for (auto* p : parameters) height += p->getPreferredHeight(); return height + 10; } private: std::vector parameters; OwnedArray