From 8d28b4477398b19a077eec7b4e80f242854cf7e9 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 18 Nov 2011 22:56:45 +0000 Subject: [PATCH] Get audio styling ranges from the timing controller. Based on a patch by jfs. Originally committed to SVN as r5878. --- aegisub/src/audio_controller.cpp | 14 +- aegisub/src/audio_controller.h | 4 + aegisub/src/audio_display.cpp | 210 ++++++++++++++++-------- aegisub/src/audio_display.h | 15 +- aegisub/src/audio_renderer.cpp | 47 +++--- aegisub/src/audio_renderer.h | 68 ++++++-- aegisub/src/audio_renderer_spectrum.cpp | 8 +- aegisub/src/audio_renderer_spectrum.h | 10 +- aegisub/src/audio_renderer_waveform.cpp | 8 +- aegisub/src/audio_renderer_waveform.h | 10 +- aegisub/src/audio_timing.h | 5 + aegisub/src/audio_timing_dialogue.cpp | 70 +++----- aegisub/src/audio_timing_karaoke.cpp | 8 + 13 files changed, 279 insertions(+), 198 deletions(-) diff --git a/aegisub/src/audio_controller.cpp b/aegisub/src/audio_controller.cpp index 5c117dfb4..2e6e529b7 100644 --- a/aegisub/src/audio_controller.cpp +++ b/aegisub/src/audio_controller.cpp @@ -336,17 +336,15 @@ void AudioController::SetTimingController(AudioTimingController *new_controller) { if (timing_controller.get() != new_controller) { timing_controller.reset(new_controller); - timing_controller->AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved))); - timing_controller->AddLabelChangedListener(std::tr1::bind(std::tr1::ref(AnnounceLabelChanged))); + timing_controller->AddMarkerMovedListener(bind(std::tr1::ref(AnnounceMarkerMoved))); + timing_controller->AddLabelChangedListener(bind(std::tr1::ref(AnnounceLabelChanged))); timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this); - timing_controller->AddUpdatedStyleRangesListener(&AudioController::OnTimingControllerUpdatedStyleRanges, this); + timing_controller->AddUpdatedStyleRangesListener(bind(std::tr1::ref(AnnounceStyleRangesChanged))); } AnnounceTimingControllerChanged(); } - - void AudioController::OnTimingControllerUpdatedPrimaryRange() { if (playback_mode == PM_PrimaryRange) @@ -357,12 +355,6 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange() AnnounceSelectionChanged(); } - -void AudioController::OnTimingControllerUpdatedStyleRanges() -{ - /// @todo redraw and stuff, probably -} - void AudioController::OnSubtitlesSave() { if (IsAudioOpen()) diff --git a/aegisub/src/audio_controller.h b/aegisub/src/audio_controller.h index ffcd12a04..c39a6a0cb 100644 --- a/aegisub/src/audio_controller.h +++ b/aegisub/src/audio_controller.h @@ -201,6 +201,9 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public /// The selected time range changed agi::signal::Signal<> AnnounceSelectionChanged; + /// The styling ranges have been updated by the timing controller + agi::signal::Signal<> AnnounceStyleRangesChanged; + /// The audio output object AudioPlayer *player; @@ -390,6 +393,7 @@ public: DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop, AddPlaybackStopListener) DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener) DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionChangedListener) + DEFINE_SIGNAL_ADDERS(AnnounceStyleRangesChanged, AddStyleRangesChangedListener) }; /// @class AudioMarker diff --git a/aegisub/src/audio_display.cpp b/aegisub/src/audio_display.cpp index dfd7efa46..62d1446c6 100644 --- a/aegisub/src/audio_display.cpp +++ b/aegisub/src/audio_display.cpp @@ -494,8 +494,54 @@ public: } }; +class AudioStyleRangeMerger : public AudioRenderingStyleRanges { + typedef std::map 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) : 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)) , timeline(new AudioDisplayTimeline(this)) , dragged_object(0) -, old_selection(0, 0) { + style_ranges[0] = AudioStyle_Normal; + scroll_left = 0; pixel_audio_width = 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->AddMarkerMovedListener(&AudioDisplay::OnMarkerMoved, this)); slots.push_back(controller->AddSelectionChangedListener(&AudioDisplay::OnSelectionChanged, this)); + slots.push_back(controller->AddStyleRangesChangedListener(&AudioDisplay::OnStyleRangesChanged, this)); OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this); @@ -764,15 +812,9 @@ BEGIN_EVENT_TABLE(AudioDisplay, wxWindow) EVT_SET_FOCUS(AudioDisplay::OnFocus) EVT_KILL_FOCUS(AudioDisplay::OnFocus) 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) { wxAutoBufferedPaintDC dc(this); @@ -793,11 +835,6 @@ void AudioDisplay::OnPaint(wxPaintEvent& event) bool redraw_scrollbar = 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(); for (wxRegionIterator region(GetUpdateRegion()); region; ++region) { @@ -816,38 +853,31 @@ void AudioDisplay::OnPaint(wxPaintEvent& event) continue; } - // 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; - - std::vector 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::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( + SampleRange updsamples( SamplesFromRelativeX(updrect.x - foot_size), SamplesFromRelativeX(updrect.x + updrect.width + foot_size)); + std::map::iterator pt = style_ranges.upper_bound(updsamples.begin()); + std::map::iterator pe = style_ranges.upper_bound(updsamples.end()); + + if (pt != style_ranges.begin()) + --pt; + + while (pt != pe) + { + AudioRenderingStyle range_style = static_cast(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 AudioMarkerVector markers; - controller->GetMarkers(updrectsamples, markers); + controller->GetMarkers(updsamples, markers); wxDCPenChanger pen_retainer(dc, wxPen()); wxDCBrushChanger brush_retainer(dc, wxBrush()); for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i) @@ -876,7 +906,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event) // Draw labels std::vector labels; - controller->GetLabels(updrectsamples, labels); + controller->GetLabels(updsamples, labels); if (!labels.empty()) { wxDCFontChanger fc(dc); @@ -943,7 +973,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event) track_cursor_label_rect.SetPosition(label_pos); track_cursor_label_rect.SetSize(label_size); 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; track_cursor_pos = new_pos; - RefreshRect(wxRect(old_pos - scroll_left - 0, audio_top, 1, audio_height)); - RefreshRect(wxRect(new_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), false); // Make sure the old label gets cleared away - RefreshRect(track_cursor_label_rect); + RefreshRect(track_cursor_label_rect, false); 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))); track_cursor_label = new_label_time.GetASSFormated(); track_cursor_label_rect.x += new_pos - old_pos; - RefreshRect(track_cursor_label_rect); + RefreshRect(track_cursor_label_rect, false); } else { @@ -1148,7 +1178,7 @@ void AudioDisplay::OnSize(wxSizeEvent &event) void AudioDisplay::OnFocus(wxFocusEvent &event) { // 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() { - /// @todo Handle rendering style ranges from timing controller instead SampleRange sel(controller->GetPrimaryPlaybackRange()); scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length())); + if (OPT_GET("Audio/Auto/Scroll")->GetBool()) + { 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(wxRect(std::min(e1, e2)-10, audio_top, abs(e1-e2)+20, audio_height)); - } - } - else - { - RefreshRect(wxRect(s1 - 10, audio_top, e1 + 20, audio_height)); - RefreshRect(wxRect(s2 - 10, audio_top, e2 + 20, audio_height)); } - RefreshRect(scrollbar->GetBounds()); + RefreshRect(scrollbar->GetBounds(), false); +} - old_selection = sel; +void AudioDisplay::OnStyleRangesChanged() +{ + AudioStyleRangeMerger asrm; + controller->GetTimingController()->GetRenderingStyles(asrm); + + std::map old_style_ranges; + swap(old_style_ranges, style_ranges); + style_ranges.insert(asrm.begin(), asrm.end()); + + std::map::iterator old_style_it = old_style_ranges.begin(); + std::map::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()) + { + 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 + { + if (old_style != new_style) + Redraw(range_start, new_style_it->first); + new_style = new_style_it->second; + range_start = new_style_it->first; + ++new_style_it; + } + } + + // Fill in the last style range + if (old_style != new_style) + { + Redraw(range_start, SamplesFromRelativeX(GetClientSize().GetWidth())); + } +} + +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() { /// @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); } diff --git a/aegisub/src/audio_display.h b/aegisub/src/audio_display.h index 34afe0fd9..f279747c0 100644 --- a/aegisub/src/audio_display.h +++ b/aegisub/src/audio_display.h @@ -98,7 +98,7 @@ public: /// timing controller. The audio display also renders audio according to the audio controller /// and the timing controller, using an audio renderer instance. class AudioDisplay: public wxWindow { -private: + std::list slots; agi::Context *context; @@ -146,6 +146,8 @@ private: /// Height of main audio area in pixels int audio_height; + /// Width of the audio marker feet in pixels + static const int foot_size = 6; /// Zoom level given as a number, see SetZoomLevel for details int zoom_level; @@ -163,8 +165,8 @@ private: /// @brief Remove the tracking cursor from the display void RemoveTrackCursor(); - /// Previous audio selection for optimizing redraw when selection changes - SampleRange old_selection; + /// Previous style ranges for optimizing redraw when ranges change + std::map style_ranges; /// @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. 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 void OnPaint(wxPaintEvent &event); /// wxWidgets mouse input event @@ -187,10 +194,10 @@ private: void OnAudioOpen(AudioProvider *provider); void OnPlaybackPosition(int64_t sample_position); void OnSelectionChanged(); + void OnStyleRangesChanged(); void OnMarkerMoved(); public: - AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context); ~AudioDisplay(); diff --git a/aegisub/src/audio_renderer.cpp b/aegisub/src/audio_renderer.cpp index 5f6ab5cb5..cc95f17a5 100644 --- a/aegisub/src/audio_renderer.cpp +++ b/aegisub/src/audio_renderer.cpp @@ -46,9 +46,12 @@ #include "audio_renderer.h" #include "include/aegisub/audio_provider.h" -#undef min -#undef max +template static void for_each(C &container, F const& func) +{ + std::for_each(container.begin(), container.end(), func); +} +using std::tr1::placeholders::_1; AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer) { @@ -80,19 +83,18 @@ size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const AudioRenderer::AudioRenderer() : cache_bitmap_width(32) // arbitrary value for now -, bitmaps_normal(256, AudioRendererBitmapCacheBitmapFactory(this)) -, bitmaps_selected(256, AudioRendererBitmapCacheBitmapFactory(this)) , cache_bitmap_maxsize(0) , cache_renderer_maxsize(0) , renderer(0) , provider(0) { + bitmaps.resize(AudioStyle_MAX, AudioRendererBitmapCache(256, AudioRendererBitmapCacheBitmapFactory(this))); + // Make sure there's *some* values for those fields, and in the caches SetSamplesPerPixel(1); SetHeight(1); } - AudioRenderer::~AudioRenderer() { // 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); cache_numblocks = rendered_width / cache_bitmap_width; - bitmaps_normal.SetBlockCount(cache_numblocks); - bitmaps_selected.SetBlockCount(cache_numblocks); + for_each(bitmaps, bind(&AudioRendererBitmapCache::SetBlockCount, _1, cache_numblocks)); } } -wxBitmap AudioRenderer::GetCachedBitmap(int i, bool selected) +wxBitmap AudioRenderer::GetCachedBitmap(int i, AudioRenderingStyle style) { assert(provider); assert(renderer); - // Pick the cache to use - AudioRendererBitmapCache *cache = selected ? &bitmaps_selected : &bitmaps_normal; - bool created = false; - wxBitmap *bmp = cache->Get(i, &created); + wxBitmap *bmp = bitmaps[style].Get(i, &created); assert(bmp); if (created) { - renderer->Render(*bmp, i*cache_bitmap_width, selected); + renderer->Render(*bmp, i*cache_bitmap_width, style); } 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); @@ -257,7 +255,7 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool else if (firstbitmap == lastbitmap) { const int renderwidth = lastbitmapoffset - firstbitmapoffset; - wxBitmap bmp = GetCachedBitmap(firstbitmap, selected); + wxBitmap bmp = GetCachedBitmap(firstbitmap, style); wxMemoryDC bmpdc(bmp); dc.Blit(origin, wxSize(renderwidth, pixel_height), &bmpdc, wxPoint(firstbitmapoffset, 0)); origin.x += renderwidth; @@ -267,7 +265,7 @@ void AudioRenderer::Render(wxDC &dc, wxPoint origin, int start, int length, bool wxBitmap bmp; { - bmp = GetCachedBitmap(firstbitmap, selected); + bmp = GetCachedBitmap(firstbitmap, style); // Can't use dc.DrawBitmap here because we need to clip the bitmap wxMemoryDC bmpdc(bmp); 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) { - bmp = GetCachedBitmap(i, selected); + bmp = GetCachedBitmap(i, style); dc.DrawBitmap(bmp, origin); origin.x += cache_bitmap_width; } { - bmp = GetCachedBitmap(lastbitmap, selected); + bmp = GetCachedBitmap(lastbitmap, style); // We also need clipping here wxMemoryDC bmpdc(bmp); 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 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_selected.Age(cache_bitmap_maxsize); - else - bitmaps_normal.Age(cache_bitmap_maxsize); + bitmaps[style].Age(cache_bitmap_maxsize); renderer->AgeCache(cache_renderer_maxsize); } void AudioRenderer::Invalidate() { - bitmaps_normal.Age(0); - bitmaps_selected.Age(0); + for_each(bitmaps, bind(&AudioRendererBitmapCache::Age, _1, 0)); } - - void AudioRendererBitmapProvider::SetProvider(AudioProvider *_provider) { if (provider == _provider) return; @@ -342,4 +334,3 @@ void AudioRendererBitmapProvider::SetAmplitudeScale(float _amplitude_scale) OnSetAmplitudeScale(); } - diff --git a/aegisub/src/audio_renderer.h b/aegisub/src/audio_renderer.h index a5de2eaef..2a8a31a01 100644 --- a/aegisub/src/audio_renderer.h +++ b/aegisub/src/audio_renderer.h @@ -38,6 +38,7 @@ #ifndef AGI_PRE #include +#include #include #include @@ -52,6 +53,41 @@ class AudioProvider; class AudioRendererBitmapProvider; 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 /// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer struct AudioRendererBitmapCacheBitmapFactory { @@ -104,10 +140,8 @@ class AudioRenderer { /// Width of bitmaps to store in cache const int cache_bitmap_width; - /// Cached bitmaps for normal audio ranges - AudioRendererBitmapCache bitmaps_normal; - /// Cached bitmaps for marked (selected) audio ranges - AudioRendererBitmapCache bitmaps_selected; + /// Cached bitmaps for audio ranges + std::vector bitmaps; /// Number of blocks in the bitmap caches size_t cache_numblocks; /// The maximum allowed size of each bitmap cache, in bytes @@ -122,13 +156,13 @@ class AudioRenderer { AudioProvider *provider; /// @brief Make sure bitmap index i is in cache - /// @param i Index of bitmap to get into cache - /// @param selected Whether to get a "selected" state bitmap or not + /// @param i Index of bitmap to get into cache + /// @param style Rendering style required for bitmap /// @return The requested bitmap /// /// Will attempt retrieving the requested bitmap from the cache, creating 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 /// @@ -230,11 +264,11 @@ public: /// @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 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 /// 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 /// @@ -284,22 +318,22 @@ public: virtual ~AudioRendererBitmapProvider() { } /// @brief Rendering function - /// @param bmp Bitmap to render to - /// @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 bmp Bitmap to render to + /// @param start First pixel from beginning of the audio stream to render + /// @param style Style to render audio in /// /// Deriving classes must implement this method. The bitmap in bmp holds /// 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 - /// @param dc The device context to render to - /// @param rect The rectangle to fill with the image of blank audio - /// @param selected Whether to render as being selected or not + /// @param dc The device context to render to + /// @param rect The rectangle to fill with the image of blank audio + /// @param style Style to render audio in /// /// Deriving classes must implement this method. The rectangle has the height /// 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 /// @param provider Audio provider to change to diff --git a/aegisub/src/audio_renderer_spectrum.cpp b/aegisub/src/audio_renderer_spectrum.cpp index c0609853c..04f64679d 100644 --- a/aegisub/src/audio_renderer_spectrum.cpp +++ b/aegisub/src/audio_renderer_spectrum.cpp @@ -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) return; @@ -284,7 +284,7 @@ void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, bool selected) ptrdiff_t stride = img.GetWidth()*3; 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 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 - AudioColorScheme *pal = selected ? &colors_selected : &colors_normal; + AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal; unsigned char color_raw[4]; pal->map(0.0, color_raw); wxColour col(color_raw[0], color_raw[1], color_raw[2]); diff --git a/aegisub/src/audio_renderer_spectrum.h b/aegisub/src/audio_renderer_spectrum.h index c9e9843f0..ba6722a1c 100644 --- a/aegisub/src/audio_renderer_spectrum.h +++ b/aegisub/src/audio_renderer_spectrum.h @@ -115,13 +115,13 @@ public: virtual ~AudioSpectrumRenderer(); /// @brief Render a range of audio spectrum - /// @param bmp [in,out] Bitmap to render into, also carries lenght information - /// @param start First column of pixel data in display to render - /// @param selected Whether to use the alternate colour scheme - void Render(wxBitmap &bmp, int start, bool selected); + /// @param bmp [in,out] Bitmap to render into, also carries lenght information + /// @param start First column of pixel data in display to render + /// @param style Style to render audio in + void Render(wxBitmap &bmp, int start, AudioRenderingStyle style); /// @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 /// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data diff --git a/aegisub/src/audio_renderer_waveform.cpp b/aegisub/src/audio_renderer_waveform.cpp index 4e77e043a..47a870f88 100644 --- a/aegisub/src/audio_renderer_waveform.cpp +++ b/aegisub/src/audio_renderer_waveform.cpp @@ -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); wxRect rect(wxPoint(0, 0), bmp.GetSize()); int midpoint = rect.height / 2; - AudioColorScheme *pal = selected ? &colors_selected : &colors_normal; + AudioColorScheme *pal = style == AudioStyle_Selected ? &colors_selected : &colors_normal; // Fill the background 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 bg(pal->get(0.0)); diff --git a/aegisub/src/audio_renderer_waveform.h b/aegisub/src/audio_renderer_waveform.h index 5abf20b20..7b7cc24e0 100644 --- a/aegisub/src/audio_renderer_waveform.h +++ b/aegisub/src/audio_renderer_waveform.h @@ -61,13 +61,13 @@ public: virtual ~AudioWaveformRenderer(); /// @brief Render a range of audio waveform - /// @param bmp [in,out] Bitmap to render into, also carries lenght information - /// @param start First column of pixel data in display to render - /// @param selected Whether to use the alternate colour scheme - void Render(wxBitmap &bmp, int start, bool selected); + /// @param bmp [in,out] Bitmap to render into, also carries lenght information + /// @param start First column of pixel data in display to render + /// @param style Style to render audio in + void Render(wxBitmap &bmp, int start, AudioRenderingStyle style); /// @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 /// @param max_size Maximum size in bytes for the cache diff --git a/aegisub/src/audio_timing.h b/aegisub/src/audio_timing.h index 066178d53..4f5a42817 100644 --- a/aegisub/src/audio_timing.h +++ b/aegisub/src/audio_timing.h @@ -36,6 +36,7 @@ class AssDialogue; class AssFile; class AssKaraoke; +class AudioRenderingStyleRanges; namespace agi { struct Context; } #include "audio_controller.h" @@ -79,6 +80,10 @@ public: /// currently. 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? /// @return True if this timing mode needs labels on the audio display. /// diff --git a/aegisub/src/audio_timing_dialogue.cpp b/aegisub/src/audio_timing_dialogue.cpp index 66cef0083..a3da1d6a9 100644 --- a/aegisub/src/audio_timing_dialogue.cpp +++ b/aegisub/src/audio_timing_dialogue.cpp @@ -44,6 +44,7 @@ #include "ass_time.h" #include "audio_controller.h" #include "audio_marker_provider_keyframes.h" +#include "audio_renderer.h" #include "audio_timing.h" #include "include/aegisub/context.h" #include "main.h" @@ -106,8 +107,10 @@ public: /// This checks that the markers aren't already part of a pair, and then /// sets their "other" field. Positions and styles aren't affected. static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2); -}; + /// Implicit decay to the position of the marker + operator int64_t() const { return position; } +}; /// @class AudioTimingControllerDialogue @@ -176,6 +179,7 @@ public: wxString GetWarningMessage() const; SampleRange GetIdealVisibleSampleRange() const; SampleRange GetPrimaryPlaybackRange() const; + void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const; bool HasLabels() const { return false; } void GetLabels(SampleRange const& range, std::vector &out) const { } void Next(); @@ -195,8 +199,6 @@ public: ~AudioTimingControllerDialogue(); }; - - AudioTimingController *CreateDialogueTimingController(agi::Context *c) { return new AudioTimingControllerDialogue(c); @@ -226,7 +228,6 @@ void AudioMarkerDialogueTiming::SetPosition(int64_t new_position) } } - AudioMarkerDialogueTiming::AudioMarkerDialogueTiming() : other(0) , position(0) @@ -238,7 +239,6 @@ AudioMarkerDialogueTiming::AudioMarkerDialogueTiming() // Nothing more to do } - void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2) { assert(marker1->other == 0); @@ -248,8 +248,6 @@ void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, Aud marker2->other = marker1; } - - // AudioTimingControllerDialogue AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c) @@ -279,35 +277,31 @@ AudioTimingControllerDialogue::~AudioTimingControllerDialogue() context->selectionController->RemoveSelectionListener(this); } - - 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 { - return markers[0].GetPosition() < markers[1].GetPosition() ? &markers[0] : &markers[1]; + return &std::min(markers[0], markers[1]); } 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 { - 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 { - if (range.contains(markers[0].GetPosition())) + if (range.contains(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]); keyframes_provider.GetMarkers(range, out_markers); @@ -331,49 +325,42 @@ void AudioTimingControllerDialogue::OnFileChanged(int type) { } } - wxString AudioTimingControllerDialogue::GetWarningMessage() const { // We have no warning messages currently, maybe add the old "Modified" message back later? return wxString(); } - - SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const { return GetPrimaryPlaybackRange(); } - - SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const { - return SampleRange( - GetLeftMarker()->GetPosition(), - GetRightMarker()->GetPosition()); + return SampleRange(*GetLeftMarker(), *GetRightMarker()); } - +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() { context->selectionController->NextLine(); } - - void AudioTimingControllerDialogue::Prev() { context->selectionController->PrevLine(); } - - void AudioTimingControllerDialogue::Commit() { - int new_start_ms = context->audioController->MillisecondsFromSamples(GetLeftMarker()->GetPosition()); - int new_end_ms = context->audioController->MillisecondsFromSamples(GetRightMarker()->GetPosition()); + int new_start_ms = context->audioController->MillisecondsFromSamples(*GetLeftMarker()); + int new_end_ms = context->audioController->MillisecondsFromSamples(*GetRightMarker()); // 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 @@ -420,8 +407,6 @@ void AudioTimingControllerDialogue::Commit() } } - - void AudioTimingControllerDialogue::Revert() { if (AssDialogue *line = context->selectionController->GetActiveLine()) @@ -439,16 +424,13 @@ void AudioTimingControllerDialogue::Revert() } } - - bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const { 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) { assert(sensitivity >= 0); @@ -458,8 +440,8 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen AudioMarkerDialogueTiming *left = GetLeftMarker(); AudioMarkerDialogueTiming *right = GetRightMarker(); - dist_l = tabs(left->GetPosition() - sample); - dist_r = tabs(right->GetPosition() - sample); + dist_l = tabs(*left - sample); + dist_r = tabs(*right - sample); if (dist_l < dist_r && dist_l <= sensitivity) { @@ -483,8 +465,6 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen return right; } - - AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int sensitivity) { AudioMarkerDialogueTiming *right = GetRightMarker(); @@ -492,20 +472,16 @@ AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int se return right; } - - void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position) { assert(marker == &markers[0] || marker == &markers[1]); - SetMarker(static_cast(marker), new_position); } - - void AudioTimingControllerDialogue::UpdateSelection() { AnnounceUpdatedPrimaryRange(); + AnnounceUpdatedStyleRanges(); } void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int64_t sample) diff --git a/aegisub/src/audio_timing_karaoke.cpp b/aegisub/src/audio_timing_karaoke.cpp index ea27ea76d..8507beff3 100644 --- a/aegisub/src/audio_timing_karaoke.cpp +++ b/aegisub/src/audio_timing_karaoke.cpp @@ -29,6 +29,7 @@ #include "ass_file.h" #include "ass_karaoke.h" #include "audio_marker_provider_keyframes.h" +#include "audio_renderer.h" #include "audio_timing.h" #include "include/aegisub/context.h" #include "main.h" @@ -120,6 +121,7 @@ public: void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const; wxString GetWarningMessage() const { return ""; } SampleRange GetIdealVisibleSampleRange() const; + void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const; SampleRange GetPrimaryPlaybackRange() const; bool HasLabels() const { return true; } void GetLabels(const SampleRange &range, std::vector &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 { return SampleRange( cur_syl > 0 ? markers[cur_syl - 1] : start_marker,