From 92e10c80a240a13c6c6d9bebfdc54bfb4d7e9f8b Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 18 Nov 2011 22:58:12 +0000 Subject: [PATCH] Add markers and styling ranges in the audio display for inactive lines. Closes #1327. Originally committed to SVN as r5887. --- aegisub/src/audio_renderer.cpp | 18 +-- aegisub/src/audio_timing_dialogue.cpp | 161 +++++++++++++++++++++----- 2 files changed, 134 insertions(+), 45 deletions(-) diff --git a/aegisub/src/audio_renderer.cpp b/aegisub/src/audio_renderer.cpp index cc95f17a5..2abd0849d 100644 --- a/aegisub/src/audio_renderer.cpp +++ b/aegisub/src/audio_renderer.cpp @@ -53,34 +53,28 @@ template static void for_each(C &container, F const& func) using std::tr1::placeholders::_1; -AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *_renderer) +AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *renderer) +: renderer(renderer) { - assert(_renderer != 0); - renderer = _renderer; + assert(renderer); } - -wxBitmap *AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int i) +wxBitmap *AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int /* i */) { - (void)i; return new wxBitmap(renderer->cache_bitmap_width, renderer->pixel_height, 24); } - void AudioRendererBitmapCacheBitmapFactory::DisposeBlock(wxBitmap *bmp) { delete bmp; } - size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const { return sizeof(wxBitmap) + renderer->cache_bitmap_width * renderer->pixel_height * 3; } - - AudioRenderer::AudioRenderer() : cache_bitmap_width(32) // arbitrary value for now , cache_bitmap_maxsize(0) @@ -97,10 +91,8 @@ AudioRenderer::AudioRenderer() AudioRenderer::~AudioRenderer() { - // Nothing to do, everything is auto-allocated } - void AudioRenderer::SetSamplesPerPixel(int _pixel_samples) { if (pixel_samples == _pixel_samples) return; @@ -173,7 +165,7 @@ void AudioRenderer::SetCacheMaxSize(size_t max_size) // Limit the bitmap cache sizes to 16 MB hard, to avoid the risk of exhausting // system bitmap object resources and similar. Experimenting shows that 16 MB // bitmap cache should be plenty even if working with a one hour audio clip. - cache_bitmap_maxsize = std::min(max_size/8, (size_t)0x1000000); + cache_bitmap_maxsize = std::min(max_size/8, 0x1000000); // The renderer gets whatever is left. cache_renderer_maxsize = max_size - 2*cache_bitmap_maxsize; } diff --git a/aegisub/src/audio_timing_dialogue.cpp b/aegisub/src/audio_timing_dialogue.cpp index a3da1d6a9..1369225bf 100644 --- a/aegisub/src/audio_timing_dialogue.cpp +++ b/aegisub/src/audio_timing_dialogue.cpp @@ -112,6 +112,30 @@ public: operator int64_t() const { return position; } }; +/// @class InactiveLineMarker +/// @brief Markers for the beginning and ends of inactive lines +class InactiveLineMarker : public AudioMarker { + int64_t position; + Pen style; + FeetStyle feet; + +public: + int64_t GetPosition() const { return position; } + wxPen GetStyle() const { return style; } + FeetStyle GetFeet() const { return feet; } + bool CanSnap() const { return false; } + + InactiveLineMarker(int64_t position, bool start) + : position(position) + , style("Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness") + , feet(start ? Feet_Right : Feet_Left) + { + } + + /// Implicit decay to the position of the marker + operator int64_t() const { return position; } +}; + /// @class AudioTimingControllerDialogue /// @brief Default timing mode for dialogue subtitles @@ -127,7 +151,10 @@ public: /// lines at the same time, if they e.g. have end1==start2. class AudioTimingControllerDialogue : public AudioTimingController, private SelectionListener { /// Start and end markers for the active line - AudioMarkerDialogueTiming markers[2]; + AudioMarkerDialogueTiming active_markers[2]; + + /// Markers for inactive lines + std::vector inactive_markers; /// Marker provider for video keyframes AudioMarkerProviderKeyframes keyframes_provider; @@ -140,30 +167,36 @@ class AudioTimingControllerDialogue : public AudioTimingController, private Sele /// Commit id for coalescing purposes when in auto commit mode int commit_id; + /// The owning project context + agi::Context *context; + + /// Autocommit option + const agi::OptionValue *auto_commit; + const agi::OptionValue *inactive_line_mode; + + agi::signal::Connection commit_connection; + agi::signal::Connection audio_open_connection; + agi::signal::Connection inactive_line_mode_connection; + /// Get the leftmost of the markers AudioMarkerDialogueTiming *GetLeftMarker(); const AudioMarkerDialogueTiming *GetLeftMarker() const; + /// Get the rightmost of the markers AudioMarkerDialogueTiming *GetRightMarker(); const AudioMarkerDialogueTiming *GetRightMarker() const; - /// The owning project context - agi::Context *context; - /// Update the audio controller's selection void UpdateSelection(); + /// Regenerate markers for inactive lines + void RegenerateInactiveLines(); + /// @brief Set the position of a marker and announce the change to the world /// @param marker Marker to move /// @param sample New position of the marker void SetMarker(AudioMarkerDialogueTiming *marker, int64_t sample); - /// Autocommit option - const agi::OptionValue *auto_commit; - - agi::signal::Connection commit_slot; - agi::signal::Connection audio_open_slot; - // SubtitleSelectionListener interface void OnActiveLineChanged(AssDialogue *new_line); void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed); @@ -256,18 +289,19 @@ AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c) , commit_id(-1) , context(c) , auto_commit(OPT_GET("Audio/Auto/Commit")) +, inactive_line_mode(OPT_GET("Audio/Inactive Lines Display Mode")) +, commit_connection(c->ass->AddCommitListener(&AudioTimingControllerDialogue::OnFileChanged, this)) +, audio_open_connection(c->audioController->AddAudioOpenListener(&AudioTimingControllerDialogue::Revert, this)) +, inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this)) { assert(c->audioController != 0); - AudioMarkerDialogueTiming::InitPair(&markers[0], &markers[1]); + AudioMarkerDialogueTiming::InitPair(&active_markers[0], &active_markers[1]); if (c->audioController->IsAudioOpen()) Revert(); c->selectionController->AddSelectionListener(this); - commit_slot = c->ass->AddCommitListener(&AudioTimingControllerDialogue::OnFileChanged, this); - audio_open_slot = c->audioController->AddAudioOpenListener(&AudioTimingControllerDialogue::Revert, this); - keyframes_provider.AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved))); } @@ -279,38 +313,49 @@ AudioTimingControllerDialogue::~AudioTimingControllerDialogue() AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() { - return markers[0] < markers[1] ? &markers[0] : &markers[1]; + return active_markers[0] < active_markers[1] ? &active_markers[0] : &active_markers[1]; } const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() const { - return &std::min(markers[0], markers[1]); + return &std::min(active_markers[0], active_markers[1]); } AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() { - return markers[0] < markers[1] ? &markers[1] : &markers[0]; + return active_markers[0] < active_markers[1] ? &active_markers[1] : &active_markers[0]; } const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() const { - return &std::max(markers[0], markers[1]); + return &std::max(active_markers[0], active_markers[1]); } void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const { - if (range.contains(markers[0])) - out_markers.push_back(&markers[0]); - if (range.contains(markers[1])) - out_markers.push_back(&markers[1]); - keyframes_provider.GetMarkers(range, out_markers); + + // Copy inactive line markers in the range + std::vector::const_iterator + a = lower_bound(inactive_markers.begin(), inactive_markers.end(), range.begin()), + b = upper_bound(inactive_markers.begin(), inactive_markers.end(), range.end()); + + for (; a != b; ++a) + out_markers.push_back(&*a); + + if (range.contains(active_markers[0])) + out_markers.push_back(&active_markers[0]); + if (range.contains(active_markers[1])) + out_markers.push_back(&active_markers[1]); } void AudioTimingControllerDialogue::OnActiveLineChanged(AssDialogue *new_line) { if (context->audioController->IsAudioOpen()) + { Revert(); + RegenerateInactiveLines(); + } } void AudioTimingControllerDialogue::OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) @@ -322,6 +367,11 @@ void AudioTimingControllerDialogue::OnFileChanged(int type) { if (type & AssFile::COMMIT_DIAG_TIME) { Revert(); + RegenerateInactiveLines(); + } + else if (type & AssFile::COMMIT_DIAG_ADDREM) + { + RegenerateInactiveLines(); } } @@ -344,7 +394,10 @@ SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const { ranges.AddRange(*GetLeftMarker(), *GetRightMarker(), AudioStyle_Selected); - /// @todo Find inactive and non-selected lines to add to styles + for (size_t i = 0; i < inactive_markers.size(); i += 2) + { + ranges.AddRange(inactive_markers[i], inactive_markers[i + 1], AudioStyle_Inactive); + } } void AudioTimingControllerDialogue::Next() @@ -378,7 +431,7 @@ void AudioTimingControllerDialogue::Commit() (*sub)->End.SetMS(new_end_ms); } - commit_slot.Block(); + commit_connection.Block(); if (user_triggered) { context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME); @@ -387,7 +440,7 @@ void AudioTimingControllerDialogue::Commit() else commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, context->selectionController->GetActiveLine()); - commit_slot.Unblock(); + commit_connection.Unblock(); timing_modified = false; } @@ -399,8 +452,8 @@ void AudioTimingControllerDialogue::Commit() Next(); if (context->selectionController->GetActiveLine()->End.GetMS() == 0) { const int default_duration = OPT_GET("Timing/Default Duration")->GetInt(); - markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms)); - markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms + default_duration)); + active_markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms)); + active_markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms + default_duration)); timing_modified = true; UpdateSelection(); } @@ -416,8 +469,8 @@ void AudioTimingControllerDialogue::Revert() if (new_start.GetMS() != 0 || new_end.GetMS() != 0) { - markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(new_start.GetMS())); - markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(new_end.GetMS())); + active_markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(new_start.GetMS())); + active_markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(new_end.GetMS())); timing_modified = false; UpdateSelection(); } @@ -428,7 +481,7 @@ bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivi { SampleRange range(sample-sensitivity, sample+sensitivity); - return range.contains(markers[0]) || range.contains(markers[1]); + return range.contains(active_markers[0]) || range.contains(active_markers[1]); } AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sensitivity) @@ -474,7 +527,7 @@ AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int se void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position) { - assert(marker == &markers[0] || marker == &markers[1]); + assert(marker == &active_markers[0] || marker == &active_markers[1]); SetMarker(static_cast(marker), new_position); } @@ -491,3 +544,47 @@ void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, if (auto_commit->GetBool()) Commit(); UpdateSelection(); } + +void AudioTimingControllerDialogue::RegenerateInactiveLines() +{ + switch (inactive_line_mode->GetInt()) + { + case 1: // Preview line only + inactive_markers.clear(); + if (AssDialogue *line = context->selectionController->GetActiveLine()) + { + std::list::iterator it = find(context->ass->Line.begin(), context->ass->Line.end(), line); + while (--it != context->ass->Line.begin() && !dynamic_cast(*it)) ; + if (AssDialogue *prev = dynamic_cast(*it)) + { + inactive_markers.push_back(InactiveLineMarker( + context->audioController->SamplesFromMilliseconds(prev->Start.GetMS()), true)); + inactive_markers.push_back(InactiveLineMarker( + context->audioController->SamplesFromMilliseconds(prev->End.GetMS()), false)); + } + } + break; + case 2: // All inactive lines + { + inactive_markers.clear(); + AssDialogue *active_line = context->selectionController->GetActiveLine(); + for (std::list::const_iterator it = context->ass->Line.begin(); it != context->ass->Line.end(); ++it) + { + AssDialogue *line = dynamic_cast(*it); + if (line && line != active_line) + { + inactive_markers.push_back(InactiveLineMarker( + context->audioController->SamplesFromMilliseconds(line->Start.GetMS()), true)); + inactive_markers.push_back(InactiveLineMarker( + context->audioController->SamplesFromMilliseconds(line->End.GetMS()), false)); + } + } + break; + } + default: + if (inactive_markers.empty()) + return; + inactive_markers.clear(); + } + AnnounceUpdatedStyleRanges(); +}