Aegisub/aegisub/src/audio_display.h
Thomas Goyne 7ca0ad3233 Repaint the entire audio display when style ranges change
In practice, painting audio from the cache isn't all that expensive, so
the limited refreshing was just introducing places for bugs without
improving performance all that much.

Closes #1432.

Originally committed to SVN as r6439.
2012-02-02 22:57:53 +00:00

331 lines
12 KiB
C++

// Copyright (c) 2005, Rodrigo Braz Monteiro
// Copyright (c) 2009-2010, Niels Martin Hansen
// 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.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$
/// @file audio_display.h
/// @see audio_display.cpp
/// @ingroup audio_ui
///
#ifndef AGI_PRE
#include <list>
#include <stdint.h>
#include <wx/gdicmn.h>
#include <wx/string.h>
#include <wx/window.h>
#endif
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/signal.h>
namespace agi { struct Context; }
class AudioController;
class AudioRenderer;
class AudioRendererBitmapProvider;
class AudioKaraoke;
class AudioProvider;
class TimeRange;
// Helper classes used in implementation of the audio display
class AudioDisplayScrollbar;
class AudioDisplayTimeline;
class AudioDisplaySelection;
class AudioMarkerInteractionObject;
/// @class AudioDisplayInteractionObject
/// @brief Interface for objects on the audio display that can respond to mouse events
class AudioDisplayInteractionObject {
public:
/// @brief The user is interacting with the object using the mouse
/// @param event Mouse event data
/// @return True to take mouse capture, false to release mouse capture
///
/// Assuming no object has the mouse capture, the audio display uses other methods
/// in the object implementing this interface to determine whether a mouse event
/// should go to the object. If the mouse event goes to the object, this method
/// is called.
///
/// If this method returns true, the audio display takes the mouse capture and
/// stores a pointer to the AudioDisplayInteractionObject interface for the object
/// and redirects the next mouse event to that object.
///
/// If the object that has the mouse capture returns false from this method, the
/// capture is released and regular processing is done for the next event.
///
/// If the object does not have mouse capture and returns false from this method,
/// no capture is taken or released and regular processing is done for the next
/// mouse event.
virtual bool OnMouseEvent(wxMouseEvent &event) = 0;
/// @brief Destructor
///
/// Empty virtual destructor for the cases that need it.
virtual ~AudioDisplayInteractionObject() { }
};
/// @class AudioDisplay
/// @brief Primary view/UI for interaction with audio timing
///
/// The audio display is the common view that allows the user to interact with the active
/// timing controller. The audio display also renders audio according to the audio controller
/// and the timing controller, using an audio renderer instance.
class AudioDisplay: public wxWindow {
agi::signal::Connection audio_open_connection;
std::list<agi::signal::Connection> connections;
agi::Context *context;
/// The audio renderer manager
agi::scoped_ptr<AudioRenderer> audio_renderer;
/// The current audio renderer
agi::scoped_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
/// The controller managing us
AudioController *controller;
/// Scrollbar helper object
agi::scoped_ptr<AudioDisplayScrollbar> scrollbar;
/// Timeline helper object
agi::scoped_ptr<AudioDisplayTimeline> timeline;
/// The interaction object for the last-dragged audio marker
agi::scoped_ptr<AudioMarkerInteractionObject> audio_marker;
/// Current object on display being dragged, if any
AudioDisplayInteractionObject *dragged_object;
/// Change the dragged object and update mouse capture
void SetDraggedObject(AudioDisplayInteractionObject *new_obj);
/// Leftmost pixel in the virtual audio image being displayed
int scroll_left;
/// Total width of the audio in pixels
int pixel_audio_width;
/// Horizontal zoom measured in millisecond per pixels
double ms_per_pixel;
/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
float scale_amplitude;
/// Top of the main audio area in pixels
int audio_top;
/// Height of main audio area in pixels
int audio_height;
/// Width of the audio marker feet in pixels
static const int foot_size = 6;
/// Zoom level given as a number, see SetZoomLevel for details
int zoom_level;
/// Absolute pixel position of the tracking cursor (mouse or playback)
int track_cursor_pos;
/// Label to show by track cursor
wxString track_cursor_label;
/// Bounding rectangle last drawn track cursor label
wxRect track_cursor_label_rect;
/// @brief Move the tracking cursor
/// @param new_pos New absolute pixel position of the tracking cursor
/// @param show_time Display timestamp by the tracking cursor?
void SetTrackCursor(int new_pos, bool show_time);
/// @brief Remove the tracking cursor from the display
void RemoveTrackCursor();
/// Previous style ranges for optimizing redraw when ranges change
std::map<int, int> style_ranges;
/// @brief Reload all rendering settings from Options and reset caches
///
/// This can be called if some rendering quality settings have been changed
/// in Options and need to be reloaded to take effect.
void ReloadRenderingSettings();
/// Paint the audio data for a time range
/// @param dc DC to paint to
/// @param updtime Time range to repaint
/// @param updrect Pixel range to repaint
void PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect);
/// Paint the markers in a time range
/// @param dc DC to paint to
/// @param updtime Time range to repaint
void PaintMarkers(wxDC &dc, TimeRange updtime);
/// Draw a single foot for a marker
/// @param dc DC to paint to
/// @param marker_x Position of the marker whose foot is being painted in pixels
/// @param dir -1 for left, 1 for right
void PaintFoot(wxDC &dc, int marker_x, int dir);
/// Paint the labels in a time range
/// @param dc DC to paint to
/// @param updtime Time range to repaint
void PaintLabels(wxDC &dc, TimeRange updtime);
/// Paint the track cursor
/// @param dc DC to paint to
void PaintTrackCursor(wxDC &dc);
/// wxWidgets paint event
void OnPaint(wxPaintEvent &event);
/// wxWidgets mouse input event
void OnMouseEvent(wxMouseEvent &event);
/// wxWidgets control size changed event
void OnSize(wxSizeEvent &event);
/// wxWidgets input focus changed event
void OnFocus(wxFocusEvent &event);
/// wxWidgets keypress event
void OnKeyDown(wxKeyEvent& event);
// AudioControllerAudioEventListener implementation
void OnAudioOpen(AudioProvider *provider);
void OnPlaybackPosition(int ms_position);
void OnSelectionChanged();
void OnStyleRangesChanged();
void OnTimingController();
void OnMarkerMoved();
public:
AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context);
~AudioDisplay();
/// @brief Scroll the audio display
/// @param pixel_amount Number of pixels to scroll the view
///
/// A positive amount moves the display to the right, making later parts of the audio visible.
void ScrollBy(int pixel_amount);
/// @brief Scroll the audio display
/// @param pixel_position Absolute pixel to put at left edge of the audio display
///
/// This is the principal scrolling function. All other scrolling functions eventually
/// call this function to perform the actual scrolling.
void ScrollPixelToLeft(int pixel_position);
/// @brief Scroll the audio display
/// @param pixel_position Absolute pixel to put in center of the audio display
void ScrollPixelToCenter(int pixel_position) { ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2); }
/// @brief Scroll the audio display
/// @param ms Time in milliseconds to put at left edge of the audio display
void ScrollTimeToLeft(int ms) { ScrollPixelToLeft(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display
/// @param ms Time in milliseconds to put in center of the audio display
void ScrollTimeToCenter(int ms) { ScrollPixelToCenter(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display
/// @param range Time range to ensure is in view
///
/// If the entire range is already visible inside the display, nothing is
/// scrolled. If just one of the two endpoints is visible, the display is
/// scrolled such that the visible endpoint stays in view but more of the
/// rest of the range becomes visible.
///
/// If the entire range fits inside the display, the display is centered
/// over the range. For this calculation, the display is considered
/// smaller by some margins, see below.
///
/// If the range does not fit within the display with margins subtracted,
/// the start of the range is ensured visible and as much of the rest of
/// the range is brought into view.
///
/// For the purpose of this function, a 5 percent margin is assumed at each
/// end of the audio display such that a range endpoint that is ensured to
/// be in view never gets closer to the edge of the display than the
/// margin. The edge that is not ensured to be in view might be outside of
/// view or might be closer to the display edge than the
/// margin.
void ScrollTimeRangeInView(const TimeRange &range);
/// @brief Change the zoom level
/// @param new_zoom_level The new zoom level to use
///
/// A zoom level of 0 is the default zoom level, all other levels are based
/// on this. Negative zoom levels zoom out, positive zoom in.
///
/// The zoom levels generally go from +30 to -30. It is possible to zoom in
/// more than +30.
void SetZoomLevel(int new_zoom_level);
/// @brief Get the zoom level
/// @return The zoom level
///
/// See SetZoomLevel for a description of zoom levels.
int GetZoomLevel() const;
/// @brief Get a textual description of a zoom level
/// @param level The zoom level to describe
/// @return A translated string describing a zoom level
///
/// The zoom level description can tell the user details about how much audio is
/// actually displayed.
wxString GetZoomLevelDescription(int level) const;
/// @brief Get the zoom factor in percent for a zoom level
/// @param level The zoom level to get the factor of
/// @return The zoom factor in percent
///
/// Positive: 125, 150, 175, 200, 225, ...
///
/// Negative: 90, 80, 70, 60, 50, 45, 40, 35, 30, 25, 20, 19, 18, 17, ..., 1
///
/// Too negative numbers get clamped.
static int GetZoomLevelFactor(int level);
/// @brief Set amplitude scale factor
/// @param scale New amplitude scale factor, 1.0 is no scaling
void SetAmplitudeScale(float scale);
/// Get a time in milliseconds from an X coordinate relative to current scroll
int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
/// Get a time in milliseconds from an absolute X coordinate
int TimeFromAbsoluteX(int x) const { return int(x * ms_per_pixel); }
/// Get an X coordinate relative to the current scroll from a time in milliseconds
int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
/// Get an absolute X coordinate from a time in milliseconds
int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
DECLARE_EVENT_TABLE()
};