Use signals in AudioController and AudioTimingController

Originally committed to SVN as r4907.
This commit is contained in:
Thomas Goyne 2010-12-08 08:09:16 +00:00
parent 3bb1f590d7
commit 3345797ff6
31 changed files with 222 additions and 448 deletions

View File

@ -39,7 +39,6 @@
#include "ass_export_filter.h"
#include "ass_exporter.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "frame_main.h"
/// @brief Constructor

View File

@ -255,7 +255,7 @@ AudioBox::AudioBox(wxWindow *parent, AudioController *_controller, SelectionCont
SetSizer(MainSizer);
SetKaraokeButtons(); // Decide which one to show or hide.
timing_controller_dialogue = CreateDialogueTimingController(controller, selection_controller);
controller->SetTimingController(timing_controller_dialogue);
}
@ -425,8 +425,8 @@ void AudioBox::OnPrev(wxCommandEvent &event) {
/// @param event
///
void AudioBox::OnPlay500Before(wxCommandEvent &event) {
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(AudioController::SampleRange(
SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(SampleRange(
times.begin() - controller->SamplesFromMilliseconds(500),
times.begin()));
}
@ -437,8 +437,8 @@ void AudioBox::OnPlay500Before(wxCommandEvent &event) {
/// @param event
///
void AudioBox::OnPlay500After(wxCommandEvent &event) {
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(AudioController::SampleRange(
SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(SampleRange(
times.end(),
times.end() + controller->SamplesFromMilliseconds(500)));
}
@ -449,8 +449,8 @@ void AudioBox::OnPlay500After(wxCommandEvent &event) {
/// @param event
///
void AudioBox::OnPlay500First(wxCommandEvent &event) {
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(AudioController::SampleRange(
SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(SampleRange(
times.begin(),
times.begin() + std::min(
controller->SamplesFromMilliseconds(500),
@ -463,8 +463,8 @@ void AudioBox::OnPlay500First(wxCommandEvent &event) {
/// @param event
///
void AudioBox::OnPlay500Last(wxCommandEvent &event) {
AudioController::SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(AudioController::SampleRange(
SampleRange times(controller->GetPrimaryPlaybackRange());
controller->PlayRange(SampleRange(
times.end() - std::min(
controller->SamplesFromMilliseconds(500),
times.length()),

View File

@ -54,14 +54,11 @@
#include <wx/tglbtn.h>
#endif
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
#error You must include "audio_controller.h" before "audio_box.h"
#endif
//////////////
// Prototypes
class AudioController;
class AssDialogue;
class AudioTimingController;
class AudioDisplay;
class AudioKaraoke;
class FrameMain;
@ -80,7 +77,7 @@ class AudioBox : public wxPanel {
/// Selection controller used for timing controllers
SelectionController<AssDialogue> *selection_controller;
/// The regular dalogue timing controller
/// The regular dialogue timing controller
AudioTimingController *timing_controller_dialogue;
/// DOCME

View File

@ -74,71 +74,51 @@ bool operator < (int64_t a, const AudioMarkerKeyframe &b) { return a < b.GetPosi
bool operator < (const AudioMarkerKeyframe &a, int64_t b) { return a.GetPosition() < b; }
wxPen AudioMarkerKeyframe::style;
class AudioMarkerProviderKeyframes : public AudioMarkerProvider, private AudioControllerAudioEventListener {
// GetMarkers needs to be const but still needs to modify this state, which is really
// just a cache... use the mutable "hack".
mutable int last_keyframes_revision;
mutable std::vector<AudioMarkerKeyframe> keyframe_samples;
AudioController *controller;
int64_t samplerate;
class AudioMarkerProviderKeyframes : public AudioMarkerProvider {
VideoContext *vc;
void ReloadKeyframes() const
agi::signal::Connection keyframe_slot;
agi::signal::Connection audio_open_slot;
std::vector<AudioMarkerKeyframe> keyframe_samples;
AudioController *controller;
void OnKeyframesOpen(std::vector<int> const& raw_keyframes)
{
keyframe_samples.clear();
VideoContext *vc = VideoContext::Get();
if (!vc) return;
last_keyframes_revision = vc->GetKeyframesRevision();
const std::vector<int> &raw_keyframes = vc->GetKeyFrames();
keyframe_samples.reserve(raw_keyframes.size());
for (size_t i = 0; i < raw_keyframes.size(); ++i)
{
keyframe_samples.push_back(AudioMarkerKeyframe(
vc->TimeAtFrame(raw_keyframes[i]) * samplerate / 1000));
controller->SamplesFromMilliseconds(vc->TimeAtFrame(raw_keyframes[i]))));
}
std::sort(keyframe_samples.begin(), keyframe_samples.end());
AnnounceMarkerMoved();
}
private:
// AudioControllerAudioEventListener implementation
virtual void OnAudioOpen(AudioProvider *provider)
void OnAudioOpen(AudioProvider *)
{
samplerate = provider->GetSampleRate();
ReloadKeyframes();
OnKeyframesOpen(vc->GetKeyFrames());
}
virtual void OnAudioClose() { }
virtual void OnPlaybackPosition(int64_t sample_position) { }
virtual void OnPlaybackStop() { }
public:
AudioMarkerProviderKeyframes(AudioController *controller)
: controller(controller)
: vc(VideoContext::Get())
, keyframe_slot(vc->AddKeyframesOpenListener(&AudioMarkerProviderKeyframes::OnKeyframesOpen, this))
, audio_open_slot(controller->AddAudioOpenListener(&AudioMarkerProviderKeyframes::OnAudioOpen, this))
, controller(controller)
{
// Assume that a video context with keyframes revision 0 never has keyframes loaded
last_keyframes_revision = 0;
samplerate = 44100;
controller->AddAudioListener(this);
OnKeyframesOpen(vc->GetKeyFrames());
}
virtual ~AudioMarkerProviderKeyframes()
void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const
{
controller->RemoveAudioListener(this);
}
void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out) const
{
VideoContext *vc = VideoContext::Get();
if (!vc) return;
// Re-read keyframe data if the revision number changed, the keyframe data probably did too
if (vc->GetKeyframesRevision() != last_keyframes_revision)
ReloadKeyframes();
// Find first and last keyframes inside the range
std::vector<AudioMarkerKeyframe>::iterator a = std::lower_bound(
std::vector<AudioMarkerKeyframe>::const_iterator a = std::lower_bound(
keyframe_samples.begin(), keyframe_samples.end(), range.begin());
std::vector<AudioMarkerKeyframe>::iterator b = std::upper_bound(
std::vector<AudioMarkerKeyframe>::const_iterator b = std::upper_bound(
keyframe_samples.begin(), keyframe_samples.end(), range.end());
// Place pointers to the markers in the output vector
@ -147,22 +127,9 @@ public:
}
};
/// Type of the audio event listener container in AudioController
typedef std::set<AudioControllerAudioEventListener *> AudioEventListenerSet;
/// Type of the timing event listener container in AudioController
typedef std::set<AudioControllerTimingEventListener *> TimingEventListenerSet;
/// Macro to iterate audio event listeners in AudioController implementation
#define AUDIO_LISTENERS(listener) for (AudioEventListenerSet::iterator listener = audio_event_listeners.begin(); listener != audio_event_listeners.end(); ++listener)
/// Macro to iterate audio event listeners in AudioController implementation
#define TIMING_LISTENERS(listener) for (TimingEventListenerSet::iterator listener = timing_event_listeners.begin(); listener != timing_event_listeners.end(); ++listener)
AudioController::AudioController()
: player(0)
, provider(0)
, timing_controller(0)
, keyframes_marker_provider(new AudioMarkerProviderKeyframes(this))
, playback_mode(PM_NotPlaying)
, playback_timer(this)
@ -195,10 +162,7 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &event)
}
else
{
AUDIO_LISTENERS(l)
{
(*l)->OnPlaybackPosition(pos);
}
AnnouncePlaybackPosition(pos);
}
}
@ -312,10 +276,7 @@ void AudioController::OpenAudio(const wxString &url)
}
// Tell listeners about this.
AUDIO_LISTENERS(l)
{
(*l)->OnAudioOpen(provider);
}
AnnounceAudioOpen(provider);
}
@ -328,10 +289,7 @@ void AudioController::CloseAudio()
player = 0;
provider = 0;
AUDIO_LISTENERS(l)
{
(*l)->OnAudioClose();
}
AnnounceAudioClose();
}
@ -347,90 +305,37 @@ wxString AudioController::GetAudioURL() const
return _T("");
}
void AudioController::AddAudioListener(AudioControllerAudioEventListener *listener)
{
audio_event_listeners.insert(listener);
}
void AudioController::RemoveAudioListener(AudioControllerAudioEventListener *listener)
{
audio_event_listeners.erase(listener);
}
void AudioController::AddTimingListener(AudioControllerTimingEventListener *listener)
{
timing_event_listeners.insert(listener);
}
void AudioController::RemoveTimingListener(AudioControllerTimingEventListener *listener)
{
timing_event_listeners.erase(listener);
}
void AudioController::SetTimingController(AudioTimingController *new_controller)
{
delete timing_controller;
timing_controller = new_controller;
TIMING_LISTENERS(l)
{
(*l)->OnTimingControllerChanged();
if (timing_controller.get() != new_controller) {
timing_controller.reset(new_controller);
timing_controller->AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
timing_controller->AddUpdatedStyleRangesListener(&AudioController::OnTimingControllerUpdatedStyleRanges, this);
}
AnnounceTimingControllerChanged();
}
void AudioController::OnTimingControllerUpdatedPrimaryRange(AudioTimingController *sending_controller)
void AudioController::OnTimingControllerUpdatedPrimaryRange()
{
assert(sending_controller != 0);
if (sending_controller != timing_controller)
return;
if (playback_mode == PM_PrimaryRange)
{
player->SetEndPosition(timing_controller->GetPrimaryPlaybackRange().end());
}
TIMING_LISTENERS(l)
{
(*l)->OnSelectionChanged();
}
AnnounceSelectionChanged();
}
void AudioController::OnTimingControllerUpdatedStyleRanges(AudioTimingController *sending_controller)
void AudioController::OnTimingControllerUpdatedStyleRanges()
{
assert(sending_controller != 0);
if (sending_controller != timing_controller)
return;
/// @todo redraw and stuff, probably
}
void AudioController::OnTimingControllerMarkerMoved(AudioTimingController *sending_controller, AudioMarker *marker)
{
assert(sending_controller != 0);
if (sending_controller != timing_controller)
return;
/// @todo shouldn't this be more detailed?
TIMING_LISTENERS(l)
{
(*l)->OnMarkersMoved();
}
}
void AudioController::PlayRange(const AudioController::SampleRange &range)
void AudioController::PlayRange(const SampleRange &range)
{
if (!IsAudioOpen()) return;
@ -438,10 +343,7 @@ void AudioController::PlayRange(const AudioController::SampleRange &range)
playback_mode = PM_Range;
playback_timer.Start(20);
AUDIO_LISTENERS(l)
{
(*l)->OnPlaybackPosition(range.begin());
}
AnnouncePlaybackPosition(range.begin());
}
@ -461,10 +363,7 @@ void AudioController::PlayToEnd(int64_t start_sample)
playback_mode = PM_ToEnd;
playback_timer.Start(20);
AUDIO_LISTENERS(l)
{
(*l)->OnPlaybackPosition(start_sample);
}
AnnouncePlaybackPosition(start_sample);
}
@ -476,10 +375,7 @@ void AudioController::Stop()
playback_mode = PM_NotPlaying;
playback_timer.Stop();
AUDIO_LISTENERS(l)
{
(*l)->OnPlaybackStop();
}
AnnouncePlaybackStop();
}
@ -505,9 +401,9 @@ void AudioController::ResyncPlaybackPosition(int64_t new_position)
}
AudioController::SampleRange AudioController::GetPrimaryPlaybackRange() const
SampleRange AudioController::GetPrimaryPlaybackRange() const
{
if (timing_controller != 0)
if (timing_controller.get())
{
return timing_controller->GetPrimaryPlaybackRange();
}
@ -522,6 +418,7 @@ void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &ma
{
/// @todo Find all sources of markers
keyframes_marker_provider->GetMarkers(range, markers);
if (timing_controller.get()) timing_controller->GetMarkers(range, markers);
}
@ -565,4 +462,3 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
return millisamples / sr;
}

View File

@ -48,9 +48,8 @@
#endif
#include <libaegisub/exception.h>
#define AGI_AUDIO_CONTROLLER_INCLUDED 1
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/signals.h>
class AudioPlayer;
class AudioProvider;
@ -62,15 +61,78 @@ class AudioTimingController;
class AudioMarker;
class AudioMarkerProvider;
typedef std::vector<const AudioMarker*> AudioMarkerVector;
/// @class SampleRange
/// @brief Represents an immutable range of audio samples
class SampleRange {
int64_t _begin;
int64_t _end;
public:
/// @brief Constructor
/// @param begin Index of the first sample to include in the range
/// @param end Index of one past the last sample to include in the range
SampleRange(int64_t begin, int64_t end)
: _begin(begin)
, _end(end)
{
assert(end >= begin);
}
/// @brief Copy constructor, optionally adjusting the range
/// @param src The range to duplicate
/// @param begin_adjust Number of samples to add to the start of the range
/// @param end_adjust Number of samples to add to the end of the range
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0)
{
_begin = src._begin + begin_adjust;
_end = src._end + end_adjust;
assert(_end >= _begin);
}
/// Get the number of samples in the range
int64_t length() const { return _end - _begin; }
/// Get the index of the first sample in the range
int64_t begin() const { return _begin; }
/// Get the index of one past the last sample in the range
int64_t end() const { return _end; }
/// Determine whether the range contains a given sample index
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); }
/// Determine whether there is an overlap between two ranges
bool overlaps(const SampleRange &other) const
{
return other.contains(_begin)
|| other.contains(_end)
|| contains(other._begin)
|| contains(other._end);
}
};
/// @class AudioMarkerProvider
/// @brief Abstract interface for audio marker providers
class AudioMarkerProvider {
protected:
/// One or more of the markers provided by this object have changed
agi::signal::Signal<> AnnounceMarkerMoved;
public:
/// Virtual destructor, does nothing
virtual ~AudioMarkerProvider() { }
/// @brief Return markers in a sample range
virtual void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const = 0;
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
};
/// @class AudioController
/// @brief Manage an open audio stream and UI state for it
///
/// Keeps track of the UI interaction state of the open audio for a project, ie. what the current
/// selection is, what moveable markers are on the audio, and any secondary non-moveable markers
/// selection is, what movable markers are on the audio, and any secondary non-movable markers
/// that are present.
///
/// Changes in interaction are broadcast to all managed audio displays so they can redraw, and
@ -85,65 +147,25 @@ typedef std::vector<const AudioMarker*> AudioMarkerVector;
/// There is not supposed to be a way to get direct access to the audio providers or players owned
/// by a controller. If some operation that isn't possible in the existing design is needed, the
/// controller should be extended in some way to allow it.
class AudioController : public wxEvtHandler {
public:
/// @class SampleRange
/// @brief Represents an immutable range of audio samples
class SampleRange {
int64_t _begin;
int64_t _end;
public:
/// @brief Constructor
/// @param begin Index of the first sample to include in the range
/// @param end Index of one past the last sample to include in the range
SampleRange(int64_t begin, int64_t end)
: _begin(begin)
, _end(end)
{
assert(end >= begin);
}
/// @brief Copy constructor, optionally adjusting the range
/// @param src The range to duplicate
/// @param begin_adjust Number of samples to add to the start of the range
/// @param end_adjust Number of samples to add to the end of the range
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0)
{
_begin = src._begin + begin_adjust;
_end = src._end + end_adjust;
assert(_end >= _begin);
}
/// Get the number of samples in the range
int64_t length() const { return _end - _begin; }
/// Get the index of the first sample in the range
int64_t begin() const { return _begin; }
/// Get the index of one past the last sample in the range
int64_t end() const { return _end; }
/// Determine whether the range contains a given sample index
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); }
/// Determine whether there is an overlap between two ranges
bool overlaps(const SampleRange &other) const
{
return other.contains(_begin)
|| other.contains(_end)
|| contains(other._begin)
|| contains(other._end);
}
};
class AudioController : public wxEvtHandler, public AudioMarkerProvider {
private:
/// A new audio stream was opened (and any previously open was closed)
agi::signal::Signal<AudioProvider*> AnnounceAudioOpen;
/// Listeners for audio-related events
std::set<AudioControllerAudioEventListener *> audio_event_listeners;
/// The current audio stream was closed
agi::signal::Signal<> AnnounceAudioClose;
/// Listeners for timing-related events
std::set<AudioControllerTimingEventListener *> timing_event_listeners;
/// Playback is in progress and the current position was updated
agi::signal::Signal<int64_t> AnnouncePlaybackPosition;
/// Playback has stopped
agi::signal::Signal<> AnnouncePlaybackStop;
/// The timing controller was replaced
agi::signal::Signal<> AnnounceTimingControllerChanged;
/// The selected time range changed
agi::signal::Signal<> AnnounceSelectionChanged;
/// The audio output object
AudioPlayer *player;
@ -152,10 +174,10 @@ private:
AudioProvider *provider;
/// The current timing mode, if any; owned by the audio controller
AudioTimingController *timing_controller;
agi::scoped_ptr<AudioTimingController> timing_controller;
/// Provide keyframe data for audio displays
std::auto_ptr<AudioMarkerProvider> keyframes_marker_provider;
agi::scoped_ptr<AudioMarkerProvider> keyframes_marker_provider;
enum PlaybackMode {
@ -174,6 +196,11 @@ private:
/// Event handler for the playback timer
void OnPlaybackTimer(wxTimerEvent &event);
/// @brief Timing controller signals primary playback range changed
void OnTimingControllerUpdatedPrimaryRange();
/// @brief Timing controller signals that the rendering style ranges have changed
void OnTimingControllerUpdatedStyleRanges();
#ifdef wxHAS_POWER_EVENTS
/// Handle computer going into suspend mode by stopping audio and closing device
@ -182,7 +209,6 @@ private:
void OnComputerResuming(wxPowerEvent &event);
#endif
public:
/// @brief Constructor
@ -213,23 +239,6 @@ public:
wxString GetAudioURL() const;
/// @brief Add an audio event listener
/// @param listener The listener to add
void AddAudioListener(AudioControllerAudioEventListener *listener);
/// @brief Remove an audio event listener
/// @param listener The listener to remove
void RemoveAudioListener(AudioControllerAudioEventListener *listener);
/// @brief Add a timing event listener
/// @param listener The listener to add
void AddTimingListener(AudioControllerTimingEventListener *listener);
/// @brief Remove a timing event listener
/// @param listener The listener to remove
void RemoveTimingListener(AudioControllerTimingEventListener *listener);
/// @brief Start or restart audio playback, playing a range
/// @param range The range of audio to play back
///
@ -279,12 +288,9 @@ public:
/// @return An immutable SampleRange object
SampleRange GetPrimaryPlaybackRange() const;
/// @brief Get all static markers inside a range
/// @brief Get all markers inside a range
/// @param range The sample range to retrieve markers for
/// @param markers Vector to fill found markers into
///
/// The markers retrieved are static markers the user can't interact with.
/// Markers for user interaction are obtained through the timing controller.
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
@ -304,7 +310,7 @@ public:
/// @brief Return the current timing controller
/// @return The current timing controller or 0
AudioTimingController * GetTimingController() const { return timing_controller; }
AudioTimingController * GetTimingController() const { return timing_controller.get(); }
/// @brief Change the current timing controller
/// @param new_mode The new timing controller or 0. This may be the same object as
@ -312,31 +318,6 @@ public:
/// the object being timed, eg. changed to a new dialogue line.
void SetTimingController(AudioTimingController *new_controller);
/// @brief Timing controller signals primary playback range changed
/// @param timing_controller The timing controller sending this notification
///
/// Only timing controllers should call this function. This function must be called
/// when the primary playback range is changed in the timing controller, usually
/// as a result of user interaction.
void OnTimingControllerUpdatedPrimaryRange(AudioTimingController *timing_controller);
/// @brief Timing controller signals that the rendering style ranges have changed
/// @param timing_controller The timing controller sending this notification
///
/// Only timing controllers should call this function. This function must be called
/// when one or more rendering style ranges have changed in the timing controller.
void OnTimingControllerUpdatedStyleRanges(AudioTimingController *timing_controller);
/// @brief Timing controller signals that an audio marker has moved
/// @param timing_controller The timing controller sending this notification
/// @param marker The marker that was moved
///
/// Only timing controllers should call this function. This function must be called
/// when a marker owned by the timing controller has been updated in some way.
void OnTimingControllerMarkerMoved(AudioTimingController *timing_controller, AudioMarker *marker);
/// @brief Convert a count of audio samples to a time in milliseconds
/// @param samples Sample count to convert
/// @return The number of milliseconds equivalent to the sample-count, rounded down
@ -346,57 +327,15 @@ public:
/// @param ms Time in milliseconds to convert
/// @return The index of the first sample that is wholly inside the millisecond
int64_t SamplesFromMilliseconds(int64_t ms) const;
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackPosition, AddPlaybackPositionListener)
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop, AddPlaybackStopListener)
DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionChangedListener)
};
/// @class AudioControllerAudioEventListener
/// @brief Abstract interface for objects that want audio events
class AudioControllerAudioEventListener {
public:
/// A new audio stream was opened (and any previously open was closed)
virtual void OnAudioOpen(AudioProvider *) = 0;
/// The current audio stream was closed
virtual void OnAudioClose() = 0;
/// Playback is in progress and ths current position was updated
virtual void OnPlaybackPosition(int64_t sample_position) = 0;
/// Playback has stopped
virtual void OnPlaybackStop() = 0;
};
/// @class AudioControllerTimingEventListener
/// @brief Abstract interface for objects that want audio timing events
class AudioControllerTimingEventListener {
public:
/// One or more moveable markers were moved
virtual void OnMarkersMoved() = 0;
/// The selection was changed
virtual void OnSelectionChanged() = 0;
/// The timing controller was replaced
virtual void OnTimingControllerChanged() = 0;
};
/// @class AudioMarkerProvider
/// @brief Abstract interface for audio marker providers
class AudioMarkerProvider {
public:
/// Virtual destructor, does nothing
virtual ~AudioMarkerProvider() { }
/// @brief Return markers in a sample range
virtual void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out) const = 0;
};
/// @class AudioMarker
/// @brief A marker on the audio display
class AudioMarker {
@ -431,8 +370,6 @@ public:
virtual bool CanSnap() const = 0;
};
namespace agi {
DEFINE_BASE_EXCEPTION(AudioControllerError, Exception);
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioControllerError, "audio_controller/open_failed");

View File

@ -496,7 +496,7 @@ public:
if (marker->CanSnap() && (default_snap != event.ShiftDown()))
{
AudioController::SampleRange snap_sample_range(
SampleRange snap_sample_range(
display->SamplesFromRelativeX(event.GetPosition().x - snap_range),
display->SamplesFromRelativeX(event.GetPosition().x + snap_range));
const AudioMarker *snap_marker = 0;
@ -546,8 +546,13 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller)
track_cursor_pos = -1;
controller->AddAudioListener(this);
controller->AddTimingListener(this);
slots.push_back(controller->AddAudioOpenListener(&AudioDisplay::OnAudioOpen, this));
slots.push_back(controller->AddAudioCloseListener(&AudioDisplay::OnAudioOpen, this, (AudioProvider*)0));
slots.push_back(controller->AddPlaybackPositionListener(&AudioDisplay::OnPlaybackPosition, this));
slots.push_back(controller->AddPlaybackStopListener(&AudioDisplay::RemoveTrackCursor, this));
slots.push_back(controller->AddTimingControllerListener(&AudioDisplay::Refresh, this, true, (const wxRect*)0));
slots.push_back(controller->AddMarkerMovedListener(&AudioDisplay::Refresh, this, true, (const wxRect*)0));
slots.push_back(controller->AddSelectionChangedListener(&AudioDisplay::OnSelectionChanged, this));
OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this);
@ -564,8 +569,6 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller)
AudioDisplay::~AudioDisplay()
{
controller->RemoveAudioListener(this);
controller->RemoveTimingListener(this);
}
@ -614,7 +617,7 @@ void AudioDisplay::ScrollSampleToCenter(int64_t sample_position)
}
void AudioDisplay::ScrollSampleRangeInView(const AudioController::SampleRange &range)
void AudioDisplay::ScrollSampleRangeInView(const SampleRange &range)
{
int client_width = GetClientRect().GetWidth();
int range_begin = AbsoluteXFromSamples(range.begin());
@ -810,7 +813,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
bool redraw_timeline = false;
/// @todo Get rendering style ranges from timing controller instead
AudioController::SampleRange sel_samples(controller->GetPrimaryPlaybackRange());
SampleRange sel_samples(controller->GetPrimaryPlaybackRange());
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
int selection_end = AbsoluteXFromSamples(sel_samples.end());
@ -856,12 +859,10 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
// Draw markers on top of it all
AudioMarkerVector markers;
const int foot_size = 6;
AudioController::SampleRange updrectsamples(
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)
@ -1184,35 +1185,15 @@ void AudioDisplay::OnAudioOpen(AudioProvider *_provider)
Refresh();
}
void AudioDisplay::OnAudioClose()
{
OnAudioOpen(0);
}
void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
{
SetTrackCursor(AbsoluteXFromSamples(sample_position), false);
}
void AudioDisplay::OnPlaybackStop()
{
RemoveTrackCursor();
}
void AudioDisplay::OnMarkersMoved()
{
Refresh();
}
void AudioDisplay::OnSelectionChanged()
{
/// @todo Handle rendering style ranges from timing controller instead
AudioController::SampleRange sel(controller->GetPrimaryPlaybackRange());
SampleRange sel(controller->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
if (sel.overlaps(old_selection))
@ -1242,11 +1223,3 @@ void AudioDisplay::OnSelectionChanged()
old_selection = sel;
}
void AudioDisplay::OnTimingControllerChanged()
{
Refresh();
/// @todo Do something more about the new timing controller?
}

View File

@ -38,6 +38,7 @@
#pragma once
#ifndef AGI_PRE
#include <list>
#include <stdint.h>
#include <wx/bitmap.h>
@ -47,6 +48,7 @@
#endif
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/signals.h>
class AudioRenderer;
@ -108,8 +110,9 @@ public:
/// 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, private AudioControllerAudioEventListener, private AudioControllerTimingEventListener {
class AudioDisplay: public wxWindow {
private:
std::list<agi::signal::Connection> slots;
/// The audio renderer manager
agi::scoped_ptr<AudioRenderer> audio_renderer;
@ -178,9 +181,8 @@ private:
/// @brief Remove the tracking cursor from the display
void RemoveTrackCursor();
/// Previous audio selection for optimising redraw when selection changes
AudioController::SampleRange old_selection;
/// Previous audio selection for optimizing redraw when selection changes
SampleRange old_selection;
/// @brief Reload all rendering settings from Options and reset caches
///
@ -197,19 +199,10 @@ private:
/// wxWidgets input focus changed event
void OnFocus(wxFocusEvent &event);
private:
// AudioControllerAudioEventListener implementation
virtual void OnAudioOpen(AudioProvider *provider);
virtual void OnAudioClose();
virtual void OnPlaybackPosition(int64_t sample_position);
virtual void OnPlaybackStop();
// AudioControllerTimingEventListener implementation
virtual void OnMarkersMoved();
virtual void OnSelectionChanged();
virtual void OnTimingControllerChanged();
public:
@ -261,7 +254,7 @@ public:
/// 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 ScrollSampleRangeInView(const AudioController::SampleRange &range);
void ScrollSampleRangeInView(const SampleRange &range);
/// @brief Change the zoom level

View File

@ -40,7 +40,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_alsa.h"
#include "main.h"
#include "compat.h"

View File

@ -40,7 +40,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound.h"
#include "frame_main.h"
#include "main.h"

View File

@ -40,7 +40,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_openal.h"
#include "frame_main.h"
#include "utils.h"

View File

@ -38,7 +38,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_oss.h"
#include "frame_main.h"
#include "compat.h"

View File

@ -41,7 +41,6 @@
#include <wx/filename.h>
#endif
#include "audio_controller.h"
#include "audio_provider_hd.h"
#include "compat.h"
#include "dialog_progress.h"

View File

@ -36,7 +36,6 @@
#include "config.h"
#include "audio_controller.h"
#include "audio_provider_ram.h"
#include "dialog_progress.h"
#include "frame_main.h"

View File

@ -38,17 +38,27 @@
class AssDialogue;
class AudioController;
#include <libaegisub/signals.h>
/// @class AudioTimingController
/// @brief Base class for objects controlling audio timing
///
/// There is just one active audio timing controller at a time per audio controller.
/// The timing controller manages the timing mode and supplies markers that can be
/// manupulated to the audio display, as well as the current selection.
/// manipulated to the audio display, as well as the current selection.
///
/// The timing controller must then be sent the marker drag events as well as clicks
/// in empty areas of the audio display.
class AudioTimingController : public AudioMarkerProvider {
protected:
/// The primary playback range has changed, usually as a result of user interaction.
agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
/// One or more rendering style ranges have changed in the timing controller.
agi::signal::Signal<> AnnounceUpdatedStyleRanges;
/// A marker has been updated in some way.
agi::signal::Signal<AudioMarker*> AnnounceMarkerMoved;
public:
/// @brief Get any warning message to show in the audio display
/// @return The warning message to show, may be empty if there is none
@ -58,13 +68,13 @@ public:
/// @return A sample range
///
/// This is used for "bring working area into view" operations.
virtual AudioController::SampleRange GetIdealVisibleSampleRange() const = 0;
virtual SampleRange GetIdealVisibleSampleRange() const = 0;
/// @brief Get the primary playback range
/// @return A sample range
///
/// Get the sample range the user is most likely to want to play back currently.
virtual AudioController::SampleRange GetPrimaryPlaybackRange() const = 0;
virtual SampleRange GetPrimaryPlaybackRange() const = 0;
/// @brief Does this timing mode have labels on the audio display?
/// @return True if this timing mode needs labels on the audio display.
@ -122,9 +132,11 @@ public:
virtual void OnMarkerDrag(AudioMarker *marker, int64_t new_position) = 0;
/// @brief Destructor
///
/// Does nothing in the base class, only present for virtual destruction.
virtual ~AudioTimingController() { }
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
};

View File

@ -143,12 +143,12 @@ private:
public:
// AudioMarkerProvider interface
virtual void GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out_markers) const;
virtual void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const;
// AudioTimingController interface
virtual wxString GetWarningMessage() const;
virtual AudioController::SampleRange GetIdealVisibleSampleRange() const;
virtual AudioController::SampleRange GetPrimaryPlaybackRange() const;
virtual SampleRange GetIdealVisibleSampleRange() const;
virtual SampleRange GetPrimaryPlaybackRange() const;
virtual bool HasLabels() const;
virtual void Next();
virtual void Prev();
@ -270,7 +270,7 @@ const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
void AudioTimingControllerDialogue::GetMarkers(const AudioController::SampleRange &range, AudioMarkerVector &out_markers) const
void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const
{
if (range.contains(markers[0].GetPosition()))
out_markers.push_back(&markers[0]);
@ -303,16 +303,16 @@ wxString AudioTimingControllerDialogue::GetWarningMessage() const
AudioController::SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
{
return GetPrimaryPlaybackRange();
}
AudioController::SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
{
return AudioController::SampleRange(
return SampleRange(
GetLeftMarker()->GetPosition(),
GetRightMarker()->GetPosition());
}
@ -396,7 +396,7 @@ void AudioTimingControllerDialogue::Revert()
bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const
{
AudioController::SampleRange range(sample-sensitivity, sample+sensitivity);
SampleRange range(sample-sensitivity, sample+sensitivity);
return range.contains(markers[0].GetPosition()) || range.contains(markers[1].GetPosition());
}
@ -419,7 +419,7 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen
// Clicked near the left marker:
// Insta-move it and start dragging it
left->SetPosition(sample);
audio_controller->OnTimingControllerMarkerMoved(this, left);
AnnounceMarkerMoved(left);
timing_modified = true;
UpdateSelection();
return left;
@ -436,7 +436,7 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen
// Insta-set the left marker to the clicked position and return the right as the dragged one,
// such that if the user does start dragging, he will create a new selection from scratch
left->SetPosition(sample);
audio_controller->OnTimingControllerMarkerMoved(this, left);
AnnounceMarkerMoved(left);
timing_modified = true;
UpdateSelection();
return right;
@ -449,7 +449,7 @@ AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int se
AudioMarkerDialogueTiming *right = GetRightMarker();
right->SetPosition(sample);
audio_controller->OnTimingControllerMarkerMoved(this, right);
AnnounceMarkerMoved(right);
timing_modified = true;
UpdateSelection();
return right;
@ -462,7 +462,7 @@ void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t ne
assert(marker == &markers[0] || marker == &markers[1]);
static_cast<AudioMarkerDialogueTiming*>(marker)->SetPosition(new_position);
audio_controller->OnTimingControllerMarkerMoved(this, marker);
AnnounceMarkerMoved(marker);
timing_modified = true;
UpdateSelection();
@ -472,7 +472,7 @@ void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t ne
void AudioTimingControllerDialogue::UpdateSelection()
{
audio_controller->OnTimingControllerUpdatedPrimaryRange(this);
AnnounceUpdatedPrimaryRange();
}

View File

@ -50,7 +50,6 @@
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_style.h"
#include "audio_controller.h"
#include "selection_controller.h"
#include "compat.h"
#include "frame_main.h"

View File

@ -42,7 +42,6 @@
#include <wx/display.h> /// Must be included last.
#endif
#include "audio_controller.h"
#include "dialog_detached_video.h"
#include "frame_main.h"
#include "main.h"

View File

@ -51,7 +51,6 @@
#include "ass_file.h"
#include "ass_override.h"
#include "ass_style.h"
#include "audio_controller.h"
#include "compat.h"
#include "dialog_fonts_collector.h"
#include "font_file_lister.h"

View File

@ -47,7 +47,6 @@
#include "compat.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "dialog_search_replace.h"
#include "frame_main.h"
#include "main.h"

View File

@ -44,7 +44,6 @@
#include "ass_dialogue.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "compat.h"
#include "dialog_spellchecker.h"
#include "frame_main.h"

View File

@ -379,7 +379,7 @@ void DialogStyling::OnPlayVideoButton(wxCommandEvent &event) {
/// @param event
///
void DialogStyling::OnPlayAudioButton(wxCommandEvent &event) {
audio->PlayRange(AudioController::SampleRange(
audio->PlayRange(SampleRange(
audio->SamplesFromMilliseconds(line->Start.GetMS()),
audio->SamplesFromMilliseconds(line->End.GetMS())));
TypeBox->SetFocus();

View File

@ -444,7 +444,7 @@ void DialogTranslation::OnPlayVideoButton(wxCommandEvent &event) {
/// @param event
///
void DialogTranslation::OnPlayAudioButton(wxCommandEvent &event) {
audio->PlayRange(AudioController::SampleRange(
audio->PlayRange(SampleRange(
audio->SamplesFromMilliseconds(current->Start.GetMS()),
audio->SamplesFromMilliseconds(current->End.GetMS())));
TransText->SetFocus();

View File

@ -43,7 +43,6 @@
#include <wx/filename.h>
#endif
#include "audio_controller.h"
#include "drop.h"
#include "frame_main.h"

View File

@ -48,7 +48,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "compat.h"
#include "ffmpegsource_common.h"
#include "frame_main.h"

View File

@ -132,7 +132,8 @@ FrameMain::FrameMain (wxArrayString args)
// Contexts and controllers
audioController = new AudioController;
audioController->AddAudioListener(this);
audioController->AddAudioOpenListener(&FrameMain::OnAudioOpen, this);
audioController->AddAudioCloseListener(&FrameMain::OnAudioClose, this);
// Create menu and tool bars
StartupLog(_T("Apply saved Maximized state"));

View File

@ -47,10 +47,6 @@
#include <wx/timer.h>
#endif
#ifndef AGI_AUDIO_CONTROLLER_INCLUDED
#error You must include "audio_controller.h" before "frame_main.h"
#endif
class AssFile;
class VideoDisplay;
class VideoSlider;
@ -63,6 +59,7 @@ class DialogDetachedVideo;
class DialogStyling;
class AegisubFileDropTarget;
class AudioController;
class AudioProvider;
namespace Automation4 { class FeatureMacro; class ScriptManager; }
@ -73,7 +70,7 @@ namespace Automation4 { class FeatureMacro; class ScriptManager; }
/// @brief DOCME
///
/// DOCME
class FrameMain: public wxFrame, private AudioControllerAudioEventListener {
class FrameMain: public wxFrame {
friend class AegisubFileDropTarget;
friend class AegisubApp;
friend class SubtitlesGrid;
@ -327,13 +324,9 @@ private:
void RebuildRecentList(wxString listName,wxMenu *menu,int startID);
void SynchronizeProject(bool FromSubs=false);
private:
// AudioControllerAudioEventListener implementation
virtual void OnAudioOpen(AudioProvider *provider);
virtual void OnAudioClose();
virtual void OnPlaybackPosition(int64_t sample_position);
virtual void OnPlaybackStop();
void OnAudioOpen(AudioProvider *provider);
void OnAudioClose();
void OnSubtitlesFileChanged();

View File

@ -1517,8 +1517,8 @@ void FrameMain::OnMedusaStop(wxCommandEvent &) {
// Otherwise, play the last 500 ms
else {
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(AudioController::SampleRange(
SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(SampleRange(
sel.end() - audioController->SamplesFromMilliseconds(500),
sel.end()));;
}
@ -1526,7 +1526,7 @@ void FrameMain::OnMedusaStop(wxCommandEvent &) {
/// @brief DOCME
void FrameMain::OnMedusaShiftStartForward(wxCommandEvent &) {
AudioController::SampleRange newsel(
SampleRange newsel(
audioController->GetPrimaryPlaybackRange(),
audioController->SamplesFromMilliseconds(10),
0);
@ -1536,7 +1536,7 @@ void FrameMain::OnMedusaShiftStartForward(wxCommandEvent &) {
/// @brief DOCME
void FrameMain::OnMedusaShiftStartBack(wxCommandEvent &) {
AudioController::SampleRange newsel(
SampleRange newsel(
audioController->GetPrimaryPlaybackRange(),
-audioController->SamplesFromMilliseconds(10),
0);
@ -1546,7 +1546,7 @@ void FrameMain::OnMedusaShiftStartBack(wxCommandEvent &) {
/// @brief DOCME
void FrameMain::OnMedusaShiftEndForward(wxCommandEvent &) {
AudioController::SampleRange newsel(
SampleRange newsel(
audioController->GetPrimaryPlaybackRange(),
0,
audioController->SamplesFromMilliseconds(10));
@ -1556,7 +1556,7 @@ void FrameMain::OnMedusaShiftEndForward(wxCommandEvent &) {
/// @brief DOCME
void FrameMain::OnMedusaShiftEndBack(wxCommandEvent &) {
AudioController::SampleRange newsel(
SampleRange newsel(
audioController->GetPrimaryPlaybackRange(),
0,
-audioController->SamplesFromMilliseconds(10));
@ -1566,16 +1566,16 @@ void FrameMain::OnMedusaShiftEndBack(wxCommandEvent &) {
/// @brief DOCME
void FrameMain::OnMedusaPlayBefore(wxCommandEvent &) {
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(AudioController::SampleRange(
SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(SampleRange(
sel.begin() - audioController->SamplesFromMilliseconds(500),
sel.begin()));;
}
/// @brief DOCME
void FrameMain::OnMedusaPlayAfter(wxCommandEvent &) {
AudioController::SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(AudioController::SampleRange(
SampleRange sel(audioController->GetPrimaryPlaybackRange());
audioController->PlayRange(SampleRange(
sel.end(),
sel.end() + audioController->SamplesFromMilliseconds(500)));;
}
@ -1623,16 +1623,6 @@ void FrameMain::OnAudioClose()
SetDisplayMode(-1, 0);
}
void FrameMain::OnPlaybackPosition(int64_t sample_position)
{
// do nothing
}
void FrameMain::OnPlaybackStop()
{
// do nothing
}
void FrameMain::OnSubtitlesFileChanged() {
if (OPT_GET("App/Auto/Save on Every Change")->GetBool()) {
if (ass->IsModified() && !ass->filename.empty()) SaveSubtitles(false);
@ -1640,4 +1630,3 @@ void FrameMain::OnSubtitlesFileChanged() {
UpdateTitle();
}

View File

@ -55,7 +55,6 @@
#include "ass_file.h"
#include "ass_time.h"
#include "selection_controller.h"
#include "audio_controller.h"
#include "audio_box.h"
#ifdef WITH_AUTOMATION
#include "auto4_base.h"

View File

@ -50,7 +50,6 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "ass_file.h"
#include "dialog_progress.h"
#include "frame_main.h"

View File

@ -45,7 +45,6 @@
#include "ass_dialogue.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "frame_main.h"
#include "help_button.h"
#include "libresrc/libresrc.h"

View File

@ -308,7 +308,7 @@ void VideoContext::PlayNextFrame() {
JumpToFrame(frame_n + 1);
// Start playing audio
if (playAudioOnStep->GetBool()) {
audio->PlayRange(AudioController::SampleRange(
audio->PlayRange(SampleRange(
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame)),
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame + 1))));
}
@ -322,7 +322,7 @@ void VideoContext::PlayPrevFrame() {
JumpToFrame(frame_n -1);
// Start playing audio
if (playAudioOnStep->GetBool()) {
audio->PlayRange(AudioController::SampleRange(
audio->PlayRange(SampleRange(
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame - 1)),
audio->SamplesFromMilliseconds(TimeAtFrame(thisFrame))));
}
@ -355,7 +355,7 @@ void VideoContext::PlayLine() {
if (!curline) return;
// Start playing audio
audio->PlayRange(AudioController::SampleRange(
audio->PlayRange(SampleRange(
audio->SamplesFromMilliseconds(curline->Start.GetMS()),
audio->SamplesFromMilliseconds(curline->End.GetMS())));