forked from mia/Aegisub
Get audio styling ranges from the timing controller. Based on a patch by jfs.
Originally committed to SVN as r5878.
This commit is contained in:
parent
262d5195c5
commit
8d28b44773
13 changed files with 279 additions and 198 deletions
|
@ -336,17 +336,15 @@ 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(bind(std::tr1::ref(AnnounceMarkerMoved)));
|
||||||
timing_controller->AddLabelChangedListener(std::tr1::bind(std::tr1::ref(AnnounceLabelChanged)));
|
timing_controller->AddLabelChangedListener(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(bind(std::tr1::ref(AnnounceStyleRangesChanged)));
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnounceTimingControllerChanged();
|
AnnounceTimingControllerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioController::OnTimingControllerUpdatedPrimaryRange()
|
void AudioController::OnTimingControllerUpdatedPrimaryRange()
|
||||||
{
|
{
|
||||||
if (playback_mode == PM_PrimaryRange)
|
if (playback_mode == PM_PrimaryRange)
|
||||||
|
@ -357,12 +355,6 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange()
|
||||||
AnnounceSelectionChanged();
|
AnnounceSelectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioController::OnTimingControllerUpdatedStyleRanges()
|
|
||||||
{
|
|
||||||
/// @todo redraw and stuff, probably
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioController::OnSubtitlesSave()
|
void AudioController::OnSubtitlesSave()
|
||||||
{
|
{
|
||||||
if (IsAudioOpen())
|
if (IsAudioOpen())
|
||||||
|
|
|
@ -201,6 +201,9 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
|
||||||
/// The selected time range changed
|
/// The selected time range changed
|
||||||
agi::signal::Signal<> AnnounceSelectionChanged;
|
agi::signal::Signal<> AnnounceSelectionChanged;
|
||||||
|
|
||||||
|
/// The styling ranges have been updated by the timing controller
|
||||||
|
agi::signal::Signal<> AnnounceStyleRangesChanged;
|
||||||
|
|
||||||
/// The audio output object
|
/// The audio output object
|
||||||
AudioPlayer *player;
|
AudioPlayer *player;
|
||||||
|
|
||||||
|
@ -390,6 +393,7 @@ public:
|
||||||
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop, AddPlaybackStopListener)
|
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop, AddPlaybackStopListener)
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionChangedListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionChangedListener)
|
||||||
|
DEFINE_SIGNAL_ADDERS(AnnounceStyleRangesChanged, AddStyleRangesChangedListener)
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @class AudioMarker
|
/// @class AudioMarker
|
||||||
|
|
|
@ -494,8 +494,54 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AudioStyleRangeMerger : public AudioRenderingStyleRanges {
|
||||||
|
typedef std::map<int64_t, AudioRenderingStyle> style_map;
|
||||||
|
public:
|
||||||
|
typedef style_map::iterator iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
style_map points;
|
||||||
|
|
||||||
|
void Split(int64_t point)
|
||||||
|
{
|
||||||
|
iterator it = points.lower_bound(point);
|
||||||
|
if (it == points.end() || it->first != point)
|
||||||
|
{
|
||||||
|
assert(it != points.begin());
|
||||||
|
points[point] = (--it)->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Restyle(int64_t start, int64_t end, AudioRenderingStyle style)
|
||||||
|
{
|
||||||
|
assert(points.lower_bound(end) != points.end());
|
||||||
|
for (iterator pt = points.lower_bound(start); pt->first < end; ++pt)
|
||||||
|
{
|
||||||
|
if (style > pt->second)
|
||||||
|
pt->second = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioStyleRangeMerger()
|
||||||
|
{
|
||||||
|
points[0] = AudioStyle_Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRange(int64_t start, int64_t end, AudioRenderingStyle style)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
if (end < start) return;
|
||||||
|
|
||||||
|
Split(start);
|
||||||
|
Split(end);
|
||||||
|
Restyle(start, end, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() { return points.begin(); }
|
||||||
|
iterator end() { return points.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context)
|
AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context)
|
||||||
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS|wxBORDER_SIMPLE)
|
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS|wxBORDER_SIMPLE)
|
||||||
|
@ -506,8 +552,9 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::C
|
||||||
, scrollbar(new AudioDisplayScrollbar(this))
|
, scrollbar(new AudioDisplayScrollbar(this))
|
||||||
, timeline(new AudioDisplayTimeline(this))
|
, timeline(new AudioDisplayTimeline(this))
|
||||||
, dragged_object(0)
|
, dragged_object(0)
|
||||||
, old_selection(0, 0)
|
|
||||||
{
|
{
|
||||||
|
style_ranges[0] = AudioStyle_Normal;
|
||||||
|
|
||||||
scroll_left = 0;
|
scroll_left = 0;
|
||||||
pixel_audio_width = 0;
|
pixel_audio_width = 0;
|
||||||
scale_amplitude = 1.0;
|
scale_amplitude = 1.0;
|
||||||
|
@ -521,6 +568,7 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::C
|
||||||
slots.push_back(controller->AddTimingControllerListener(&AudioDisplay::Refresh, this, true, (const wxRect*)0));
|
slots.push_back(controller->AddTimingControllerListener(&AudioDisplay::Refresh, this, true, (const wxRect*)0));
|
||||||
slots.push_back(controller->AddMarkerMovedListener(&AudioDisplay::OnMarkerMoved, this));
|
slots.push_back(controller->AddMarkerMovedListener(&AudioDisplay::OnMarkerMoved, this));
|
||||||
slots.push_back(controller->AddSelectionChangedListener(&AudioDisplay::OnSelectionChanged, this));
|
slots.push_back(controller->AddSelectionChangedListener(&AudioDisplay::OnSelectionChanged, this));
|
||||||
|
slots.push_back(controller->AddStyleRangesChangedListener(&AudioDisplay::OnStyleRangesChanged, this));
|
||||||
|
|
||||||
OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this);
|
OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this);
|
||||||
|
|
||||||
|
@ -764,15 +812,9 @@ BEGIN_EVENT_TABLE(AudioDisplay, wxWindow)
|
||||||
EVT_SET_FOCUS(AudioDisplay::OnFocus)
|
EVT_SET_FOCUS(AudioDisplay::OnFocus)
|
||||||
EVT_KILL_FOCUS(AudioDisplay::OnFocus)
|
EVT_KILL_FOCUS(AudioDisplay::OnFocus)
|
||||||
EVT_KEY_DOWN(AudioDisplay::OnKeyDown)
|
EVT_KEY_DOWN(AudioDisplay::OnKeyDown)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE();
|
||||||
|
|
||||||
|
|
||||||
struct RedrawSubregion {
|
|
||||||
int x1, x2;
|
|
||||||
bool selected;
|
|
||||||
RedrawSubregion(int x1, int x2, bool selected) : x1(x1), x2(x2), selected(selected) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
void AudioDisplay::OnPaint(wxPaintEvent& event)
|
void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
{
|
{
|
||||||
wxAutoBufferedPaintDC dc(this);
|
wxAutoBufferedPaintDC dc(this);
|
||||||
|
@ -793,11 +835,6 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
bool redraw_scrollbar = false;
|
bool redraw_scrollbar = false;
|
||||||
bool redraw_timeline = false;
|
bool redraw_timeline = false;
|
||||||
|
|
||||||
/// @todo Get rendering style ranges from timing controller instead
|
|
||||||
SampleRange sel_samples(controller->GetPrimaryPlaybackRange());
|
|
||||||
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
|
|
||||||
int selection_end = AbsoluteXFromSamples(sel_samples.end());
|
|
||||||
|
|
||||||
wxPoint client_org = GetClientAreaOrigin();
|
wxPoint client_org = GetClientAreaOrigin();
|
||||||
for (wxRegionIterator region(GetUpdateRegion()); region; ++region)
|
for (wxRegionIterator region(GetUpdateRegion()); region; ++region)
|
||||||
{
|
{
|
||||||
|
@ -816,38 +853,31 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// p1 -> p2 = before selection
|
SampleRange updsamples(
|
||||||
// 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int foot_size = 6;
|
|
||||||
SampleRange updrectsamples(
|
|
||||||
SamplesFromRelativeX(updrect.x - foot_size),
|
SamplesFromRelativeX(updrect.x - foot_size),
|
||||||
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
||||||
|
|
||||||
|
std::map<int64_t, int>::iterator pt = style_ranges.upper_bound(updsamples.begin());
|
||||||
|
std::map<int64_t, int>::iterator pe = style_ranges.upper_bound(updsamples.end());
|
||||||
|
|
||||||
|
if (pt != style_ranges.begin())
|
||||||
|
--pt;
|
||||||
|
|
||||||
|
while (pt != pe)
|
||||||
|
{
|
||||||
|
AudioRenderingStyle range_style = static_cast<AudioRenderingStyle>(pt->second);
|
||||||
|
int range_x1 = std::max(updrect.x, RelativeXFromSamples(pt->first));
|
||||||
|
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromSamples(pt->first);
|
||||||
|
|
||||||
|
if (range_x2 > range_x1)
|
||||||
|
{
|
||||||
|
audio_renderer->Render(dc, wxPoint(range_x1, audio_top), range_x1, range_x2 - range_x1, range_style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw markers on top of it all
|
// Draw markers on top of it all
|
||||||
AudioMarkerVector markers;
|
AudioMarkerVector markers;
|
||||||
controller->GetMarkers(updrectsamples, markers);
|
controller->GetMarkers(updsamples, markers);
|
||||||
wxDCPenChanger pen_retainer(dc, wxPen());
|
wxDCPenChanger pen_retainer(dc, wxPen());
|
||||||
wxDCBrushChanger brush_retainer(dc, wxBrush());
|
wxDCBrushChanger brush_retainer(dc, wxBrush());
|
||||||
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
||||||
|
@ -876,7 +906,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
|
|
||||||
// Draw labels
|
// Draw labels
|
||||||
std::vector<AudioLabelProvider::AudioLabel> labels;
|
std::vector<AudioLabelProvider::AudioLabel> labels;
|
||||||
controller->GetLabels(updrectsamples, labels);
|
controller->GetLabels(updsamples, labels);
|
||||||
if (!labels.empty())
|
if (!labels.empty())
|
||||||
{
|
{
|
||||||
wxDCFontChanger fc(dc);
|
wxDCFontChanger fc(dc);
|
||||||
|
@ -943,7 +973,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
||||||
track_cursor_label_rect.SetPosition(label_pos);
|
track_cursor_label_rect.SetPosition(label_pos);
|
||||||
track_cursor_label_rect.SetSize(label_size);
|
track_cursor_label_rect.SetSize(label_size);
|
||||||
if (need_extra_redraw)
|
if (need_extra_redraw)
|
||||||
RefreshRect(track_cursor_label_rect);
|
RefreshRect(track_cursor_label_rect, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,11 +1006,11 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
|
||||||
int old_pos = track_cursor_pos;
|
int old_pos = track_cursor_pos;
|
||||||
track_cursor_pos = new_pos;
|
track_cursor_pos = new_pos;
|
||||||
|
|
||||||
RefreshRect(wxRect(old_pos - scroll_left - 0, audio_top, 1, audio_height));
|
RefreshRect(wxRect(old_pos - scroll_left - 0, audio_top, 1, audio_height), false);
|
||||||
RefreshRect(wxRect(new_pos - scroll_left - 0, audio_top, 1, audio_height));
|
RefreshRect(wxRect(new_pos - scroll_left - 0, audio_top, 1, audio_height), false);
|
||||||
|
|
||||||
// Make sure the old label gets cleared away
|
// Make sure the old label gets cleared away
|
||||||
RefreshRect(track_cursor_label_rect);
|
RefreshRect(track_cursor_label_rect, false);
|
||||||
|
|
||||||
if (show_time)
|
if (show_time)
|
||||||
{
|
{
|
||||||
|
@ -988,7 +1018,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
|
||||||
new_label_time.SetMS(controller->MillisecondsFromSamples(SamplesFromAbsoluteX(track_cursor_pos)));
|
new_label_time.SetMS(controller->MillisecondsFromSamples(SamplesFromAbsoluteX(track_cursor_pos)));
|
||||||
track_cursor_label = new_label_time.GetASSFormated();
|
track_cursor_label = new_label_time.GetASSFormated();
|
||||||
track_cursor_label_rect.x += new_pos - old_pos;
|
track_cursor_label_rect.x += new_pos - old_pos;
|
||||||
RefreshRect(track_cursor_label_rect);
|
RefreshRect(track_cursor_label_rect, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1148,7 +1178,7 @@ void AudioDisplay::OnSize(wxSizeEvent &event)
|
||||||
void AudioDisplay::OnFocus(wxFocusEvent &event)
|
void AudioDisplay::OnFocus(wxFocusEvent &event)
|
||||||
{
|
{
|
||||||
// The scrollbar indicates focus so repaint that
|
// The scrollbar indicates focus so repaint that
|
||||||
RefreshRect(scrollbar->GetBounds());
|
RefreshRect(scrollbar->GetBounds(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1174,42 +1204,76 @@ void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
|
||||||
|
|
||||||
void AudioDisplay::OnSelectionChanged()
|
void AudioDisplay::OnSelectionChanged()
|
||||||
{
|
{
|
||||||
/// @todo Handle rendering style ranges from timing controller instead
|
|
||||||
SampleRange sel(controller->GetPrimaryPlaybackRange());
|
SampleRange sel(controller->GetPrimaryPlaybackRange());
|
||||||
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
|
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
|
||||||
|
|
||||||
if (OPT_GET("Audio/Auto/Scroll")->GetBool())
|
if (OPT_GET("Audio/Auto/Scroll")->GetBool())
|
||||||
|
{
|
||||||
ScrollSampleRangeInView(sel);
|
ScrollSampleRangeInView(sel);
|
||||||
|
|
||||||
int s1 = RelativeXFromSamples(sel.begin());
|
|
||||||
int e1 = RelativeXFromSamples(sel.end());
|
|
||||||
int s2 = RelativeXFromSamples(old_selection.begin());
|
|
||||||
int e2 = RelativeXFromSamples(old_selection.end());
|
|
||||||
|
|
||||||
if (sel.overlaps(old_selection))
|
|
||||||
{
|
|
||||||
// Only redraw the parts of the selection that changed, to avoid flicker
|
|
||||||
if (s1 != s2)
|
|
||||||
{
|
|
||||||
RefreshRect(wxRect(std::min(s1, s2)-10, audio_top, abs(s1-s2)+20, audio_height));
|
|
||||||
}
|
}
|
||||||
if (e1 != e2)
|
|
||||||
|
RefreshRect(scrollbar->GetBounds(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioDisplay::OnStyleRangesChanged()
|
||||||
|
{
|
||||||
|
AudioStyleRangeMerger asrm;
|
||||||
|
controller->GetTimingController()->GetRenderingStyles(asrm);
|
||||||
|
|
||||||
|
std::map<int64_t, int> old_style_ranges;
|
||||||
|
swap(old_style_ranges, style_ranges);
|
||||||
|
style_ranges.insert(asrm.begin(), asrm.end());
|
||||||
|
|
||||||
|
std::map<int64_t, int>::iterator old_style_it = old_style_ranges.begin();
|
||||||
|
std::map<int64_t, int>::iterator new_style_it = style_ranges.begin();
|
||||||
|
|
||||||
|
int old_style = old_style_it->second;
|
||||||
|
int new_style = new_style_it->second;
|
||||||
|
int64_t range_start = 0;
|
||||||
|
|
||||||
|
// Repaint each range which has changed
|
||||||
|
while (old_style_it != old_style_ranges.end() || new_style_it != style_ranges.end())
|
||||||
{
|
{
|
||||||
RefreshRect(wxRect(std::min(e1, e2)-10, audio_top, abs(e1-e2)+20, audio_height));
|
if (new_style_it == style_ranges.end() || (old_style_it != old_style_ranges.end() && old_style_it->first <= new_style_it->first))
|
||||||
}
|
{
|
||||||
|
if (old_style != new_style)
|
||||||
|
Redraw(range_start, old_style_it->first);
|
||||||
|
old_style = old_style_it->second;
|
||||||
|
range_start = old_style_it->first;
|
||||||
|
++old_style_it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RefreshRect(wxRect(s1 - 10, audio_top, e1 + 20, audio_height));
|
if (old_style != new_style)
|
||||||
RefreshRect(wxRect(s2 - 10, audio_top, e2 + 20, audio_height));
|
Redraw(range_start, new_style_it->first);
|
||||||
|
new_style = new_style_it->second;
|
||||||
|
range_start = new_style_it->first;
|
||||||
|
++new_style_it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshRect(scrollbar->GetBounds());
|
// Fill in the last style range
|
||||||
|
if (old_style != new_style)
|
||||||
|
{
|
||||||
|
Redraw(range_start, SamplesFromRelativeX(GetClientSize().GetWidth()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
old_selection = sel;
|
void AudioDisplay::Redraw(int64_t sample_start, int64_t sample_end)
|
||||||
|
{
|
||||||
|
if (sample_start == sample_end) return;
|
||||||
|
|
||||||
|
sample_start = RelativeXFromSamples(sample_start) - foot_size;
|
||||||
|
sample_end = RelativeXFromSamples(sample_end) + foot_size;
|
||||||
|
|
||||||
|
if (sample_end >= 0 && sample_start <= GetClientSize().GetWidth())
|
||||||
|
{
|
||||||
|
RefreshRect(wxRect(sample_start, audio_top, sample_end, audio_height), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDisplay::OnMarkerMoved()
|
void AudioDisplay::OnMarkerMoved()
|
||||||
{
|
{
|
||||||
/// @todo investigate if it's worth refreshing only the changed spots
|
/// @todo investigate if it's worth refreshing only the changed spots
|
||||||
RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height));
|
RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ public:
|
||||||
/// timing controller. The audio display also renders audio according to the audio controller
|
/// timing controller. The audio display also renders audio according to the audio controller
|
||||||
/// and the timing controller, using an audio renderer instance.
|
/// and the timing controller, using an audio renderer instance.
|
||||||
class AudioDisplay: public wxWindow {
|
class AudioDisplay: public wxWindow {
|
||||||
private:
|
|
||||||
std::list<agi::signal::Connection> slots;
|
std::list<agi::signal::Connection> slots;
|
||||||
agi::Context *context;
|
agi::Context *context;
|
||||||
|
|
||||||
|
@ -146,6 +146,8 @@ private:
|
||||||
/// Height of main audio area in pixels
|
/// Height of main audio area in pixels
|
||||||
int audio_height;
|
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
|
/// Zoom level given as a number, see SetZoomLevel for details
|
||||||
int zoom_level;
|
int zoom_level;
|
||||||
|
@ -163,8 +165,8 @@ private:
|
||||||
/// @brief Remove the tracking cursor from the display
|
/// @brief Remove the tracking cursor from the display
|
||||||
void RemoveTrackCursor();
|
void RemoveTrackCursor();
|
||||||
|
|
||||||
/// Previous audio selection for optimizing redraw when selection changes
|
/// Previous style ranges for optimizing redraw when ranges change
|
||||||
SampleRange old_selection;
|
std::map<int64_t, int> style_ranges;
|
||||||
|
|
||||||
/// @brief Reload all rendering settings from Options and reset caches
|
/// @brief Reload all rendering settings from Options and reset caches
|
||||||
///
|
///
|
||||||
|
@ -172,6 +174,11 @@ private:
|
||||||
/// in Options and need to be reloaded to take effect.
|
/// in Options and need to be reloaded to take effect.
|
||||||
void ReloadRenderingSettings();
|
void ReloadRenderingSettings();
|
||||||
|
|
||||||
|
/// @brief Repaint a range of samples
|
||||||
|
/// @param sample_start First sample to repaint
|
||||||
|
/// @param sample_end Last sample to repaint
|
||||||
|
void Redraw(int64_t sample_start, int64_t sample_end);
|
||||||
|
|
||||||
/// wxWidgets paint event
|
/// wxWidgets paint event
|
||||||
void OnPaint(wxPaintEvent &event);
|
void OnPaint(wxPaintEvent &event);
|
||||||
/// wxWidgets mouse input event
|
/// wxWidgets mouse input event
|
||||||
|
@ -187,10 +194,10 @@ private:
|
||||||
void OnAudioOpen(AudioProvider *provider);
|
void OnAudioOpen(AudioProvider *provider);
|
||||||
void OnPlaybackPosition(int64_t sample_position);
|
void OnPlaybackPosition(int64_t sample_position);
|
||||||
void OnSelectionChanged();
|
void OnSelectionChanged();
|
||||||
|
void OnStyleRangesChanged();
|
||||||
void OnMarkerMoved();
|
void OnMarkerMoved();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context);
|
AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context);
|
||||||
~AudioDisplay();
|
~AudioDisplay();
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,12 @@
|
||||||
#include "audio_renderer.h"
|
#include "audio_renderer.h"
|
||||||
#include "include/aegisub/audio_provider.h"
|
#include "include/aegisub/audio_provider.h"
|
||||||
|
|
||||||
#undef min
|
template<class C, class F> static void for_each(C &container, F const& func)
|
||||||
#undef max
|
{
|
||||||
|
std::for_each(container.begin(), container.end(), func);
|
||||||
|
}
|
||||||
|
|
||||||
|
using std::tr1::placeholders::_1;
|
||||||
|
|
||||||
AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer)
|
AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer)
|
||||||
{
|
{
|
||||||
|
@ -80,19 +83,18 @@ size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const
|
||||||
|
|
||||||
AudioRenderer::AudioRenderer()
|
AudioRenderer::AudioRenderer()
|
||||||
: cache_bitmap_width(32) // arbitrary value for now
|
: cache_bitmap_width(32) // arbitrary value for now
|
||||||
, bitmaps_normal(256, AudioRendererBitmapCacheBitmapFactory(this))
|
|
||||||
, bitmaps_selected(256, AudioRendererBitmapCacheBitmapFactory(this))
|
|
||||||
, cache_bitmap_maxsize(0)
|
, cache_bitmap_maxsize(0)
|
||||||
, cache_renderer_maxsize(0)
|
, cache_renderer_maxsize(0)
|
||||||
, renderer(0)
|
, renderer(0)
|
||||||
, provider(0)
|
, provider(0)
|
||||||
{
|
{
|
||||||
|
bitmaps.resize(AudioStyle_MAX, AudioRendererBitmapCache(256, AudioRendererBitmapCacheBitmapFactory(this)));
|
||||||
|
|
||||||
// Make sure there's *some* values for those fields, and in the caches
|
// Make sure there's *some* values for those fields, and in the caches
|
||||||
SetSamplesPerPixel(1);
|
SetSamplesPerPixel(1);
|
||||||
SetHeight(1);
|
SetHeight(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AudioRenderer::~AudioRenderer()
|
AudioRenderer::~AudioRenderer()
|
||||||
{
|
{
|
||||||
// Nothing to do, everything is auto-allocated
|
// Nothing to do, everything is auto-allocated
|
||||||
|
@ -183,26 +185,22 @@ void AudioRenderer::ResetBlockCount()
|
||||||
{
|
{
|
||||||
size_t rendered_width = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
|
size_t rendered_width = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
|
||||||
cache_numblocks = rendered_width / cache_bitmap_width;
|
cache_numblocks = rendered_width / cache_bitmap_width;
|
||||||
bitmaps_normal.SetBlockCount(cache_numblocks);
|
for_each(bitmaps, bind(&AudioRendererBitmapCache::SetBlockCount, _1, cache_numblocks));
|
||||||
bitmaps_selected.SetBlockCount(cache_numblocks);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wxBitmap AudioRenderer::GetCachedBitmap(int i, bool selected)
|
wxBitmap AudioRenderer::GetCachedBitmap(int i, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
assert(provider);
|
assert(provider);
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
// Pick the cache to use
|
|
||||||
AudioRendererBitmapCache *cache = selected ? &bitmaps_selected : &bitmaps_normal;
|
|
||||||
|
|
||||||
bool created = false;
|
bool created = false;
|
||||||
wxBitmap *bmp = cache->Get(i, &created);
|
wxBitmap *bmp = bitmaps[style].Get(i, &created);
|
||||||
assert(bmp);
|
assert(bmp);
|
||||||
if (created)
|
if (created)
|
||||||
{
|
{
|
||||||
renderer->Render(*bmp, i*cache_bitmap_width, selected);
|
renderer->Render(*bmp, i*cache_bitmap_width, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bmp->IsOk());
|
assert(bmp->IsOk());
|
||||||
|
@ -210,7 +208,7 @@ wxBitmap AudioRenderer::GetCachedBitmap(int i, bool selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool selected)
|
void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
assert(start >= 0);
|
assert(start >= 0);
|
||||||
|
|
||||||
|
@ -257,7 +255,7 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
||||||
else if (firstbitmap == lastbitmap)
|
else if (firstbitmap == lastbitmap)
|
||||||
{
|
{
|
||||||
const int renderwidth = lastbitmapoffset - firstbitmapoffset;
|
const int renderwidth = lastbitmapoffset - firstbitmapoffset;
|
||||||
wxBitmap bmp = GetCachedBitmap(firstbitmap, selected);
|
wxBitmap bmp = GetCachedBitmap(firstbitmap, style);
|
||||||
wxMemoryDC bmpdc(bmp);
|
wxMemoryDC bmpdc(bmp);
|
||||||
dc.Blit(origin, wxSize(renderwidth, pixel_height), &bmpdc, wxPoint(firstbitmapoffset, 0));
|
dc.Blit(origin, wxSize(renderwidth, pixel_height), &bmpdc, wxPoint(firstbitmapoffset, 0));
|
||||||
origin.x += renderwidth;
|
origin.x += renderwidth;
|
||||||
|
@ -267,7 +265,7 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
||||||
wxBitmap bmp;
|
wxBitmap bmp;
|
||||||
|
|
||||||
{
|
{
|
||||||
bmp = GetCachedBitmap(firstbitmap, selected);
|
bmp = GetCachedBitmap(firstbitmap, style);
|
||||||
// Can't use dc.DrawBitmap here because we need to clip the bitmap
|
// Can't use dc.DrawBitmap here because we need to clip the bitmap
|
||||||
wxMemoryDC bmpdc(bmp);
|
wxMemoryDC bmpdc(bmp);
|
||||||
dc.Blit(origin, wxSize(cache_bitmap_width-firstbitmapoffset, pixel_height),
|
dc.Blit(origin, wxSize(cache_bitmap_width-firstbitmapoffset, pixel_height),
|
||||||
|
@ -277,13 +275,13 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
||||||
|
|
||||||
for (int i = firstbitmap+1; i < lastbitmap; ++i)
|
for (int i = firstbitmap+1; i < lastbitmap; ++i)
|
||||||
{
|
{
|
||||||
bmp = GetCachedBitmap(i, selected);
|
bmp = GetCachedBitmap(i, style);
|
||||||
dc.DrawBitmap(bmp, origin);
|
dc.DrawBitmap(bmp, origin);
|
||||||
origin.x += cache_bitmap_width;
|
origin.x += cache_bitmap_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
bmp = GetCachedBitmap(lastbitmap, selected);
|
bmp = GetCachedBitmap(lastbitmap, style);
|
||||||
// We also need clipping here
|
// We also need clipping here
|
||||||
wxMemoryDC bmpdc(bmp);
|
wxMemoryDC bmpdc(bmp);
|
||||||
dc.Blit(origin, wxSize(lastbitmapoffset+1, pixel_height), &bmpdc, wxPoint(0, 0));
|
dc.Blit(origin, wxSize(lastbitmapoffset+1, pixel_height), &bmpdc, wxPoint(0, 0));
|
||||||
|
@ -294,26 +292,20 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool
|
||||||
// Now render blank audio from origin to end
|
// Now render blank audio from origin to end
|
||||||
if (origin.x < lastx)
|
if (origin.x < lastx)
|
||||||
{
|
{
|
||||||
renderer->RenderBlank(dc, wxRect(origin.x-1, origin.y, lastx-origin.x+1, pixel_height), selected);
|
renderer->RenderBlank(dc, wxRect(origin.x-1, origin.y, lastx-origin.x+1, pixel_height), style);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected)
|
bitmaps[style].Age(cache_bitmap_maxsize);
|
||||||
bitmaps_selected.Age(cache_bitmap_maxsize);
|
|
||||||
else
|
|
||||||
bitmaps_normal.Age(cache_bitmap_maxsize);
|
|
||||||
renderer->AgeCache(cache_renderer_maxsize);
|
renderer->AgeCache(cache_renderer_maxsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioRenderer::Invalidate()
|
void AudioRenderer::Invalidate()
|
||||||
{
|
{
|
||||||
bitmaps_normal.Age(0);
|
for_each(bitmaps, bind(&AudioRendererBitmapCache::Age, _1, 0));
|
||||||
bitmaps_selected.Age(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioRendererBitmapProvider::SetProvider(AudioProvider *_provider)
|
void AudioRendererBitmapProvider::SetProvider(AudioProvider *_provider)
|
||||||
{
|
{
|
||||||
if (provider == _provider) return;
|
if (provider == _provider) return;
|
||||||
|
@ -342,4 +334,3 @@ void AudioRendererBitmapProvider::SetAmplitudeScale(float _amplitude_scale)
|
||||||
|
|
||||||
OnSetAmplitudeScale();
|
OnSetAmplitudeScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
#ifndef AGI_PRE
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <wx/dc.h>
|
#include <wx/dc.h>
|
||||||
#include <wx/gdicmn.h>
|
#include <wx/gdicmn.h>
|
||||||
|
@ -52,6 +53,41 @@ class AudioProvider;
|
||||||
class AudioRendererBitmapProvider;
|
class AudioRendererBitmapProvider;
|
||||||
class AudioRenderer;
|
class AudioRenderer;
|
||||||
|
|
||||||
|
/// @brief Styles audio may be rendered in
|
||||||
|
///
|
||||||
|
/// The constants are ordered by priority:
|
||||||
|
/// Selected has highest priority and should overlap active, which should
|
||||||
|
/// overlap inactive, which should overlap normal regions.
|
||||||
|
enum AudioRenderingStyle {
|
||||||
|
/// Regular audio with no special properties
|
||||||
|
AudioStyle_Normal,
|
||||||
|
/// Audio belonging to objects that can not be manipulated currently
|
||||||
|
AudioStyle_Inactive,
|
||||||
|
/// Audio that may be manipulated indirectly, usually part of selected lines
|
||||||
|
AudioStyle_Active,
|
||||||
|
/// Primary selection for work, usually coinciding with the primary playback range
|
||||||
|
AudioStyle_Selected,
|
||||||
|
/// Number of audio styles
|
||||||
|
AudioStyle_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @class AudioRenderingStyleRanges
|
||||||
|
/// @brief Abstract container for ranges of audio rendering styles
|
||||||
|
///
|
||||||
|
/// Interface for producers of audio rendering ranges, consumers should implement
|
||||||
|
/// this interface for objects to pass to producers.
|
||||||
|
class AudioRenderingStyleRanges {
|
||||||
|
public:
|
||||||
|
/// @brief Add a range to the line
|
||||||
|
/// @param start First sample index in range
|
||||||
|
/// @param end One past last sample index in range
|
||||||
|
/// @param style Style of the range added
|
||||||
|
virtual void AddRange(int64_t start, int64_t end, AudioRenderingStyle style) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @class AudioRendererBitmapCacheBitmapFactory
|
/// @class AudioRendererBitmapCacheBitmapFactory
|
||||||
/// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer
|
/// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer
|
||||||
struct AudioRendererBitmapCacheBitmapFactory {
|
struct AudioRendererBitmapCacheBitmapFactory {
|
||||||
|
@ -104,10 +140,8 @@ class AudioRenderer {
|
||||||
/// Width of bitmaps to store in cache
|
/// Width of bitmaps to store in cache
|
||||||
const int cache_bitmap_width;
|
const int cache_bitmap_width;
|
||||||
|
|
||||||
/// Cached bitmaps for normal audio ranges
|
/// Cached bitmaps for audio ranges
|
||||||
AudioRendererBitmapCache bitmaps_normal;
|
std::vector<AudioRendererBitmapCache> bitmaps;
|
||||||
/// Cached bitmaps for marked (selected) audio ranges
|
|
||||||
AudioRendererBitmapCache bitmaps_selected;
|
|
||||||
/// Number of blocks in the bitmap caches
|
/// Number of blocks in the bitmap caches
|
||||||
size_t cache_numblocks;
|
size_t cache_numblocks;
|
||||||
/// The maximum allowed size of each bitmap cache, in bytes
|
/// The maximum allowed size of each bitmap cache, in bytes
|
||||||
|
@ -123,12 +157,12 @@ class AudioRenderer {
|
||||||
|
|
||||||
/// @brief Make sure bitmap index i is in cache
|
/// @brief Make sure bitmap index i is in cache
|
||||||
/// @param i Index of bitmap to get into cache
|
/// @param i Index of bitmap to get into cache
|
||||||
/// @param selected Whether to get a "selected" state bitmap or not
|
/// @param style Rendering style required for bitmap
|
||||||
/// @return The requested bitmap
|
/// @return The requested bitmap
|
||||||
///
|
///
|
||||||
/// Will attempt retrieving the requested bitmap from the cache, creating it
|
/// Will attempt retrieving the requested bitmap from the cache, creating it
|
||||||
/// if the cache doesn't have it.
|
/// if the cache doesn't have it.
|
||||||
wxBitmap GetCachedBitmap(int i, bool selected);
|
wxBitmap GetCachedBitmap(int i, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Update the block count in the bitmap caches
|
/// @brief Update the block count in the bitmap caches
|
||||||
///
|
///
|
||||||
|
@ -230,11 +264,11 @@ public:
|
||||||
/// @param origin Top left corner to render at, in the DC's coordinates
|
/// @param origin Top left corner to render at, in the DC's coordinates
|
||||||
/// @param start First pixel from beginning of the audio stream to render
|
/// @param start First pixel from beginning of the audio stream to render
|
||||||
/// @param length Number of pixels of audio to render
|
/// @param length Number of pixels of audio to render
|
||||||
/// @param selected Whether to render the audio as being selected or not
|
/// @param style Style to render audio in
|
||||||
///
|
///
|
||||||
/// The first audio sample rendered is start*pixel_samples, and the number
|
/// The first audio sample rendered is start*pixel_samples, and the number
|
||||||
/// of audio samples rendered is length*pixel_samples.
|
/// of audio samples rendered is length*pixel_samples.
|
||||||
void Render(wxDC &dc, wxPoint origin, int start, int length, bool selected);
|
void Render(wxDC &dc, wxPoint origin, int start, int length, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Invalidate all cached data
|
/// @brief Invalidate all cached data
|
||||||
///
|
///
|
||||||
|
@ -286,20 +320,20 @@ public:
|
||||||
/// @brief Rendering function
|
/// @brief Rendering function
|
||||||
/// @param bmp Bitmap to render to
|
/// @param bmp Bitmap to render to
|
||||||
/// @param start First pixel from beginning of the audio stream to render
|
/// @param start First pixel from beginning of the audio stream to render
|
||||||
/// @param selected Whether to render the audio as being selected or not
|
/// @param style Style to render audio in
|
||||||
///
|
///
|
||||||
/// Deriving classes must implement this method. The bitmap in bmp holds
|
/// Deriving classes must implement this method. The bitmap in bmp holds
|
||||||
/// the width and height to render.
|
/// the width and height to render.
|
||||||
virtual void Render(wxBitmap &bmp, int start, bool selected) = 0;
|
virtual void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) = 0;
|
||||||
|
|
||||||
/// @brief Blank audio rendering function
|
/// @brief Blank audio rendering function
|
||||||
/// @param dc The device context to render to
|
/// @param dc The device context to render to
|
||||||
/// @param rect The rectangle to fill with the image of blank audio
|
/// @param rect The rectangle to fill with the image of blank audio
|
||||||
/// @param selected Whether to render as being selected or not
|
/// @param style Style to render audio in
|
||||||
///
|
///
|
||||||
/// Deriving classes must implement this method. The rectangle has the height
|
/// Deriving classes must implement this method. The rectangle has the height
|
||||||
/// of the entire canvas the audio is being rendered in.
|
/// of the entire canvas the audio is being rendered in.
|
||||||
virtual void RenderBlank(wxDC &dc, const wxRect &rect, bool selected) = 0;
|
virtual void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) = 0;
|
||||||
|
|
||||||
/// @brief Change audio provider
|
/// @brief Change audio provider
|
||||||
/// @param provider Audio provider to change to
|
/// @param provider Audio provider to change to
|
||||||
|
|
|
@ -264,7 +264,7 @@ void AudioSpectrumRenderer::FillBlock(size_t block_index, float *block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return;
|
return;
|
||||||
|
@ -284,7 +284,7 @@ void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
||||||
ptrdiff_t stride = img.GetWidth()*3;
|
ptrdiff_t stride = img.GetWidth()*3;
|
||||||
int imgheight = img.GetHeight();
|
int imgheight = img.GetHeight();
|
||||||
|
|
||||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal;
|
||||||
|
|
||||||
/// @todo Make minband and maxband configurable
|
/// @todo Make minband and maxband configurable
|
||||||
int minband = 0;
|
int minband = 0;
|
||||||
|
@ -341,10 +341,10 @@ void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioSpectrumRenderer::RenderBlank(wxDC &dc, const wxRect &rect, bool selected)
|
void AudioSpectrumRenderer::RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
// Get the colour of silence
|
// Get the colour of silence
|
||||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal;
|
||||||
unsigned char color_raw[4];
|
unsigned char color_raw[4];
|
||||||
pal->map(0.0, color_raw);
|
pal->map(0.0, color_raw);
|
||||||
wxColour col(color_raw[0], color_raw[1], color_raw[2]);
|
wxColour col(color_raw[0], color_raw[1], color_raw[2]);
|
||||||
|
|
|
@ -117,11 +117,11 @@ public:
|
||||||
/// @brief Render a range of audio spectrum
|
/// @brief Render a range of audio spectrum
|
||||||
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
||||||
/// @param start First column of pixel data in display to render
|
/// @param start First column of pixel data in display to render
|
||||||
/// @param selected Whether to use the alternate colour scheme
|
/// @param style Style to render audio in
|
||||||
void Render(wxBitmap &bmp, int start, bool selected);
|
void Render(wxBitmap &bmp, int start, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Render blank area
|
/// @brief Render blank area
|
||||||
void RenderBlank(wxDC &dc, const wxRect &rect, bool selected);
|
void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Set the derivation resolution
|
/// @brief Set the derivation resolution
|
||||||
/// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data
|
/// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data
|
||||||
|
|
|
@ -71,13 +71,13 @@ AudioWaveformRenderer::~AudioWaveformRenderer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
wxMemoryDC dc(bmp);
|
wxMemoryDC dc(bmp);
|
||||||
wxRect rect(wxPoint(0, 0), bmp.GetSize());
|
wxRect rect(wxPoint(0, 0), bmp.GetSize());
|
||||||
int midpoint = rect.height / 2;
|
int midpoint = rect.height / 2;
|
||||||
|
|
||||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal;
|
||||||
|
|
||||||
// Fill the background
|
// Fill the background
|
||||||
dc.SetBrush(wxBrush(pal->get(0.0f)));
|
dc.SetBrush(wxBrush(pal->get(0.0f)));
|
||||||
|
@ -140,9 +140,9 @@ void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, bool selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioWaveformRenderer::RenderBlank(wxDC &dc, const wxRect &rect, bool selected)
|
void AudioWaveformRenderer::RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style)
|
||||||
{
|
{
|
||||||
AudioColorScheme *pal = selected ? &colors_selected : &colors_normal;
|
AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal;
|
||||||
|
|
||||||
wxColor line(pal->get(1.0));
|
wxColor line(pal->get(1.0));
|
||||||
wxColor bg(pal->get(0.0));
|
wxColor bg(pal->get(0.0));
|
||||||
|
|
|
@ -63,11 +63,11 @@ public:
|
||||||
/// @brief Render a range of audio waveform
|
/// @brief Render a range of audio waveform
|
||||||
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
/// @param bmp [in,out] Bitmap to render into, also carries lenght information
|
||||||
/// @param start First column of pixel data in display to render
|
/// @param start First column of pixel data in display to render
|
||||||
/// @param selected Whether to use the alternate colour scheme
|
/// @param style Style to render audio in
|
||||||
void Render(wxBitmap &bmp, int start, bool selected);
|
void Render(wxBitmap &bmp, int start, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Render blank area
|
/// @brief Render blank area
|
||||||
void RenderBlank(wxDC &dc, const wxRect &rect, bool selected);
|
void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style);
|
||||||
|
|
||||||
/// @brief Cleans up the cache
|
/// @brief Cleans up the cache
|
||||||
/// @param max_size Maximum size in bytes for the cache
|
/// @param max_size Maximum size in bytes for the cache
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class AssKaraoke;
|
class AssKaraoke;
|
||||||
|
class AudioRenderingStyleRanges;
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
|
@ -79,6 +80,10 @@ public:
|
||||||
/// currently.
|
/// currently.
|
||||||
virtual SampleRange GetPrimaryPlaybackRange() const = 0;
|
virtual SampleRange GetPrimaryPlaybackRange() const = 0;
|
||||||
|
|
||||||
|
/// @brief Get all rendering style ranges
|
||||||
|
/// @param[out] swpts Rendering ranges will be added to this
|
||||||
|
virtual void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const = 0;
|
||||||
|
|
||||||
/// @brief Does this timing mode have labels on the audio display?
|
/// @brief Does this timing mode have labels on the audio display?
|
||||||
/// @return True if this timing mode needs labels on the audio display.
|
/// @return True if this timing mode needs labels on the audio display.
|
||||||
///
|
///
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "ass_time.h"
|
#include "ass_time.h"
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "audio_marker_provider_keyframes.h"
|
#include "audio_marker_provider_keyframes.h"
|
||||||
|
#include "audio_renderer.h"
|
||||||
#include "audio_timing.h"
|
#include "audio_timing.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
@ -106,8 +107,10 @@ public:
|
||||||
/// This checks that the markers aren't already part of a pair, and then
|
/// This checks that the markers aren't already part of a pair, and then
|
||||||
/// sets their "other" field. Positions and styles aren't affected.
|
/// sets their "other" field. Positions and styles aren't affected.
|
||||||
static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2);
|
static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2);
|
||||||
};
|
|
||||||
|
|
||||||
|
/// Implicit decay to the position of the marker
|
||||||
|
operator int64_t() const { return position; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// @class AudioTimingControllerDialogue
|
/// @class AudioTimingControllerDialogue
|
||||||
|
@ -176,6 +179,7 @@ public:
|
||||||
wxString GetWarningMessage() const;
|
wxString GetWarningMessage() const;
|
||||||
SampleRange GetIdealVisibleSampleRange() const;
|
SampleRange GetIdealVisibleSampleRange() const;
|
||||||
SampleRange GetPrimaryPlaybackRange() const;
|
SampleRange GetPrimaryPlaybackRange() const;
|
||||||
|
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const;
|
||||||
bool HasLabels() const { return false; }
|
bool HasLabels() const { return false; }
|
||||||
void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const { }
|
void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const { }
|
||||||
void Next();
|
void Next();
|
||||||
|
@ -195,8 +199,6 @@ public:
|
||||||
~AudioTimingControllerDialogue();
|
~AudioTimingControllerDialogue();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AudioTimingController *CreateDialogueTimingController(agi::Context *c)
|
AudioTimingController *CreateDialogueTimingController(agi::Context *c)
|
||||||
{
|
{
|
||||||
return new AudioTimingControllerDialogue(c);
|
return new AudioTimingControllerDialogue(c);
|
||||||
|
@ -226,7 +228,6 @@ void AudioMarkerDialogueTiming::SetPosition(int64_t new_position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AudioMarkerDialogueTiming::AudioMarkerDialogueTiming()
|
AudioMarkerDialogueTiming::AudioMarkerDialogueTiming()
|
||||||
: other(0)
|
: other(0)
|
||||||
, position(0)
|
, position(0)
|
||||||
|
@ -238,7 +239,6 @@ AudioMarkerDialogueTiming::AudioMarkerDialogueTiming()
|
||||||
// Nothing more to do
|
// Nothing more to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2)
|
void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2)
|
||||||
{
|
{
|
||||||
assert(marker1->other == 0);
|
assert(marker1->other == 0);
|
||||||
|
@ -248,8 +248,6 @@ void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, Aud
|
||||||
marker2->other = marker1;
|
marker2->other = marker1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// AudioTimingControllerDialogue
|
// AudioTimingControllerDialogue
|
||||||
|
|
||||||
AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
||||||
|
@ -279,35 +277,31 @@ AudioTimingControllerDialogue::~AudioTimingControllerDialogue()
|
||||||
context->selectionController->RemoveSelectionListener(this);
|
context->selectionController->RemoveSelectionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker()
|
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker()
|
||||||
{
|
{
|
||||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[0] : &markers[1];
|
return markers[0] < markers[1] ? &markers[0] : &markers[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() const
|
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() const
|
||||||
{
|
{
|
||||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[0] : &markers[1];
|
return &std::min(markers[0], markers[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
|
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
|
||||||
{
|
{
|
||||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[1] : &markers[0];
|
return markers[0] < markers[1] ? &markers[1] : &markers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() const
|
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() const
|
||||||
{
|
{
|
||||||
return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[1] : &markers[0];
|
return &std::max(markers[0], markers[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const
|
void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const
|
||||||
{
|
{
|
||||||
if (range.contains(markers[0].GetPosition()))
|
if (range.contains(markers[0]))
|
||||||
out_markers.push_back(&markers[0]);
|
out_markers.push_back(&markers[0]);
|
||||||
if (range.contains(markers[1].GetPosition()))
|
if (range.contains(markers[1]))
|
||||||
out_markers.push_back(&markers[1]);
|
out_markers.push_back(&markers[1]);
|
||||||
|
|
||||||
keyframes_provider.GetMarkers(range, out_markers);
|
keyframes_provider.GetMarkers(range, out_markers);
|
||||||
|
@ -331,49 +325,42 @@ void AudioTimingControllerDialogue::OnFileChanged(int type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString AudioTimingControllerDialogue::GetWarningMessage() const
|
wxString AudioTimingControllerDialogue::GetWarningMessage() const
|
||||||
{
|
{
|
||||||
// We have no warning messages currently, maybe add the old "Modified" message back later?
|
// We have no warning messages currently, maybe add the old "Modified" message back later?
|
||||||
return wxString();
|
return wxString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
|
SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
|
||||||
{
|
{
|
||||||
return GetPrimaryPlaybackRange();
|
return GetPrimaryPlaybackRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
||||||
{
|
{
|
||||||
return SampleRange(
|
return SampleRange(*GetLeftMarker(), *GetRightMarker());
|
||||||
GetLeftMarker()->GetPosition(),
|
|
||||||
GetRightMarker()->GetPosition());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
|
||||||
|
{
|
||||||
|
ranges.AddRange(*GetLeftMarker(), *GetRightMarker(), AudioStyle_Selected);
|
||||||
|
/// @todo Find inactive and non-selected lines to add to styles
|
||||||
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Next()
|
void AudioTimingControllerDialogue::Next()
|
||||||
{
|
{
|
||||||
context->selectionController->NextLine();
|
context->selectionController->NextLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Prev()
|
void AudioTimingControllerDialogue::Prev()
|
||||||
{
|
{
|
||||||
context->selectionController->PrevLine();
|
context->selectionController->PrevLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Commit()
|
void AudioTimingControllerDialogue::Commit()
|
||||||
{
|
{
|
||||||
int new_start_ms = context->audioController->MillisecondsFromSamples(GetLeftMarker()->GetPosition());
|
int new_start_ms = context->audioController->MillisecondsFromSamples(*GetLeftMarker());
|
||||||
int new_end_ms = context->audioController->MillisecondsFromSamples(GetRightMarker()->GetPosition());
|
int new_end_ms = context->audioController->MillisecondsFromSamples(*GetRightMarker());
|
||||||
|
|
||||||
// If auto committing is enabled, timing_modified will be true iif it is an
|
// If auto committing is enabled, timing_modified will be true iif it is an
|
||||||
// auto commit, as there is never pending changes to commit when the button
|
// auto commit, as there is never pending changes to commit when the button
|
||||||
|
@ -420,8 +407,6 @@ void AudioTimingControllerDialogue::Commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Revert()
|
void AudioTimingControllerDialogue::Revert()
|
||||||
{
|
{
|
||||||
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
||||||
|
@ -439,16 +424,13 @@ void AudioTimingControllerDialogue::Revert()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const
|
bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const
|
||||||
{
|
{
|
||||||
SampleRange range(sample-sensitivity, sample+sensitivity);
|
SampleRange range(sample-sensitivity, sample+sensitivity);
|
||||||
|
|
||||||
return range.contains(markers[0].GetPosition()) || range.contains(markers[1].GetPosition());
|
return range.contains(markers[0]) || range.contains(markers[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sensitivity)
|
AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sensitivity)
|
||||||
{
|
{
|
||||||
assert(sensitivity >= 0);
|
assert(sensitivity >= 0);
|
||||||
|
@ -458,8 +440,8 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen
|
||||||
AudioMarkerDialogueTiming *left = GetLeftMarker();
|
AudioMarkerDialogueTiming *left = GetLeftMarker();
|
||||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
AudioMarkerDialogueTiming *right = GetRightMarker();
|
||||||
|
|
||||||
dist_l = tabs(left->GetPosition() - sample);
|
dist_l = tabs(*left - sample);
|
||||||
dist_r = tabs(right->GetPosition() - sample);
|
dist_r = tabs(*right - sample);
|
||||||
|
|
||||||
if (dist_l < dist_r && dist_l <= sensitivity)
|
if (dist_l < dist_r && dist_l <= sensitivity)
|
||||||
{
|
{
|
||||||
|
@ -483,8 +465,6 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int sensitivity)
|
AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int sensitivity)
|
||||||
{
|
{
|
||||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
AudioMarkerDialogueTiming *right = GetRightMarker();
|
||||||
|
@ -492,20 +472,16 @@ AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int se
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position)
|
void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position)
|
||||||
{
|
{
|
||||||
assert(marker == &markers[0] || marker == &markers[1]);
|
assert(marker == &markers[0] || marker == &markers[1]);
|
||||||
|
|
||||||
SetMarker(static_cast<AudioMarkerDialogueTiming*>(marker), new_position);
|
SetMarker(static_cast<AudioMarkerDialogueTiming*>(marker), new_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::UpdateSelection()
|
void AudioTimingControllerDialogue::UpdateSelection()
|
||||||
{
|
{
|
||||||
AnnounceUpdatedPrimaryRange();
|
AnnounceUpdatedPrimaryRange();
|
||||||
|
AnnounceUpdatedStyleRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int64_t sample)
|
void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int64_t sample)
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "ass_karaoke.h"
|
#include "ass_karaoke.h"
|
||||||
#include "audio_marker_provider_keyframes.h"
|
#include "audio_marker_provider_keyframes.h"
|
||||||
|
#include "audio_renderer.h"
|
||||||
#include "audio_timing.h"
|
#include "audio_timing.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
@ -120,6 +121,7 @@ public:
|
||||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const;
|
void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const;
|
||||||
wxString GetWarningMessage() const { return ""; }
|
wxString GetWarningMessage() const { return ""; }
|
||||||
SampleRange GetIdealVisibleSampleRange() const;
|
SampleRange GetIdealVisibleSampleRange() const;
|
||||||
|
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const;
|
||||||
SampleRange GetPrimaryPlaybackRange() const;
|
SampleRange GetPrimaryPlaybackRange() const;
|
||||||
bool HasLabels() const { return true; }
|
bool HasLabels() const { return true; }
|
||||||
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &out_labels) const;
|
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &out_labels) const;
|
||||||
|
@ -199,6 +201,12 @@ void AudioTimingControllerKaraoke::Prev() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioTimingControllerKaraoke::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
|
||||||
|
{
|
||||||
|
SampleRange sr = GetPrimaryPlaybackRange();
|
||||||
|
ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Selected);
|
||||||
|
}
|
||||||
|
|
||||||
SampleRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
|
SampleRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
|
||||||
return SampleRange(
|
return SampleRange(
|
||||||
cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
|
cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
|
||||||
|
|
Loading…
Reference in a new issue