Add support for labels in the audio display

Originally committed to SVN as r5589.
This commit is contained in:
Thomas Goyne 2011-09-15 05:16:26 +00:00
parent 3e708eab10
commit 3f05fe6b3e
5 changed files with 131 additions and 65 deletions

View file

@ -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
{ {

View file

@ -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

View file

@ -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);

View file

@ -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)
}; };

View file

@ -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();