- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-12 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../JuceDemoHeader.h"
|
||||
|
||||
//==============================================================================
|
||||
class DemoThumbnailComp : public Component,
|
||||
public ChangeListener,
|
||||
public FileDragAndDropTarget,
|
||||
public ChangeBroadcaster,
|
||||
private ScrollBar::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
DemoThumbnailComp (AudioFormatManager& formatManager,
|
||||
AudioTransportSource& transportSource_,
|
||||
Slider& slider)
|
||||
: transportSource (transportSource_),
|
||||
zoomSlider (slider),
|
||||
scrollbar (false),
|
||||
thumbnailCache (5),
|
||||
thumbnail (512, formatManager, thumbnailCache),
|
||||
isFollowingTransport (false)
|
||||
{
|
||||
thumbnail.addChangeListener (this);
|
||||
|
||||
addAndMakeVisible (scrollbar);
|
||||
scrollbar.setRangeLimits (visibleRange);
|
||||
scrollbar.setAutoHide (false);
|
||||
scrollbar.addListener (this);
|
||||
|
||||
currentPositionMarker.setFill (Colours::white.withAlpha (0.85f));
|
||||
addAndMakeVisible (currentPositionMarker);
|
||||
}
|
||||
|
||||
~DemoThumbnailComp()
|
||||
{
|
||||
scrollbar.removeListener (this);
|
||||
thumbnail.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setFile (const File& file)
|
||||
{
|
||||
if (! file.isDirectory())
|
||||
{
|
||||
thumbnail.setSource (new FileInputSource (file));
|
||||
const Range<double> newRange (0.0, thumbnail.getTotalLength());
|
||||
scrollbar.setRangeLimits (newRange);
|
||||
setRange (newRange);
|
||||
|
||||
startTimerHz (40);
|
||||
}
|
||||
}
|
||||
|
||||
File getLastDroppedFile() const noexcept { return lastFileDropped; }
|
||||
|
||||
void setZoomFactor (double amount)
|
||||
{
|
||||
if (thumbnail.getTotalLength() > 0)
|
||||
{
|
||||
const double newScale = jmax (0.001, thumbnail.getTotalLength() * (1.0 - jlimit (0.0, 0.99, amount)));
|
||||
const double timeAtCentre = xToTime (getWidth() / 2.0f);
|
||||
setRange (Range<double> (timeAtCentre - newScale * 0.5, timeAtCentre + newScale * 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
void setRange (Range<double> newRange)
|
||||
{
|
||||
visibleRange = newRange;
|
||||
scrollbar.setCurrentRange (visibleRange);
|
||||
updateCursorPosition();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void setFollowsTransport (bool shouldFollow)
|
||||
{
|
||||
isFollowingTransport = shouldFollow;
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (Colours::darkgrey);
|
||||
g.setColour (Colours::lightblue);
|
||||
|
||||
if (thumbnail.getTotalLength() > 0.0)
|
||||
{
|
||||
Rectangle<int> thumbArea (getLocalBounds());
|
||||
thumbArea.removeFromBottom (scrollbar.getHeight() + 4);
|
||||
thumbnail.drawChannels (g, thumbArea.reduced (2),
|
||||
visibleRange.getStart(), visibleRange.getEnd(), 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setFont (14.0f);
|
||||
g.drawFittedText ("(No audio file selected)", getLocalBounds(), Justification::centred, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
scrollbar.setBounds (getLocalBounds().removeFromBottom (14).reduced (2));
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
// this method is called by the thumbnail when it has changed, so we should repaint it..
|
||||
repaint();
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& /*files*/) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int /*x*/, int /*y*/) override
|
||||
{
|
||||
lastFileDropped = File (files[0]);
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
mouseDrag (e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (canMoveTransport())
|
||||
transportSource.setPosition (jmax (0.0, xToTime ((float) e.x)));
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent&) override
|
||||
{
|
||||
transportSource.start();
|
||||
}
|
||||
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override
|
||||
{
|
||||
if (thumbnail.getTotalLength() > 0.0)
|
||||
{
|
||||
double newStart = visibleRange.getStart() - wheel.deltaX * (visibleRange.getLength()) / 10.0;
|
||||
newStart = jlimit (0.0, jmax (0.0, thumbnail.getTotalLength() - (visibleRange.getLength())), newStart);
|
||||
|
||||
if (canMoveTransport())
|
||||
setRange (Range<double> (newStart, newStart + visibleRange.getLength()));
|
||||
|
||||
if (wheel.deltaY != 0.0f)
|
||||
zoomSlider.setValue (zoomSlider.getValue() - wheel.deltaY);
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
AudioTransportSource& transportSource;
|
||||
Slider& zoomSlider;
|
||||
ScrollBar scrollbar;
|
||||
|
||||
AudioThumbnailCache thumbnailCache;
|
||||
AudioThumbnail thumbnail;
|
||||
Range<double> visibleRange;
|
||||
bool isFollowingTransport;
|
||||
File lastFileDropped;
|
||||
|
||||
DrawableRectangle currentPositionMarker;
|
||||
|
||||
float timeToX (const double time) const
|
||||
{
|
||||
return getWidth() * (float) ((time - visibleRange.getStart()) / (visibleRange.getLength()));
|
||||
}
|
||||
|
||||
double xToTime (const float x) const
|
||||
{
|
||||
return (x / getWidth()) * (visibleRange.getLength()) + visibleRange.getStart();
|
||||
}
|
||||
|
||||
bool canMoveTransport() const noexcept
|
||||
{
|
||||
return ! (isFollowingTransport && transportSource.isPlaying());
|
||||
}
|
||||
|
||||
void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) override
|
||||
{
|
||||
if (scrollBarThatHasMoved == &scrollbar)
|
||||
if (! (isFollowingTransport && transportSource.isPlaying()))
|
||||
setRange (visibleRange.movedToStartAt (newRangeStart));
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (canMoveTransport())
|
||||
updateCursorPosition();
|
||||
else
|
||||
setRange (visibleRange.movedToStartAt (transportSource.getCurrentPosition() - (visibleRange.getLength() / 2.0)));
|
||||
}
|
||||
|
||||
void updateCursorPosition()
|
||||
{
|
||||
currentPositionMarker.setVisible (transportSource.isPlaying() || isMouseButtonDown());
|
||||
|
||||
currentPositionMarker.setRectangle (Rectangle<float> (timeToX (transportSource.getCurrentPosition()) - 0.75f, 0,
|
||||
1.5f, (float) (getHeight() - scrollbar.getHeight())));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AudioPlaybackDemo : public Component,
|
||||
private FileBrowserListener,
|
||||
private Button::Listener,
|
||||
private Slider::Listener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
AudioPlaybackDemo()
|
||||
: deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
|
||||
thread ("audio file preview"),
|
||||
directoryList (nullptr, thread),
|
||||
fileTreeComp (directoryList)
|
||||
{
|
||||
addAndMakeVisible (zoomLabel);
|
||||
zoomLabel.setText ("zoom:", dontSendNotification);
|
||||
zoomLabel.setFont (Font (15.00f, Font::plain));
|
||||
zoomLabel.setJustificationType (Justification::centredRight);
|
||||
zoomLabel.setEditable (false, false, false);
|
||||
zoomLabel.setColour (TextEditor::textColourId, Colours::black);
|
||||
zoomLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
|
||||
|
||||
addAndMakeVisible (followTransportButton);
|
||||
followTransportButton.setButtonText ("Follow Transport");
|
||||
followTransportButton.addListener (this);
|
||||
|
||||
addAndMakeVisible (explanation);
|
||||
explanation.setText ("Select an audio file in the treeview above, and this page will display its waveform, and let you play it..", dontSendNotification);
|
||||
explanation.setFont (Font (14.00f, Font::plain));
|
||||
explanation.setJustificationType (Justification::bottomRight);
|
||||
explanation.setEditable (false, false, false);
|
||||
explanation.setColour (TextEditor::textColourId, Colours::black);
|
||||
explanation.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
|
||||
|
||||
addAndMakeVisible (zoomSlider);
|
||||
zoomSlider.setRange (0, 1, 0);
|
||||
zoomSlider.setSliderStyle (Slider::LinearHorizontal);
|
||||
zoomSlider.setTextBoxStyle (Slider::NoTextBox, false, 80, 20);
|
||||
zoomSlider.addListener (this);
|
||||
zoomSlider.setSkewFactor (2);
|
||||
|
||||
addAndMakeVisible (thumbnail = new DemoThumbnailComp (formatManager, transportSource, zoomSlider));
|
||||
thumbnail->addChangeListener (this);
|
||||
|
||||
addAndMakeVisible (startStopButton);
|
||||
startStopButton.setButtonText ("Play/Stop");
|
||||
startStopButton.addListener (this);
|
||||
startStopButton.setColour (TextButton::buttonColourId, Colour (0xff79ed7f));
|
||||
|
||||
addAndMakeVisible (fileTreeComp);
|
||||
|
||||
// audio setup
|
||||
formatManager.registerBasicFormats();
|
||||
|
||||
directoryList.setDirectory (File::getSpecialLocation (File::userHomeDirectory), true, true);
|
||||
thread.startThread (3);
|
||||
|
||||
fileTreeComp.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f));
|
||||
fileTreeComp.addListener (this);
|
||||
|
||||
deviceManager.addAudioCallback (&audioSourcePlayer);
|
||||
audioSourcePlayer.setSource (&transportSource);
|
||||
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
~AudioPlaybackDemo()
|
||||
{
|
||||
transportSource.setSource (nullptr);
|
||||
audioSourcePlayer.setSource (nullptr);
|
||||
|
||||
deviceManager.removeAudioCallback (&audioSourcePlayer);
|
||||
fileTreeComp.removeListener (this);
|
||||
thumbnail->removeChangeListener (this);
|
||||
followTransportButton.removeListener (this);
|
||||
zoomSlider.removeListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
fillTiledBackground (g);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds().reduced (4));
|
||||
|
||||
Rectangle<int> controls (r.removeFromBottom (90));
|
||||
|
||||
explanation.setBounds (controls.removeFromRight (controls.getWidth() / 3));
|
||||
Rectangle<int> zoom (controls.removeFromTop (25));
|
||||
zoomLabel.setBounds (zoom.removeFromLeft (50));
|
||||
zoomSlider.setBounds (zoom);
|
||||
followTransportButton.setBounds (controls.removeFromTop (25));
|
||||
startStopButton.setBounds (controls);
|
||||
|
||||
r.removeFromBottom (6);
|
||||
thumbnail->setBounds (r.removeFromBottom (140));
|
||||
r.removeFromBottom (6);
|
||||
fileTreeComp.setBounds (r);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioDeviceManager& deviceManager;
|
||||
AudioFormatManager formatManager;
|
||||
TimeSliceThread thread;
|
||||
DirectoryContentsList directoryList;
|
||||
|
||||
AudioSourcePlayer audioSourcePlayer;
|
||||
AudioTransportSource transportSource;
|
||||
ScopedPointer<AudioFormatReaderSource> currentAudioFileSource;
|
||||
|
||||
ScopedPointer<DemoThumbnailComp> thumbnail;
|
||||
Label zoomLabel, explanation;
|
||||
Slider zoomSlider;
|
||||
ToggleButton followTransportButton;
|
||||
TextButton startStopButton;
|
||||
FileTreeComponent fileTreeComp;
|
||||
|
||||
//==============================================================================
|
||||
void showFile (const File& file)
|
||||
{
|
||||
loadFileIntoTransport (file);
|
||||
|
||||
zoomSlider.setValue (0, dontSendNotification);
|
||||
thumbnail->setFile (file);
|
||||
}
|
||||
|
||||
void loadFileIntoTransport (const File& audioFile)
|
||||
{
|
||||
// unload the previous file source and delete it..
|
||||
transportSource.stop();
|
||||
transportSource.setSource (nullptr);
|
||||
currentAudioFileSource = nullptr;
|
||||
|
||||
AudioFormatReader* reader = formatManager.createReaderFor (audioFile);
|
||||
|
||||
if (reader != nullptr)
|
||||
{
|
||||
currentAudioFileSource = new AudioFormatReaderSource (reader, true);
|
||||
|
||||
// ..and plug it into our transport source
|
||||
transportSource.setSource (currentAudioFileSource,
|
||||
32768, // tells it to buffer this many samples ahead
|
||||
&thread, // this is the background thread to use for reading-ahead
|
||||
reader->sampleRate); // allows for sample rate correction
|
||||
}
|
||||
}
|
||||
|
||||
void selectionChanged() override
|
||||
{
|
||||
showFile (fileTreeComp.getSelectedFile());
|
||||
}
|
||||
|
||||
void fileClicked (const File&, const MouseEvent&) override {}
|
||||
void fileDoubleClicked (const File&) override {}
|
||||
void browserRootChanged (const File&) override {}
|
||||
|
||||
void sliderValueChanged (Slider* sliderThatWasMoved) override
|
||||
{
|
||||
if (sliderThatWasMoved == &zoomSlider)
|
||||
thumbnail->setZoomFactor (zoomSlider.getValue());
|
||||
}
|
||||
|
||||
void buttonClicked (Button* buttonThatWasClicked) override
|
||||
{
|
||||
if (buttonThatWasClicked == &startStopButton)
|
||||
{
|
||||
if (transportSource.isPlaying())
|
||||
{
|
||||
transportSource.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
transportSource.setPosition (0);
|
||||
transportSource.start();
|
||||
}
|
||||
}
|
||||
else if (buttonThatWasClicked == &followTransportButton)
|
||||
{
|
||||
thumbnail->setFollowsTransport (followTransportButton.getToggleState());
|
||||
}
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
if (source == thumbnail)
|
||||
showFile (thumbnail->getLastDroppedFile());
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPlaybackDemo);
|
||||
};
|
||||
|
||||
|
||||
// This static object will register this demo type in a global list of demos..
|
||||
static JuceDemoType<AudioPlaybackDemo> demo ("31 Audio: File Playback");
|
||||
Reference in New Issue
Block a user