forked from mia/Aegisub
Add support for labels in the audio display
Originally committed to SVN as r5589.
This commit is contained in:
parent
3e708eab10
commit
3f05fe6b3e
5 changed files with 131 additions and 65 deletions
|
@ -325,6 +325,7 @@ void AudioController::SetTimingController(AudioTimingController *new_controller)
|
||||||
if (timing_controller.get() != new_controller) {
|
if (timing_controller.get() != new_controller) {
|
||||||
timing_controller.reset(new_controller);
|
timing_controller.reset(new_controller);
|
||||||
timing_controller->AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
timing_controller->AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
||||||
|
timing_controller->AddLabelChangedListener(std::tr1::bind(std::tr1::ref(AnnounceLabelChanged)));
|
||||||
timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
|
timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
|
||||||
timing_controller->AddUpdatedStyleRangesListener(&AudioController::OnTimingControllerUpdatedStyleRanges, this);
|
timing_controller->AddUpdatedStyleRangesListener(&AudioController::OnTimingControllerUpdatedStyleRanges, this);
|
||||||
}
|
}
|
||||||
|
@ -436,6 +437,10 @@ void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &ma
|
||||||
if (timing_controller.get()) timing_controller->GetMarkers(range, markers);
|
if (timing_controller.get()) timing_controller->GetMarkers(range, markers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioController::GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const
|
||||||
|
{
|
||||||
|
if (timing_controller.get()) timing_controller->GetLabels(range, labels);
|
||||||
|
}
|
||||||
|
|
||||||
double AudioController::GetVolume() const
|
double AudioController::GetVolume() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -126,6 +126,32 @@ public:
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @class AudioLabelProvider
|
||||||
|
/// @brief Abstract interface for audio label providers
|
||||||
|
class AudioLabelProvider {
|
||||||
|
protected:
|
||||||
|
/// One or more of the labels provided by this object have changed
|
||||||
|
agi::signal::Signal<> AnnounceLabelChanged;
|
||||||
|
public:
|
||||||
|
/// A label for a range of samples on the audio display
|
||||||
|
struct AudioLabel {
|
||||||
|
/// Text of the label
|
||||||
|
wxString text;
|
||||||
|
/// Range which this label applies to
|
||||||
|
SampleRange range;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Virtual destructor, does nothing
|
||||||
|
virtual ~AudioLabelProvider() { }
|
||||||
|
|
||||||
|
/// @brief Get labels in a sample range
|
||||||
|
/// @param range Range of samples to get labels for
|
||||||
|
/// @param[out] out Vector which should be filled with the labels
|
||||||
|
virtual void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const = 0;
|
||||||
|
|
||||||
|
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
|
||||||
|
};
|
||||||
|
|
||||||
/// @class AudioController
|
/// @class AudioController
|
||||||
/// @brief Manage an open audio stream and UI state for it
|
/// @brief Manage an open audio stream and UI state for it
|
||||||
///
|
///
|
||||||
|
@ -148,7 +174,7 @@ public:
|
||||||
/// providers or players owned by a controller. If some operation that isn't
|
/// providers or players owned by a controller. If some operation that isn't
|
||||||
/// possible in the existing design is needed, the controller should be
|
/// possible in the existing design is needed, the controller should be
|
||||||
/// extended in some way to allow it.
|
/// extended in some way to allow it.
|
||||||
class AudioController : public wxEvtHandler, public AudioMarkerProvider {
|
class AudioController : public wxEvtHandler, public AudioMarkerProvider, public AudioLabelProvider {
|
||||||
private:
|
private:
|
||||||
/// A new audio stream was opened (and any previously open was closed)
|
/// A new audio stream was opened (and any previously open was closed)
|
||||||
agi::signal::Signal<AudioProvider*> AnnounceAudioOpen;
|
agi::signal::Signal<AudioProvider*> AnnounceAudioOpen;
|
||||||
|
@ -299,6 +325,11 @@ public:
|
||||||
/// @param markers Vector to fill found markers into
|
/// @param markers Vector to fill found markers into
|
||||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
|
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
|
||||||
|
|
||||||
|
/// @brief Get all labels inside a range
|
||||||
|
/// @param range The sample range to retrieve labels for
|
||||||
|
/// @param labels Vector to fill found labels into
|
||||||
|
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const;
|
||||||
|
|
||||||
|
|
||||||
/// @brief Get the playback audio volume
|
/// @brief Get the playback audio volume
|
||||||
/// @return The amplification factor for the audio
|
/// @return The amplification factor for the audio
|
||||||
|
|
|
@ -790,9 +790,8 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
|
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
|
||||||
int selection_end = AbsoluteXFromSamples(sel_samples.end());
|
int selection_end = AbsoluteXFromSamples(sel_samples.end());
|
||||||
|
|
||||||
wxRegionIterator region(GetUpdateRegion());
|
|
||||||
wxPoint client_org = GetClientAreaOrigin();
|
wxPoint client_org = GetClientAreaOrigin();
|
||||||
while (region)
|
for (wxRegionIterator region(GetUpdateRegion()); region; ++region)
|
||||||
{
|
{
|
||||||
wxRect updrect = region.GetRect();
|
wxRect updrect = region.GetRect();
|
||||||
// Work around wxMac issue, client border offsets update rectangles but does
|
// Work around wxMac issue, client border offsets update rectangles but does
|
||||||
|
@ -804,68 +803,99 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
redraw_scrollbar |= scrollbar_bounds.Intersects(updrect);
|
redraw_scrollbar |= scrollbar_bounds.Intersects(updrect);
|
||||||
redraw_timeline |= timeline_bounds.Intersects(updrect);
|
redraw_timeline |= timeline_bounds.Intersects(updrect);
|
||||||
|
|
||||||
if (audio_bounds.Intersects(updrect))
|
if (!audio_bounds.Intersects(updrect))
|
||||||
{
|
{
|
||||||
int p1, p2, p3, p4;
|
continue;
|
||||||
// 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;
|
// p1 -> p2 = before selection
|
||||||
|
// p2 -> p3 = in selection
|
||||||
|
// p3 -> p4 = after selection
|
||||||
|
int p1 = scroll_left + updrect.x;
|
||||||
|
int p2 = selection_start;
|
||||||
|
int p3 = selection_end;
|
||||||
|
int p4 = p1 + updrect.width;
|
||||||
|
|
||||||
if (p1 < p2)
|
std::vector<RedrawSubregion> subregions;
|
||||||
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;
|
if (p1 < p2)
|
||||||
for (std::vector<RedrawSubregion>::iterator sr = subregions.begin(); sr != subregions.end(); ++sr)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int foot_size = 6;
|
||||||
|
SampleRange updrectsamples(
|
||||||
|
SamplesFromRelativeX(updrect.x - foot_size),
|
||||||
|
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
||||||
|
|
||||||
|
// Draw markers on top of it all
|
||||||
|
AudioMarkerVector markers;
|
||||||
|
controller->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)
|
||||||
{
|
{
|
||||||
audio_renderer->Render(dc, wxPoint(x, audio_top), sr->x1, sr->x2 - sr->x1, sr->selected);
|
wxPoint foot_top[3] = { wxPoint(-foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||||
x += sr->x2 - sr->x1;
|
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);
|
||||||
}
|
}
|
||||||
|
if (marker->GetFeet() & AudioMarker::Feet_Right)
|
||||||
// Draw markers on top of it all
|
|
||||||
AudioMarkerVector markers;
|
|
||||||
const int foot_size = 6;
|
|
||||||
SampleRange updrectsamples(
|
|
||||||
SamplesFromRelativeX(updrect.x - foot_size),
|
|
||||||
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
|
||||||
controller->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;
|
wxPoint foot_top[3] = { wxPoint(foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||||
dc.SetPen(marker->GetStyle());
|
wxPoint foot_bot[3] = { wxPoint(foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
||||||
int marker_x = RelativeXFromSamples(marker->GetPosition());
|
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
||||||
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
|
dc.DrawPolygon(3, foot_bot, 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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
region++;
|
// Draw labels
|
||||||
|
std::vector<AudioLabelProvider::AudioLabel> labels;
|
||||||
|
controller->GetLabels(updrectsamples, labels);
|
||||||
|
if (!labels.empty())
|
||||||
|
{
|
||||||
|
wxDCFontChanger fc(dc);
|
||||||
|
wxFont font = dc.GetFont();
|
||||||
|
font.SetWeight(wxFONTWEIGHT_BOLD);
|
||||||
|
dc.SetFont(font);
|
||||||
|
dc.SetTextForeground(*wxWHITE);
|
||||||
|
for (size_t i = 0; i < labels.size(); ++i)
|
||||||
|
{
|
||||||
|
wxSize extent = dc.GetTextExtent(labels[i].text);
|
||||||
|
int left = RelativeXFromSamples(labels[i].range.begin());
|
||||||
|
int width = AbsoluteXFromSamples(labels[i].range.length());
|
||||||
|
|
||||||
|
// If it doesn't fit, truncate
|
||||||
|
if (width < extent.GetWidth())
|
||||||
|
{
|
||||||
|
dc.SetClippingRegion(left, audio_top + 4, width, extent.GetHeight());
|
||||||
|
dc.DrawText(labels[i].text, left, audio_top + 4);
|
||||||
|
dc.DestroyClippingRegion();
|
||||||
|
}
|
||||||
|
// Otherwise center in the range
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dc.DrawText(labels[i].text, left + (width - extent.GetWidth()) / 2, audio_top + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (track_cursor_pos >= 0)
|
if (track_cursor_pos >= 0)
|
||||||
|
@ -873,7 +903,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
|
wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
|
||||||
dc.DrawLine(track_cursor_pos-scroll_left, audio_top, track_cursor_pos-scroll_left, audio_top+audio_height);
|
dc.DrawLine(track_cursor_pos-scroll_left, audio_top, track_cursor_pos-scroll_left, audio_top+audio_height);
|
||||||
|
|
||||||
if (!track_cursor_label.IsEmpty())
|
if (!track_cursor_label.empty())
|
||||||
{
|
{
|
||||||
wxDCFontChanger fc(dc);
|
wxDCFontChanger fc(dc);
|
||||||
wxFont font = dc.GetFont();
|
wxFont font = dc.GetFont();
|
||||||
|
@ -887,11 +917,13 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
|
|
||||||
int old_bg_mode = dc.GetBackgroundMode();
|
int old_bg_mode = dc.GetBackgroundMode();
|
||||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||||
|
// Draw border
|
||||||
dc.SetTextForeground(wxColour(64, 64, 64));
|
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.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);
|
||||||
|
// Draw fill
|
||||||
dc.SetTextForeground(*wxWHITE);
|
dc.SetTextForeground(*wxWHITE);
|
||||||
dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
|
dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
|
||||||
dc.SetBackgroundMode(old_bg_mode);
|
dc.SetBackgroundMode(old_bg_mode);
|
||||||
|
|
|
@ -51,7 +51,7 @@ class AudioController;
|
||||||
///
|
///
|
||||||
/// The timing controller must then be sent the marker drag events as well as
|
/// The timing controller must then be sent the marker drag events as well as
|
||||||
/// clicks in empty areas of the audio display.
|
/// clicks in empty areas of the audio display.
|
||||||
class AudioTimingController : public AudioMarkerProvider {
|
class AudioTimingController : public AudioMarkerProvider, public AudioLabelProvider {
|
||||||
protected:
|
protected:
|
||||||
/// The primary playback range has changed, usually as a result of user interaction.
|
/// The primary playback range has changed, usually as a result of user interaction.
|
||||||
agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
|
agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
|
||||||
|
@ -61,6 +61,9 @@ protected:
|
||||||
|
|
||||||
/// A marker has been updated in some way.
|
/// A marker has been updated in some way.
|
||||||
agi::signal::Signal<AudioMarker*> AnnounceMarkerMoved;
|
agi::signal::Signal<AudioMarker*> AnnounceMarkerMoved;
|
||||||
|
|
||||||
|
/// A label has been updated in some way.
|
||||||
|
agi::signal::Signal<AudioLabel*> AnnounceLabelChanged;
|
||||||
public:
|
public:
|
||||||
/// @brief Get any warning message to show in the audio display
|
/// @brief Get any warning message to show in the audio display
|
||||||
/// @return The warning message to show, may be empty if there is none
|
/// @return The warning message to show, may be empty if there is none
|
||||||
|
@ -144,6 +147,7 @@ public:
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
||||||
|
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,8 @@ public:
|
||||||
wxString GetWarningMessage() const;
|
wxString GetWarningMessage() const;
|
||||||
SampleRange GetIdealVisibleSampleRange() const;
|
SampleRange GetIdealVisibleSampleRange() const;
|
||||||
SampleRange GetPrimaryPlaybackRange() const;
|
SampleRange GetPrimaryPlaybackRange() const;
|
||||||
bool HasLabels() const;
|
bool HasLabels() const { return false; }
|
||||||
|
void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const { }
|
||||||
void Next();
|
void Next();
|
||||||
void Prev();
|
void Prev();
|
||||||
void Commit();
|
void Commit();
|
||||||
|
@ -348,13 +349,6 @@ SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool AudioTimingControllerDialogue::HasLabels() const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Next()
|
void AudioTimingControllerDialogue::Next()
|
||||||
{
|
{
|
||||||
selection_controller->NextLine();
|
selection_controller->NextLine();
|
||||||
|
|
Loading…
Reference in a new issue