2006-01-16 22:02:54 +01:00
|
|
|
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
2010-12-08 04:36:10 +01:00
|
|
|
// Copyright (c) 2009-2010, Niels Martin Hansen
|
2006-01-16 22:02:54 +01:00
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
|
|
// may be used to endorse or promote products derived from this software
|
|
|
|
// without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
2006-01-16 22:02:54 +01:00
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// $Id$
|
|
|
|
|
|
|
|
/// @file audio_display.cpp
|
|
|
|
/// @brief Display audio in the main UI
|
|
|
|
/// @ingroup audio_ui
|
2010-12-08 04:36:10 +01:00
|
|
|
///
|
|
|
|
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
///////////
|
|
|
|
// Headers
|
2009-01-04 07:31:48 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2009-09-10 15:06:40 +02:00
|
|
|
#ifndef AGI_PRE
|
2010-12-08 04:36:10 +01:00
|
|
|
#include <algorithm>
|
2009-09-10 15:06:40 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
#include <wx/dcbuffer.h>
|
|
|
|
#include <wx/dcclient.h>
|
|
|
|
#include <wx/mousestate.h>
|
2009-09-10 15:06:40 +02:00
|
|
|
#endif
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "ass_time.h"
|
|
|
|
#include "audio_colorscheme.h"
|
|
|
|
#include "audio_controller.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "audio_display.h"
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "block_cache.h"
|
|
|
|
#include "audio_renderer.h"
|
|
|
|
#include "audio_renderer_spectrum.h"
|
|
|
|
#include "audio_renderer_waveform.h"
|
|
|
|
#include "selection_controller.h"
|
|
|
|
#include "audio_timing.h"
|
|
|
|
#include "include/aegisub/audio_provider.h"
|
2010-08-02 08:31:38 +02:00
|
|
|
#include "include/aegisub/audio_player.h"
|
2010-05-21 03:13:36 +02:00
|
|
|
#include "main.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "utils.h"
|
|
|
|
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
#undef min
|
|
|
|
#undef max
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
class AudioDisplayScrollbar : public AudioDisplayInteractionObject {
|
|
|
|
static const int height = 10;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
wxRect bounds;
|
|
|
|
wxRect thumb;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
bool dragging; // user is dragging with the primary mouse button
|
2008-11-14 02:21:17 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int data_length; // total amount of data in control
|
|
|
|
int page_length; // amount of data in one page
|
|
|
|
int position; // first item displayed
|
2008-11-14 02:21:17 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int sel_start; // first data item in selection
|
|
|
|
int sel_length; // number of data items in selection
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
AudioDisplay *display;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Recalculate thumb bounds from position and length data
|
|
|
|
void RecalculateThumb()
|
|
|
|
{
|
|
|
|
thumb.width = std::max((height+1)/2, bounds.width * page_length / data_length);
|
|
|
|
thumb.height = height;
|
|
|
|
thumb.x = bounds.width * position / data_length;
|
|
|
|
thumb.y = bounds.y;
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
public:
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
AudioDisplayScrollbar(AudioDisplay *_display)
|
|
|
|
: dragging(false)
|
|
|
|
, data_length(1)
|
|
|
|
, page_length(1)
|
|
|
|
, position(0)
|
|
|
|
, sel_start(-1)
|
|
|
|
, sel_length(0)
|
|
|
|
, display(_display)
|
|
|
|
{
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
virtual ~AudioDisplayScrollbar()
|
|
|
|
{
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// The audio display has changed size
|
|
|
|
void SetDisplaySize(const wxSize &display_size)
|
|
|
|
{
|
|
|
|
bounds.x = 0;
|
|
|
|
bounds.y = display_size.y - height;
|
|
|
|
bounds.width = display_size.x;
|
|
|
|
bounds.height = height;
|
|
|
|
page_length = display_size.x;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
RecalculateThumb();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2007-04-07 04:39:18 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
const wxRect & GetBounds() const
|
|
|
|
{
|
|
|
|
return bounds;
|
2007-04-07 04:39:18 +02:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int GetPosition() const
|
|
|
|
{
|
|
|
|
return position;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int SetPosition(int new_position)
|
|
|
|
{
|
|
|
|
// These two conditionals can't be swapped, otherwise the position can become
|
|
|
|
// negative if the entire data is shorter than one page.
|
|
|
|
if (new_position + page_length >= data_length)
|
|
|
|
new_position = data_length - page_length - 1;
|
|
|
|
if (new_position < 0)
|
|
|
|
new_position = 0;
|
|
|
|
|
|
|
|
// This check is required to avoid mutual recursion with the display
|
|
|
|
if (new_position != position)
|
|
|
|
{
|
|
|
|
position = new_position;
|
|
|
|
RecalculateThumb();
|
|
|
|
display->ScrollPixelToLeft(position);
|
2008-01-14 02:18:24 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
return position;
|
2008-01-14 02:18:24 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void SetSelection(int new_start, int new_length)
|
|
|
|
{
|
|
|
|
sel_start = new_start;
|
|
|
|
sel_length = new_length;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void ChangeLengths(int new_data_length, int new_page_length)
|
|
|
|
{
|
|
|
|
data_length = new_data_length;
|
|
|
|
page_length = new_page_length;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
RecalculateThumb();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
bool OnMouseEvent(wxMouseEvent &event)
|
|
|
|
{
|
|
|
|
if (event.LeftIsDown())
|
|
|
|
{
|
|
|
|
const int thumb_left = event.GetPosition().x - thumb.width/2;
|
|
|
|
const int data_length_less_page = data_length - page_length;
|
|
|
|
const int shaft_length_less_thumb = bounds.width - thumb.width;
|
|
|
|
|
|
|
|
SetPosition(data_length_less_page * thumb_left / shaft_length_less_thumb);
|
|
|
|
|
|
|
|
dragging = true;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
else if (event.LeftUp())
|
|
|
|
{
|
|
|
|
dragging = false;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2007-04-07 04:39:18 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
return dragging;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void Paint(wxDC &dc, bool has_focus)
|
|
|
|
{
|
|
|
|
wxColour light(89, 145, 220);
|
|
|
|
wxColour dark(8, 4, 13);
|
|
|
|
wxColour sel(65, 34, 103);
|
2007-04-07 04:39:18 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (has_focus)
|
|
|
|
{
|
|
|
|
light.Set(205, 240, 226);
|
|
|
|
sel.Set(82, 107, 213);
|
|
|
|
}
|
2007-04-13 03:29:05 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
dc.SetPen(wxPen(light));
|
|
|
|
dc.SetBrush(wxBrush(dark));
|
|
|
|
dc.DrawRectangle(bounds);
|
2007-04-13 03:29:05 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (sel_length > 0 && sel_start >= 0)
|
|
|
|
{
|
|
|
|
wxRect r;
|
|
|
|
r.x = sel_start * bounds.width / data_length;
|
|
|
|
r.y = bounds.y;
|
|
|
|
r.width = sel_length * bounds.width / data_length;
|
|
|
|
r.height = bounds.height;
|
2007-04-13 03:29:05 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
dc.SetPen(wxPen(sel));
|
|
|
|
dc.SetBrush(wxBrush(sel));
|
|
|
|
dc.DrawRectangle(r);
|
2007-04-13 03:29:05 +02:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
dc.SetPen(wxPen(light));
|
|
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(bounds);
|
|
|
|
|
|
|
|
dc.SetPen(wxPen(light));
|
|
|
|
dc.SetBrush(wxBrush(light));
|
|
|
|
dc.DrawRectangle(thumb);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AudioDisplayTimeline : public AudioDisplayInteractionObject {
|
|
|
|
int64_t num_samples;
|
|
|
|
int samplerate;
|
|
|
|
int samples_per_pixel;
|
|
|
|
int pixel_left;
|
|
|
|
|
|
|
|
wxRect bounds;
|
|
|
|
|
|
|
|
wxPoint drag_lastpos;
|
|
|
|
bool dragging;
|
|
|
|
|
|
|
|
enum Scale {
|
|
|
|
Sc_Millisecond,
|
|
|
|
Sc_Centisecond,
|
|
|
|
Sc_Decisecond,
|
|
|
|
Sc_Second,
|
|
|
|
Sc_Decasecond,
|
|
|
|
Sc_Minute,
|
|
|
|
Sc_Decaminute,
|
|
|
|
Sc_Hour,
|
|
|
|
Sc_Decahour, // If anyone needs this they should reconsider their project
|
|
|
|
Sc_MAX = Sc_Decahour
|
|
|
|
};
|
|
|
|
Scale scale_minor;
|
|
|
|
int scale_major_modulo; // If minor_scale_mark_index % scale_major_modulo == 0 the mark is a major mark
|
|
|
|
double scale_minor_divisor; // Absolute scale-mark index multiplied by this number gives sample index for scale mark
|
|
|
|
|
|
|
|
AudioDisplay *display;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
AudioDisplayTimeline(AudioDisplay *_display)
|
|
|
|
: num_samples(0)
|
|
|
|
, samplerate(44100)
|
|
|
|
, samples_per_pixel(1)
|
|
|
|
, pixel_left(0)
|
|
|
|
, dragging(false)
|
|
|
|
, display(_display)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~AudioDisplayTimeline()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetHeight() const
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
display->GetTextExtent(_T("0123456789:."), &width, &height);
|
|
|
|
return height + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetDisplaySize(const wxSize &display_size)
|
|
|
|
{
|
|
|
|
// The size is without anything that goes below the timeline (like scrollbar)
|
|
|
|
bounds.width = display_size.x;
|
|
|
|
bounds.height = GetHeight();
|
|
|
|
bounds.x = 0;
|
|
|
|
bounds.y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const wxRect & GetBounds() const
|
|
|
|
{
|
|
|
|
return bounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChangeAudio(int64_t new_length, int new_samplerate)
|
|
|
|
{
|
|
|
|
num_samples = new_length;
|
|
|
|
samplerate = new_samplerate;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChangeZoom(int new_pixel_samples)
|
|
|
|
{
|
|
|
|
samples_per_pixel = new_pixel_samples;
|
|
|
|
|
|
|
|
// Pixels per second
|
|
|
|
double px_sec = (double)samplerate / (double)samples_per_pixel;
|
|
|
|
|
|
|
|
if (px_sec > 3000) {
|
|
|
|
scale_minor = Sc_Millisecond;
|
|
|
|
scale_minor_divisor = (double)samplerate / 1000;
|
|
|
|
scale_major_modulo = 10;
|
|
|
|
} else if (px_sec > 300) {
|
|
|
|
scale_minor = Sc_Centisecond;
|
|
|
|
scale_minor_divisor = (double)samplerate / 100;
|
|
|
|
scale_major_modulo = 10;
|
|
|
|
} else if (px_sec > 30) {
|
|
|
|
scale_minor = Sc_Decisecond;
|
|
|
|
scale_minor_divisor = (double)samplerate / 10;
|
|
|
|
scale_major_modulo = 10;
|
|
|
|
} else if (px_sec > 3) {
|
|
|
|
scale_minor = Sc_Second;
|
|
|
|
scale_minor_divisor = (double)samplerate;
|
|
|
|
scale_major_modulo = 10;
|
|
|
|
} else if (px_sec > 1.0/3.0) {
|
|
|
|
scale_minor = Sc_Decasecond;
|
|
|
|
scale_minor_divisor = (double)samplerate * 10;
|
|
|
|
scale_major_modulo = 6;
|
|
|
|
} else if (px_sec > 1.0/9.0) {
|
|
|
|
scale_minor = Sc_Minute;
|
|
|
|
scale_minor_divisor = (double)samplerate * 60;
|
|
|
|
scale_major_modulo = 10;
|
|
|
|
} else if (px_sec > 1.0/90.0) {
|
|
|
|
scale_minor = Sc_Decaminute;
|
|
|
|
scale_minor_divisor = (double)samplerate * 600;
|
|
|
|
scale_major_modulo = 6;
|
|
|
|
} else {
|
|
|
|
scale_minor = Sc_Hour;
|
|
|
|
scale_minor_divisor = (double)samplerate * 3600;
|
|
|
|
scale_major_modulo = 10;
|
2007-04-07 04:39:18 +02:00
|
|
|
}
|
|
|
|
}
|
2006-03-06 01:45:24 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void SetPosition(int new_pixel_left)
|
|
|
|
{
|
|
|
|
if (new_pixel_left < 0)
|
|
|
|
new_pixel_left = 0;
|
2007-04-07 04:39:18 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (new_pixel_left != pixel_left)
|
|
|
|
{
|
|
|
|
pixel_left = new_pixel_left;
|
|
|
|
display->ScrollPixelToLeft(pixel_left);
|
2006-03-06 01:45:24 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
bool OnMouseEvent(wxMouseEvent &event)
|
|
|
|
{
|
|
|
|
if (event.LeftDown())
|
|
|
|
{
|
|
|
|
drag_lastpos = event.GetPosition();
|
|
|
|
dragging = true;
|
|
|
|
}
|
|
|
|
else if (event.LeftIsDown())
|
|
|
|
{
|
|
|
|
SetPosition(pixel_left - event.GetPosition().x + drag_lastpos.x);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
drag_lastpos = event.GetPosition();
|
|
|
|
dragging = true;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
else if (event.LeftUp())
|
|
|
|
{
|
|
|
|
dragging = false;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
return dragging;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void Paint(wxDC &dc)
|
|
|
|
{
|
|
|
|
wxColour light(89, 145, 220);
|
|
|
|
wxColour dark(8, 4, 13);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int bottom = bounds.y + bounds.height;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Background
|
|
|
|
dc.SetPen(wxPen(dark));
|
|
|
|
dc.SetBrush(wxBrush(dark));
|
|
|
|
dc.DrawRectangle(bounds);
|
2007-01-07 05:44:11 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Top line
|
|
|
|
dc.SetPen(wxPen(light));
|
|
|
|
dc.DrawLine(bounds.x, bottom-1, bounds.x+bounds.width, bottom-1);
|
2008-01-22 21:36:07 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Prepare for writing text
|
|
|
|
dc.SetTextBackground(dark);
|
|
|
|
dc.SetTextForeground(light);
|
2006-08-27 21:54:51 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Figure out the first scale mark to show
|
|
|
|
int64_t sample_left = pixel_left * samples_per_pixel;
|
|
|
|
int next_scale_mark = (int)(sample_left / scale_minor_divisor);
|
|
|
|
if (next_scale_mark * scale_minor_divisor < sample_left)
|
|
|
|
next_scale_mark += 1;
|
|
|
|
assert(next_scale_mark * scale_minor_divisor >= sample_left);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Draw scale marks
|
|
|
|
int next_scale_mark_pos;
|
|
|
|
int last_text_right = -1;
|
|
|
|
int last_hour = -1, last_minute = -1;
|
|
|
|
if (num_samples / samplerate < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
|
|
|
|
do {
|
|
|
|
next_scale_mark_pos = (int)(next_scale_mark * scale_minor_divisor / samples_per_pixel) - pixel_left;
|
|
|
|
bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (mark_is_major)
|
|
|
|
dc.DrawLine(next_scale_mark_pos, bottom-6, next_scale_mark_pos, bottom-1);
|
|
|
|
else
|
|
|
|
dc.DrawLine(next_scale_mark_pos, bottom-4, next_scale_mark_pos, bottom-1);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Print time labels on major scale marks
|
|
|
|
if (mark_is_major && next_scale_mark_pos > last_text_right)
|
|
|
|
{
|
|
|
|
double mark_time = next_scale_mark * scale_minor_divisor / samplerate;
|
|
|
|
int mark_hour = (int)(mark_time / 3600);
|
|
|
|
int mark_minute = (int)(mark_time / 60) % 60;
|
|
|
|
double mark_second = mark_time - mark_hour*3600 - mark_minute*60;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
wxString time_string;
|
|
|
|
bool changed_hour = mark_hour != last_hour;
|
|
|
|
bool changed_minute = mark_minute != last_minute;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (changed_hour)
|
|
|
|
{
|
|
|
|
time_string = wxString::Format(_T("%d:%02d:"), mark_hour, mark_minute);
|
|
|
|
last_hour = mark_hour;
|
|
|
|
last_minute = mark_minute;
|
|
|
|
}
|
|
|
|
else if (changed_minute)
|
|
|
|
{
|
|
|
|
time_string = wxString::Format(_T("%d:"), mark_minute);
|
|
|
|
last_minute = mark_minute;
|
|
|
|
}
|
|
|
|
if (scale_minor >= Sc_Decisecond)
|
|
|
|
time_string += wxString::Format(_T("%02d"), (int)mark_second);
|
|
|
|
else if (scale_minor == Sc_Centisecond)
|
|
|
|
time_string += wxString::Format(_T("%02.1f"), mark_second);
|
|
|
|
else
|
|
|
|
time_string += wxString::Format(_T("%02.2f"), mark_second);
|
|
|
|
|
|
|
|
int tw, th;
|
|
|
|
dc.GetTextExtent(time_string, &tw, &th);
|
|
|
|
last_text_right = next_scale_mark_pos + tw;
|
|
|
|
|
|
|
|
dc.DrawText(time_string, next_scale_mark_pos, 0);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
next_scale_mark += 1;
|
|
|
|
|
|
|
|
} while (next_scale_mark_pos < bounds.width);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AudioMarkerInteractionObject : public AudioDisplayInteractionObject {
|
|
|
|
// Object-pair being intracted with
|
|
|
|
AudioMarker *marker;
|
|
|
|
AudioTimingController *timing_controller;
|
|
|
|
// Audio display drag is happening on
|
|
|
|
AudioDisplay *display;
|
|
|
|
// Audio controller managing it all
|
|
|
|
AudioController *controller;
|
|
|
|
// Mouse button used to initiate the drag
|
|
|
|
wxMouseButton button_used;
|
|
|
|
// Default to snapping to snappable markers
|
|
|
|
bool default_snap;
|
|
|
|
// Range in pixels to snap at
|
|
|
|
int snap_range;
|
|
|
|
|
|
|
|
public:
|
|
|
|
AudioMarkerInteractionObject(AudioMarker *marker, AudioTimingController *timing_controller, AudioDisplay *display, AudioController *controller, wxMouseButton button_used)
|
|
|
|
: marker(marker)
|
|
|
|
, timing_controller(timing_controller)
|
|
|
|
, display(display)
|
|
|
|
, controller(controller)
|
|
|
|
, button_used(button_used)
|
|
|
|
{
|
|
|
|
/// @todo Make these configurable
|
|
|
|
snap_range = 5;
|
|
|
|
default_snap = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~AudioMarkerInteractionObject()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool OnMouseEvent(wxMouseEvent &event)
|
|
|
|
{
|
|
|
|
if (event.Dragging())
|
|
|
|
{
|
|
|
|
int64_t sample_pos = display->SamplesFromRelativeX(event.GetPosition().x);
|
|
|
|
|
|
|
|
if (marker->CanSnap() && (default_snap != event.ShiftDown()))
|
|
|
|
{
|
|
|
|
AudioController::SampleRange snap_sample_range(
|
|
|
|
display->SamplesFromRelativeX(event.GetPosition().x - snap_range),
|
|
|
|
display->SamplesFromRelativeX(event.GetPosition().x + snap_range));
|
|
|
|
const AudioMarker *snap_marker = 0;
|
|
|
|
AudioMarkerVector potential_snaps;
|
|
|
|
controller->GetMarkers(snap_sample_range, potential_snaps);
|
|
|
|
for (AudioMarkerVector::iterator mi = potential_snaps.begin(); mi != potential_snaps.end(); ++mi)
|
|
|
|
{
|
|
|
|
if ((*mi)->CanSnap())
|
|
|
|
{
|
|
|
|
if (!snap_marker)
|
|
|
|
snap_marker = *mi;
|
|
|
|
else if (tabs((*mi)->GetPosition() - sample_pos) < tabs(snap_marker->GetPosition() - sample_pos))
|
|
|
|
snap_marker = *mi;
|
|
|
|
}
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (snap_marker)
|
|
|
|
sample_pos = snap_marker->GetPosition();
|
|
|
|
}
|
2007-01-06 07:14:35 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
timing_controller->OnMarkerDrag(marker, sample_pos);
|
2007-04-10 02:51:03 +02:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
// We lose the marker drag if the button used to initiate it goes up
|
|
|
|
return !event.ButtonUp(button_used);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
};
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *_controller)
|
|
|
|
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS|wxBORDER_SIMPLE)
|
|
|
|
, provider(0)
|
|
|
|
, controller(_controller)
|
|
|
|
, dragged_object(0)
|
|
|
|
, old_selection(0, 0)
|
|
|
|
{
|
|
|
|
scrollbar = new AudioDisplayScrollbar(this);
|
|
|
|
timeline = new AudioDisplayTimeline(this);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_renderer = new AudioRenderer;
|
|
|
|
audio_spectrum_renderer = new AudioSpectrumRenderer;
|
|
|
|
audio_waveform_renderer = new AudioWaveformRenderer;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
scroll_left = 0;
|
|
|
|
pixel_audio_width = 0;
|
|
|
|
scale_amplitude = 1.0;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
track_cursor_pos = -1;
|
2006-02-02 19:17:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
controller->AddAudioListener(this);
|
|
|
|
controller->AddTimingListener(this);
|
2006-02-25 08:41:18 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_renderer->SetAmplitudeScale(scale_amplitude);
|
|
|
|
SetZoomLevel(0);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
ReloadRenderingSettings();
|
2006-02-02 19:17:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
SetMinClientSize(wxSize(-1, 70));
|
|
|
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
|
|
SetThemeEnabled(false);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
AudioDisplay::~AudioDisplay()
|
|
|
|
{
|
|
|
|
delete audio_renderer;
|
|
|
|
delete audio_spectrum_renderer;
|
|
|
|
delete audio_waveform_renderer;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
delete timeline;
|
|
|
|
delete scrollbar;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
controller->RemoveAudioListener(this);
|
|
|
|
controller->RemoveTimingListener(this);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2007-04-10 04:55:23 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::ScrollBy(int pixel_amount)
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(scroll_left + pixel_amount);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::ScrollPixelToLeft(int pixel_position)
|
|
|
|
{
|
|
|
|
const int client_width = GetClientRect().GetWidth();
|
|
|
|
|
|
|
|
if (pixel_position + client_width >= pixel_audio_width)
|
|
|
|
pixel_position = pixel_audio_width - client_width;
|
|
|
|
if (pixel_position < 0)
|
|
|
|
pixel_position = 0;
|
|
|
|
|
|
|
|
// This check is required to avoid needless redraws, but more importantly to
|
|
|
|
// avoid mutual recursion with the scrollbar and timeline.
|
|
|
|
if (pixel_position != scroll_left)
|
|
|
|
{
|
|
|
|
scroll_left = pixel_position;
|
|
|
|
scrollbar->SetPosition(scroll_left);
|
|
|
|
timeline->SetPosition(scroll_left);
|
|
|
|
Refresh();
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::ScrollPixelToCenter(int pixel_position)
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
void AudioDisplay::ScrollSampleToLeft(int64_t sample_position)
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(AbsoluteXFromSamples(sample_position));
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
void AudioDisplay::ScrollSampleToCenter(int64_t sample_position)
|
|
|
|
{
|
|
|
|
ScrollPixelToCenter(AbsoluteXFromSamples(sample_position));
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2007-03-18 02:20:25 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::ScrollSampleRangeInView(const AudioController::SampleRange &range)
|
|
|
|
{
|
|
|
|
int client_width = GetClientRect().GetWidth();
|
|
|
|
int range_begin = AbsoluteXFromSamples(range.begin());
|
|
|
|
int range_end = AbsoluteXFromSamples(range.end());
|
|
|
|
int range_len = range_end - range_begin;
|
|
|
|
|
|
|
|
// Is everything already in view?
|
|
|
|
if (range_begin >= scroll_left && range_end <= scroll_left+client_width)
|
2010-01-06 09:02:15 +01:00
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// For the rest of the calculation, remove 5 % from each side of the client area.
|
|
|
|
// The leftadjust is the amount to subtract from the final scroll_left value.
|
|
|
|
int leftadjust = client_width / 20;
|
|
|
|
client_width = client_width * 9 / 10;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// The entire range can fit inside the view, center it
|
|
|
|
if (range_len < client_width)
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(range_begin - (client_width-range_len)/2 - leftadjust);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Range doesn't fit in view and we're viewing a middle part of it, just leave it alone
|
|
|
|
else if (range_begin < scroll_left+leftadjust && range_end > scroll_left+leftadjust+client_width)
|
|
|
|
{
|
|
|
|
// nothing
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Right edge is in view, scroll it as far to the right as possible
|
|
|
|
else if (range_end >= scroll_left+leftadjust && range_end < scroll_left+leftadjust+client_width)
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(range_end - client_width - leftadjust);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Nothing is in view or the left edge is in view, scroll left edge as far to the left as possible
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScrollPixelToLeft(range_begin - leftadjust);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::SetZoomLevel(int new_zoom_level)
|
|
|
|
{
|
|
|
|
zoom_level = new_zoom_level;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (!provider)
|
|
|
|
{
|
|
|
|
pixel_samples = 1;
|
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
const int samples_per_second = provider ? provider->GetSampleRate() : 48000;
|
|
|
|
const int base_pixels_per_second = 50; /// @todo Make this customisable
|
|
|
|
const int base_samples_per_pixel = samples_per_second / base_pixels_per_second;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
const int factor = GetZoomLevelFactor(zoom_level);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
const int new_samples_per_pixel = std::max(1, 100 * base_samples_per_pixel / factor);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (pixel_samples != new_samples_per_pixel)
|
|
|
|
{
|
|
|
|
pixel_samples = new_samples_per_pixel;
|
|
|
|
audio_renderer->SetSamplesPerPixel(pixel_samples);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (provider)
|
|
|
|
pixel_audio_width = provider->GetNumSamples() / pixel_samples + 1;
|
|
|
|
else
|
|
|
|
pixel_audio_width = 1;
|
2006-02-20 23:32:20 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
scrollbar->ChangeLengths(pixel_audio_width, GetClientSize().GetWidth());
|
|
|
|
timeline->ChangeZoom(pixel_samples);
|
2006-02-20 23:32:20 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
Refresh();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int AudioDisplay::GetZoomLevel() const
|
|
|
|
{
|
|
|
|
return zoom_level;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
wxString AudioDisplay::GetZoomLevelDescription(int level) const
|
|
|
|
{
|
|
|
|
const int factor = GetZoomLevelFactor(level);
|
|
|
|
const int base_pixels_per_second = 50; /// @todo Make this customisable along with the above
|
|
|
|
const int second_pixels = 100 * base_pixels_per_second / factor;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
return wxString::Format(_("%d%%, %d pixel/second"), factor, second_pixels);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2006-02-24 02:16:27 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
int AudioDisplay::GetZoomLevelFactor(int level)
|
|
|
|
{
|
|
|
|
int factor = 100;
|
|
|
|
|
|
|
|
if (level > 0)
|
|
|
|
{
|
|
|
|
factor += 25 * level;
|
|
|
|
}
|
|
|
|
else if (level < 0)
|
|
|
|
{
|
|
|
|
if (level >= -5)
|
|
|
|
factor += 10 * level;
|
|
|
|
else if (level >= -11)
|
|
|
|
factor = 50 + (level+5) * 5;
|
|
|
|
else
|
|
|
|
factor = 20 + level + 11;
|
|
|
|
if (factor <= 0)
|
|
|
|
factor = 1;
|
2009-06-17 01:44:06 +02:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
return factor;
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::SetAmplitudeScale(float scale)
|
|
|
|
{
|
|
|
|
audio_renderer->SetAmplitudeScale(scale);
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float AudioDisplay::GetAmplitudeScale() const
|
|
|
|
{
|
|
|
|
return audio_renderer->GetAmplitudeScale();
|
|
|
|
}
|
2006-07-04 08:13:54 +02:00
|
|
|
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::ReloadRenderingSettings()
|
|
|
|
{
|
|
|
|
int64_t spectrum_quality = OPT_GET("Audio/Renderer/Spectrum/Quality")->GetInt();
|
|
|
|
#ifdef WITH_FFTW
|
|
|
|
// FFTW is so fast we can afford to upgrade quality by two levels
|
|
|
|
spectrum_quality += 2;
|
2007-09-21 23:00:41 +02:00
|
|
|
#endif
|
2010-12-08 04:36:10 +01:00
|
|
|
if (spectrum_quality < 0) spectrum_quality = 0;
|
|
|
|
if (spectrum_quality > 5) spectrum_quality = 5;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Quality indexes: 0 1 2 3 4 5
|
|
|
|
int spectrum_width[] = {8, 9, 9, 9, 10, 11};
|
|
|
|
int spectrum_distance[] = {8, 8, 7, 6, 6, 5};
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_spectrum_renderer->SetResolution(
|
|
|
|
spectrum_width[spectrum_quality],
|
|
|
|
spectrum_distance[spectrum_quality]);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (OPT_GET("Audio/Spectrum")->GetBool())
|
|
|
|
audio_renderer->SetRenderer(audio_spectrum_renderer);
|
|
|
|
else
|
|
|
|
audio_renderer->SetRenderer(audio_waveform_renderer);
|
2006-03-07 01:12:50 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_renderer->Invalidate();
|
2006-03-06 01:45:24 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
Refresh();
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
BEGIN_EVENT_TABLE(AudioDisplay, wxWindow)
|
|
|
|
EVT_MOUSE_EVENTS(AudioDisplay::OnMouseEvent)
|
|
|
|
EVT_PAINT(AudioDisplay::OnPaint)
|
|
|
|
EVT_SIZE(AudioDisplay::OnSize)
|
|
|
|
EVT_SET_FOCUS(AudioDisplay::OnFocus)
|
|
|
|
EVT_KILL_FOCUS(AudioDisplay::OnFocus)
|
|
|
|
END_EVENT_TABLE()
|
2007-01-06 00:43:24 +01:00
|
|
|
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
struct RedrawSubregion {
|
|
|
|
int x1, x2;
|
|
|
|
bool selected;
|
|
|
|
RedrawSubregion(int x1, int x2, bool selected) : x1(x1), x2(x2), selected(selected) { }
|
|
|
|
};
|
2007-01-15 04:50:49 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnPaint(wxPaintEvent& event)
|
|
|
|
{
|
|
|
|
wxAutoBufferedPaintDC dc(this);
|
|
|
|
|
|
|
|
if (!provider)
|
|
|
|
{
|
|
|
|
dc.SetBackground(*wxBLACK_BRUSH);
|
|
|
|
dc.Clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int client_width, client_height;
|
|
|
|
GetClientSize(&client_width, &client_height);
|
|
|
|
|
|
|
|
wxRect audio_bounds(0, audio_top, client_width, audio_height);
|
|
|
|
const wxRect &scrollbar_bounds = scrollbar->GetBounds();
|
|
|
|
const wxRect &timeline_bounds = timeline->GetBounds();
|
|
|
|
bool redraw_scrollbar = false;
|
|
|
|
bool redraw_timeline = false;
|
|
|
|
|
|
|
|
/// @todo Get rendering style ranges from timing controller instead
|
|
|
|
AudioController::SampleRange sel_samples(controller->GetPrimaryPlaybackRange());
|
|
|
|
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
|
|
|
|
int selection_end = AbsoluteXFromSamples(sel_samples.end());
|
|
|
|
|
|
|
|
wxRegionIterator region(GetUpdateRegion());
|
|
|
|
wxPoint client_org = GetClientAreaOrigin();
|
|
|
|
while (region)
|
|
|
|
{
|
|
|
|
wxRect updrect = region.GetRect();
|
|
|
|
// Work around wxMac issue, client border offsets update rectangles but does
|
|
|
|
// not affect drawing coordinates.
|
|
|
|
updrect.x += client_org.x; updrect.y += client_org.y;
|
|
|
|
|
|
|
|
redraw_scrollbar |= scrollbar_bounds.Intersects(updrect);
|
|
|
|
redraw_timeline |= timeline_bounds.Intersects(updrect);
|
|
|
|
|
|
|
|
if (audio_bounds.Intersects(updrect))
|
|
|
|
{
|
|
|
|
int p1, p2, p3, p4;
|
|
|
|
// p1 -> p2 = before selection
|
|
|
|
// p2 -> p3 = in selection
|
|
|
|
// p3 -> p4 = after selection
|
|
|
|
p1 = scroll_left + updrect.x;
|
|
|
|
p2 = selection_start;
|
|
|
|
p3 = selection_end;
|
|
|
|
p4 = p1 + updrect.width;
|
|
|
|
|
|
|
|
std::vector<RedrawSubregion> subregions;
|
|
|
|
|
|
|
|
if (p1 < p2)
|
|
|
|
subregions.push_back(RedrawSubregion(p1, std::min(p2, p4), false));
|
|
|
|
if (p4 > p2 && p1 < p3)
|
|
|
|
subregions.push_back(RedrawSubregion(std::max(p1, p2), std::min(p3, p4), true));
|
|
|
|
if (p4 > p3)
|
|
|
|
subregions.push_back(RedrawSubregion(std::max(p1, p3), p4, false));
|
|
|
|
|
|
|
|
int x = updrect.x;
|
|
|
|
for (std::vector<RedrawSubregion>::iterator sr = subregions.begin(); sr != subregions.end(); ++sr)
|
|
|
|
{
|
|
|
|
audio_renderer->Render(dc, wxPoint(x, audio_top), sr->x1, sr->x2 - sr->x1, sr->selected);
|
|
|
|
x += sr->x2 - sr->x1;
|
2007-01-06 00:43:24 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Draw markers on top of it all
|
|
|
|
AudioMarkerVector markers;
|
|
|
|
const int foot_size = 6;
|
|
|
|
AudioController::SampleRange updrectsamples(
|
|
|
|
SamplesFromRelativeX(updrect.x - foot_size),
|
|
|
|
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
|
|
|
controller->GetMarkers(updrectsamples, markers);
|
|
|
|
if (controller->GetTimingController())
|
|
|
|
controller->GetTimingController()->GetMarkers(updrectsamples, markers);
|
|
|
|
wxDCPenChanger pen_retainer(dc, wxPen());
|
|
|
|
wxDCBrushChanger brush_retainer(dc, wxBrush());
|
|
|
|
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
|
|
|
{
|
|
|
|
const AudioMarker *marker = *marker_i;
|
|
|
|
dc.SetPen(marker->GetStyle());
|
|
|
|
int marker_x = RelativeXFromSamples(marker->GetPosition());
|
|
|
|
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
|
|
|
|
dc.SetBrush(wxBrush(marker->GetStyle().GetColour()));
|
|
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
|
|
if (marker->GetFeet() & AudioMarker::Feet_Left)
|
|
|
|
{
|
|
|
|
wxPoint foot_top[3] = { wxPoint(-foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
|
|
|
wxPoint foot_bot[3] = { wxPoint(-foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
|
|
|
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
|
|
|
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
if (marker->GetFeet() & AudioMarker::Feet_Right)
|
|
|
|
{
|
|
|
|
wxPoint foot_top[3] = { wxPoint(foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
|
|
|
wxPoint foot_bot[3] = { wxPoint(foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
|
|
|
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
|
|
|
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
2007-01-06 00:43:24 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
region++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (track_cursor_pos >= 0)
|
|
|
|
{
|
|
|
|
wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
|
|
|
|
dc.DrawLine(track_cursor_pos-scroll_left, audio_top, track_cursor_pos-scroll_left, audio_top+audio_height);
|
|
|
|
|
|
|
|
if (!track_cursor_label.IsEmpty())
|
|
|
|
{
|
|
|
|
wxDCFontChanger fc(dc);
|
|
|
|
wxFont font = dc.GetFont();
|
|
|
|
font.SetWeight(wxFONTWEIGHT_BOLD);
|
|
|
|
dc.SetFont(font);
|
|
|
|
|
|
|
|
wxSize label_size(dc.GetTextExtent(track_cursor_label));
|
|
|
|
wxPoint label_pos(track_cursor_pos - scroll_left - label_size.x/2, audio_top + 2);
|
|
|
|
if (label_pos.x < 2) label_pos.x = 2;
|
|
|
|
if (label_pos.x + label_size.x >= client_width - 2) label_pos.x = client_width - label_size.x - 2;
|
|
|
|
|
|
|
|
int old_bg_mode = dc.GetBackgroundMode();
|
|
|
|
dc.SetBackgroundMode(wxTRANSPARENT);
|
|
|
|
dc.SetTextForeground(wxColour(64, 64, 64));
|
|
|
|
dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y+1);
|
|
|
|
dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y-1);
|
|
|
|
dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y+1);
|
|
|
|
dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y-1);
|
|
|
|
dc.SetTextForeground(*wxWHITE);
|
|
|
|
dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
|
|
|
|
dc.SetBackgroundMode(old_bg_mode);
|
|
|
|
|
|
|
|
label_pos.x -= 2; label_pos.y -= 2;
|
|
|
|
label_size.IncBy(4, 4);
|
|
|
|
// If the rendered text changes size we have to draw it an extra time to make sure the entire thing was drawn
|
|
|
|
bool need_extra_redraw = track_cursor_label_rect.GetSize() != label_size;
|
|
|
|
track_cursor_label_rect.SetPosition(label_pos);
|
|
|
|
track_cursor_label_rect.SetSize(label_size);
|
|
|
|
if (need_extra_redraw)
|
|
|
|
RefreshRect(track_cursor_label_rect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw_scrollbar)
|
|
|
|
scrollbar->Paint(dc, HasFocus());
|
|
|
|
if (redraw_timeline)
|
|
|
|
timeline->Paint(dc);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::SetDraggedObject(AudioDisplayInteractionObject *new_obj)
|
|
|
|
{
|
|
|
|
// Special case for audio markers being dragged: they use a temporary wrapper object
|
|
|
|
// which must be deleted when it is no longer used.
|
|
|
|
AudioMarkerInteractionObject *dragged_marker = dynamic_cast<AudioMarkerInteractionObject*>(dragged_object);
|
|
|
|
if (dragged_marker)
|
|
|
|
delete dragged_marker;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
dragged_object = new_obj;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (dragged_object && !HasCapture())
|
|
|
|
CaptureMouse();
|
|
|
|
else if (!dragged_object && HasCapture())
|
|
|
|
ReleaseMouse();
|
|
|
|
}
|
2007-01-08 04:05:26 +01:00
|
|
|
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
|
|
|
|
{
|
|
|
|
if (new_pos != track_cursor_pos)
|
|
|
|
{
|
|
|
|
int old_pos = track_cursor_pos;
|
|
|
|
track_cursor_pos = new_pos;
|
2007-01-06 00:43:24 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
RefreshRect(wxRect(old_pos - scroll_left - 0, audio_top, 1, audio_height));
|
|
|
|
RefreshRect(wxRect(new_pos - scroll_left - 0, audio_top, 1, audio_height));
|
|
|
|
|
|
|
|
// Make sure the old label gets cleared away
|
|
|
|
RefreshRect(track_cursor_label_rect);
|
|
|
|
|
|
|
|
if (show_time)
|
|
|
|
{
|
|
|
|
AssTime new_label_time;
|
|
|
|
new_label_time.SetMS(controller->MillisecondsFromSamples(SamplesFromAbsoluteX(track_cursor_pos)));
|
|
|
|
track_cursor_label = new_label_time.GetASSFormated();
|
|
|
|
track_cursor_label_rect.x += new_pos - old_pos;
|
|
|
|
RefreshRect(track_cursor_label_rect);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
track_cursor_label_rect.SetSize(wxSize(0,0));
|
|
|
|
track_cursor_label.Clear();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2007-01-06 00:43:24 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::RemoveTrackCursor()
|
|
|
|
{
|
|
|
|
SetTrackCursor(-1, false);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2007-01-06 05:19:00 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
|
|
|
|
{
|
|
|
|
// Check for mouse wheel scrolling
|
|
|
|
if (event.GetWheelRotation() != 0)
|
|
|
|
{
|
|
|
|
// First check if the cursor is inside or outside the display.
|
|
|
|
// If it's outside, we want to send the event to the control it's over instead.
|
|
|
|
/// @todo Factor this into a reusable function
|
|
|
|
{
|
|
|
|
wxWindow *targetwindow = wxFindWindowAtPoint(event.GetPosition());
|
|
|
|
if (targetwindow && targetwindow != this)
|
|
|
|
{
|
|
|
|
targetwindow->GetEventHandler()->ProcessEvent(event);
|
|
|
|
event.Skip(false);
|
|
|
|
return;
|
|
|
|
}
|
2007-01-06 05:19:00 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
bool zoom = event.CmdDown();
|
|
|
|
if (OPT_GET("Audio/Wheel Default to Zoom")->GetBool()) zoom = !zoom;
|
2007-01-06 05:19:00 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (!zoom)
|
|
|
|
{
|
|
|
|
int amount = -event.GetWheelRotation();
|
|
|
|
// If the user did a horizontal scroll the amount should be inverted
|
|
|
|
// for it to be natural.
|
|
|
|
if (event.GetWheelAxis() == 1) amount = -amount;
|
2007-01-06 05:19:00 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Reset any accumulated zoom
|
|
|
|
mouse_zoom_accum = 0;
|
2007-01-06 05:19:00 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
ScrollBy(amount);
|
|
|
|
}
|
|
|
|
else if (event.GetWheelAxis() == 0)
|
|
|
|
{
|
|
|
|
mouse_zoom_accum += event.GetWheelRotation();
|
|
|
|
int zoom_delta = mouse_zoom_accum / event.GetWheelDelta();
|
|
|
|
mouse_zoom_accum %= event.GetWheelDelta();
|
|
|
|
SetZoomLevel(GetZoomLevel() + zoom_delta);
|
|
|
|
/// @todo This has to update the trackbar in the audio box... maybe move handling mouse zoom to
|
|
|
|
/// the audio box instead to avoid messing with friend classes?
|
|
|
|
}
|
2007-01-06 05:19:00 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Scroll event processed
|
|
|
|
return;
|
2008-01-20 21:09:49 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// If we have focus, we get mouse move events on Mac even when the mouse is
|
|
|
|
// outside our client rectangle, we don't want those.
|
|
|
|
if (event.Moving() && !GetClientRect().Contains(event.GetPosition()))
|
|
|
|
{
|
|
|
|
event.Skip();
|
2007-06-23 04:43:42 +02:00
|
|
|
return;
|
2006-02-25 07:04:46 +01:00
|
|
|
}
|
2006-03-06 00:01:02 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (event.IsButton())
|
|
|
|
SetFocus();
|
|
|
|
|
|
|
|
// Handle any ongoing drag
|
|
|
|
if (dragged_object && HasCapture())
|
|
|
|
{
|
|
|
|
if (!dragged_object->OnMouseEvent(event))
|
|
|
|
{
|
|
|
|
SetDraggedObject(0);
|
|
|
|
SetCursor(wxNullCursor);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Something is wrong, we might have lost capture somehow.
|
|
|
|
// Fix state and pretend it didn't happen.
|
|
|
|
SetDraggedObject(0);
|
|
|
|
SetCursor(wxNullCursor);
|
2006-03-06 00:01:02 +01:00
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
wxPoint mousepos = event.GetPosition();
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Check for scrollbar action
|
|
|
|
if (scrollbar->GetBounds().Contains(mousepos))
|
|
|
|
{
|
|
|
|
if (!controller->IsPlaying())
|
|
|
|
RemoveTrackCursor();
|
|
|
|
if (scrollbar->OnMouseEvent(event))
|
|
|
|
SetDraggedObject(scrollbar);
|
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Check for timeline action
|
|
|
|
if (timeline->GetBounds().Contains(mousepos))
|
|
|
|
{
|
|
|
|
SetCursor(wxCursor(wxCURSOR_SIZEWE));
|
|
|
|
if (!controller->IsPlaying())
|
|
|
|
RemoveTrackCursor();
|
|
|
|
if (timeline->OnMouseEvent(event))
|
|
|
|
SetDraggedObject(timeline);
|
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
AudioTimingController *timing = controller->GetTimingController();
|
|
|
|
int drag_sensitivity = pixel_samples*3; /// @todo Make this depend on configuration
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
// Not scrollbar, not timeline, no button action
|
|
|
|
if (event.Moving())
|
|
|
|
{
|
|
|
|
if (timing)
|
|
|
|
{
|
|
|
|
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (timing->IsNearbyMarker(samplepos, drag_sensitivity))
|
|
|
|
SetCursor(wxCursor(wxCURSOR_SIZEWE));
|
|
|
|
else
|
|
|
|
SetCursor(wxNullCursor);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (!controller->IsPlaying())
|
|
|
|
SetTrackCursor(scroll_left + mousepos.x, true);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (event.Leaving() && !controller->IsPlaying())
|
|
|
|
{
|
|
|
|
RemoveTrackCursor();
|
2007-01-06 05:27:09 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (event.LeftDown() && timing)
|
|
|
|
{
|
|
|
|
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
|
|
|
|
AudioMarker *marker = timing->OnLeftClick(samplepos, drag_sensitivity);
|
2007-01-06 05:27:09 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (marker)
|
|
|
|
{
|
|
|
|
RemoveTrackCursor();
|
|
|
|
SetDraggedObject(new AudioMarkerInteractionObject(marker, timing, this, controller, wxMOUSE_BTN_LEFT));
|
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (event.RightDown() && timing)
|
|
|
|
{
|
|
|
|
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
|
|
|
|
AudioMarker *marker = timing->OnRightClick(samplepos, drag_sensitivity);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (marker)
|
|
|
|
{
|
|
|
|
RemoveTrackCursor();
|
|
|
|
SetDraggedObject(new AudioMarkerInteractionObject(marker, timing, this, controller, wxMOUSE_BTN_RIGHT));
|
|
|
|
return;
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
/// @todo Handle middle click to seek video
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnSize(wxSizeEvent &event)
|
|
|
|
{
|
|
|
|
// We changed size, update the sub-controls' internal data and redraw
|
|
|
|
wxSize size = GetClientSize();
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
scrollbar->SetDisplaySize(size);
|
|
|
|
timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_height = size.GetHeight();
|
|
|
|
audio_height -= scrollbar->GetBounds().GetHeight();
|
|
|
|
audio_height -= timeline->GetHeight();
|
|
|
|
audio_renderer->SetHeight(audio_height);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_top = timeline->GetHeight();
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
Refresh();
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2006-07-01 08:37:46 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnFocus(wxFocusEvent &event)
|
|
|
|
{
|
|
|
|
// The scrollbar indicates focus so repaint that
|
|
|
|
RefreshRect(scrollbar->GetBounds());
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnAudioOpen(AudioProvider *_provider)
|
|
|
|
{
|
|
|
|
provider = _provider;
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
audio_renderer->SetAudioProvider(provider);
|
|
|
|
audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
if (provider)
|
|
|
|
timeline->ChangeAudio(provider->GetNumSamples(), provider->GetSampleRate());
|
|
|
|
|
|
|
|
SetZoomLevel(zoom_level);
|
|
|
|
|
|
|
|
Refresh();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnAudioClose()
|
|
|
|
{
|
|
|
|
OnAudioOpen(0);
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2007-06-20 04:18:55 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
|
|
|
|
{
|
|
|
|
SetTrackCursor(AbsoluteXFromSamples(sample_position), false);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnPlaybackStop()
|
|
|
|
{
|
|
|
|
RemoveTrackCursor();
|
|
|
|
}
|
2006-01-16 22:02:54 +01:00
|
|
|
|
2007-06-20 04:18:55 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
void AudioDisplay::OnMarkersMoved()
|
|
|
|
{
|
|
|
|
Refresh();
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
void AudioDisplay::OnSelectionChanged()
|
|
|
|
{
|
|
|
|
/// @todo Handle rendering style ranges from timing controller instead
|
|
|
|
AudioController::SampleRange sel(controller->GetPrimaryPlaybackRange());
|
|
|
|
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
|
|
|
|
|
|
|
|
if (sel.overlaps(old_selection))
|
|
|
|
{
|
|
|
|
// Only redraw the parts of the selection that changed, to avoid flicker
|
|
|
|
int s1 = RelativeXFromSamples(sel.begin());
|
|
|
|
int e1 = RelativeXFromSamples(sel.end());
|
|
|
|
int s2 = RelativeXFromSamples(old_selection.begin());
|
|
|
|
int e2 = RelativeXFromSamples(old_selection.end());
|
|
|
|
if (s1 != s2)
|
|
|
|
{
|
|
|
|
wxRect r(std::min(s1, s2)-10, audio_top, abs(s1-s2)+20, audio_height);
|
|
|
|
RefreshRect(r);
|
|
|
|
}
|
|
|
|
if (e1 != e2)
|
|
|
|
{
|
|
|
|
wxRect r(std::min(e1, e2)-10, audio_top, abs(e1-e2)+20, audio_height);
|
|
|
|
RefreshRect(r);
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
RefreshRect(wxRect(0, audio_top, GetClientSize().GetX(), audio_height));
|
2006-01-16 22:02:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
RefreshRect(scrollbar->GetBounds());
|
|
|
|
|
|
|
|
old_selection = sel;
|
2010-12-07 20:09:28 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
void AudioDisplay::OnTimingControllerChanged()
|
|
|
|
{
|
|
|
|
Refresh();
|
|
|
|
/// @todo Do something more about the new timing controller?
|
2008-03-13 19:55:09 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|