2010-12-08 04:36:10 +01:00
|
|
|
// Copyright (c) 2010, Niels Martin Hansen
|
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
|
|
// may be used to endorse or promote products derived from this software
|
|
|
|
// without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
//
|
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
|
|
|
#include "ass_dialogue.h"
|
2010-12-08 09:10:00 +01:00
|
|
|
#include "ass_file.h"
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
#include "audio_controller.h"
|
2014-05-23 00:40:16 +02:00
|
|
|
#include "audio_marker.h"
|
|
|
|
#include "audio_rendering_style.h"
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "audio_timing.h"
|
2012-02-10 01:04:34 +01:00
|
|
|
#include "command/command.h"
|
2011-09-28 21:44:44 +02:00
|
|
|
#include "include/aegisub/context.h"
|
2013-01-07 02:50:09 +01:00
|
|
|
#include "options.h"
|
2011-10-19 06:19:01 +02:00
|
|
|
#include "pen.h"
|
2011-07-15 06:04:13 +02:00
|
|
|
#include "selection_controller.h"
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "utils.h"
|
|
|
|
|
2014-07-06 16:28:58 +02:00
|
|
|
#include <libaegisub/ass/time.h>
|
2014-04-23 22:53:24 +02:00
|
|
|
#include <libaegisub/make_unique.h>
|
2014-04-23 01:21:53 +02:00
|
|
|
|
2014-03-22 00:11:56 +01:00
|
|
|
#include <boost/range/algorithm.hpp>
|
|
|
|
#include <wx/pen.h>
|
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
namespace {
|
2012-02-10 01:04:24 +01:00
|
|
|
class TimeableLine;
|
|
|
|
|
|
|
|
/// @class DialogueTimingMarker
|
2010-12-08 04:36:10 +01:00
|
|
|
/// @brief AudioMarker implementation for AudioTimingControllerDialogue
|
|
|
|
///
|
|
|
|
/// Audio marker intended to live in pairs of two, taking styles depending
|
|
|
|
/// on which marker in the pair is to the left and which is to the right.
|
2014-03-13 02:39:07 +01:00
|
|
|
class DialogueTimingMarker final : public AudioMarker {
|
2012-02-02 00:58:58 +01:00
|
|
|
/// Current ms position of this marker
|
|
|
|
int position;
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
/// Draw style for the marker
|
2012-02-10 01:04:24 +01:00
|
|
|
const Pen *style;
|
|
|
|
|
|
|
|
/// Feet style for the marker
|
2010-12-08 04:36:10 +01:00
|
|
|
FeetStyle feet;
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Rendering style of the owning line, needed for sorting
|
|
|
|
AudioRenderingStyle type;
|
2011-10-19 06:19:01 +02:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The line which owns this marker
|
|
|
|
TimeableLine *line;
|
2011-10-19 06:19:01 +02:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
public:
|
2013-11-21 18:13:36 +01:00
|
|
|
int GetPosition() const override { return position; }
|
|
|
|
wxPen GetStyle() const override { return *style; }
|
|
|
|
FeetStyle GetFeet() const override { return feet; }
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Move the marker to a new position
|
2012-02-02 00:58:58 +01:00
|
|
|
/// @param new_position The position to move the marker to, in milliseconds
|
2010-12-08 04:36:10 +01:00
|
|
|
///
|
2012-02-10 01:04:24 +01:00
|
|
|
/// This notifies the owning line of the change, so that it can ensure that
|
|
|
|
/// this marker has the appropriate rendering style.
|
2012-02-02 00:58:58 +01:00
|
|
|
void SetPosition(int new_position);
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Constructor
|
|
|
|
/// @param position Initial position of this marker
|
|
|
|
/// @param style Rendering style of this marker
|
|
|
|
/// @param feet Foot style of this marker
|
|
|
|
/// @param type Type of this marker, used only for sorting
|
|
|
|
/// @param line Line which this is a marker for
|
|
|
|
DialogueTimingMarker(int position, const Pen *style, FeetStyle feet, AudioRenderingStyle type, TimeableLine *line)
|
|
|
|
: position(position)
|
|
|
|
, style(style)
|
|
|
|
, feet(feet)
|
|
|
|
, type(type)
|
|
|
|
, line(line)
|
|
|
|
{
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
DialogueTimingMarker(DialogueTimingMarker const& other, TimeableLine *line)
|
|
|
|
: position(other.position)
|
|
|
|
, style(other.style)
|
|
|
|
, feet(other.feet)
|
|
|
|
, type(other.type)
|
|
|
|
, line(line)
|
|
|
|
{
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Get the line which this is a marker for
|
|
|
|
TimeableLine *GetLine() const { return line; }
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2011-11-18 23:56:45 +01:00
|
|
|
/// Implicit decay to the position of the marker
|
2012-02-02 00:58:58 +01:00
|
|
|
operator int() const { return position; }
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
/// Comparison operator
|
|
|
|
///
|
|
|
|
/// Compares first on position, then on audio rendering style so that the
|
|
|
|
/// markers for the active line end up after those for the inactive lines.
|
|
|
|
bool operator<(DialogueTimingMarker const& other) const
|
|
|
|
{
|
|
|
|
if (position < other.position) return true;
|
|
|
|
if (position > other.position) return false;
|
|
|
|
return type < other.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Swap the rendering style of this marker with that of the passed marker
|
|
|
|
void SwapStyles(DialogueTimingMarker &other)
|
|
|
|
{
|
|
|
|
std::swap(style, other.style);
|
|
|
|
std::swap(feet, other.feet);
|
|
|
|
}
|
2011-11-18 23:56:45 +01:00
|
|
|
};
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// A comparison predicate for pointers to dialogue markers and millisecond positions
|
|
|
|
struct marker_ptr_cmp
|
|
|
|
{
|
|
|
|
bool operator()(const DialogueTimingMarker *lft, const DialogueTimingMarker *rgt) const
|
|
|
|
{
|
|
|
|
return *lft < *rgt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator()(const DialogueTimingMarker *lft, int rgt) const
|
|
|
|
{
|
|
|
|
return *lft < rgt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator()(int lft, const DialogueTimingMarker *rgt) const
|
|
|
|
{
|
|
|
|
return lft < *rgt;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/// @class TimeableLine
|
|
|
|
/// @brief A single dialogue line which can be timed via AudioTimingControllerDialogue
|
|
|
|
///
|
|
|
|
/// This class provides markers and styling ranges for a single dialogue line,
|
|
|
|
/// both active and inactive. In addition, it can apply changes made via those
|
|
|
|
/// markers to the tracked dialogue line.
|
|
|
|
class TimeableLine {
|
|
|
|
/// The current tracked dialogue line
|
2014-03-22 00:01:24 +01:00
|
|
|
AssDialogue *line = nullptr;
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The rendering style of this line
|
|
|
|
AudioRenderingStyle style;
|
|
|
|
|
|
|
|
/// One of the markers. Initially the left marker, but the user may change this.
|
|
|
|
DialogueTimingMarker marker1;
|
|
|
|
/// One of the markers. Initially the right marker, but the user may change this.
|
|
|
|
DialogueTimingMarker marker2;
|
|
|
|
|
|
|
|
/// Pointer to whichever marker happens to be on the left
|
|
|
|
DialogueTimingMarker *left_marker;
|
|
|
|
/// Pointer to whichever marker happens to be on the right
|
|
|
|
DialogueTimingMarker *right_marker;
|
2011-11-18 23:58:12 +01:00
|
|
|
|
|
|
|
public:
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Constructor
|
|
|
|
/// @param style Rendering style to use for this line's time range
|
|
|
|
/// @param style_left The rendering style for the start marker
|
|
|
|
/// @param style_right The rendering style for the end marker
|
|
|
|
TimeableLine(AudioRenderingStyle style, const Pen *style_left, const Pen *style_right)
|
2014-03-22 00:01:24 +01:00
|
|
|
: style(style)
|
|
|
|
, marker1(0, style_left, AudioMarker::Feet_Right, style, this)
|
|
|
|
, marker2(0, style_right, AudioMarker::Feet_Left, style, this)
|
|
|
|
, left_marker(&marker1)
|
|
|
|
, right_marker(&marker2)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
|
|
|
}
|
2011-11-18 23:58:12 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Explicit copy constructor needed due to that the markers have a pointer to this
|
|
|
|
TimeableLine(TimeableLine const& other)
|
2014-03-22 00:01:24 +01:00
|
|
|
: line(other.line)
|
|
|
|
, style(other.style)
|
|
|
|
, marker1(*other.left_marker, this)
|
|
|
|
, marker2(*other.right_marker, this)
|
|
|
|
, left_marker(&marker1)
|
|
|
|
, right_marker(&marker2)
|
2011-11-18 23:58:12 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Get the tracked dialogue line
|
|
|
|
AssDialogue *GetLine() const { return line; }
|
|
|
|
|
|
|
|
/// Get the time range for this line
|
|
|
|
operator TimeRange() const { return TimeRange(*left_marker, *right_marker); }
|
|
|
|
|
|
|
|
/// Add this line's style to the style ranges
|
|
|
|
void GetStyleRange(AudioRenderingStyleRanges *ranges) const
|
|
|
|
{
|
|
|
|
ranges->AddRange(*left_marker, *right_marker, style);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get this line's markers
|
|
|
|
/// @param c Vector to add the markers to
|
2014-05-12 21:07:46 +02:00
|
|
|
template<typename Container>
|
|
|
|
void GetMarkers(Container *c) const
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
|
|
|
c->push_back(left_marker);
|
|
|
|
c->push_back(right_marker);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the leftmost of the markers
|
|
|
|
DialogueTimingMarker *GetLeftMarker() { return left_marker; }
|
2014-05-15 16:13:09 +02:00
|
|
|
const DialogueTimingMarker *GetLeftMarker() const { return left_marker; }
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
/// Get the rightmost of the markers
|
|
|
|
DialogueTimingMarker *GetRightMarker() { return right_marker; }
|
2014-05-15 16:13:09 +02:00
|
|
|
const DialogueTimingMarker *GetRightMarker() const { return right_marker; }
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
/// Does this line have a marker in the given range?
|
|
|
|
bool ContainsMarker(TimeRange const& range) const
|
|
|
|
{
|
|
|
|
return range.contains(marker1) || range.contains(marker2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if the markers have the correct styles, and correct them if needed
|
|
|
|
void CheckMarkers()
|
|
|
|
{
|
|
|
|
if (*right_marker < *left_marker)
|
|
|
|
{
|
|
|
|
marker1.SwapStyles(marker2);
|
|
|
|
std::swap(left_marker, right_marker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Apply any changes made here to the tracked dialogue line
|
|
|
|
void Apply()
|
|
|
|
{
|
|
|
|
if (line)
|
|
|
|
{
|
|
|
|
line->Start = left_marker->GetPosition();
|
|
|
|
line->End = right_marker->GetPosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the dialogue line which this is tracking and reset the markers to
|
|
|
|
/// the line's time range
|
2012-05-04 04:53:03 +02:00
|
|
|
/// @return Were the markers actually set to the line's time?
|
|
|
|
bool SetLine(AssDialogue *new_line)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
2012-05-04 04:53:03 +02:00
|
|
|
if (!line || new_line->End > 0)
|
|
|
|
{
|
|
|
|
line = new_line;
|
|
|
|
marker1.SetPosition(new_line->Start);
|
|
|
|
marker2.SetPosition(new_line->End);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
line = new_line;
|
|
|
|
return false;
|
|
|
|
}
|
2012-02-10 01:04:24 +01:00
|
|
|
}
|
2011-11-18 23:58:12 +01:00
|
|
|
};
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
void DialogueTimingMarker::SetPosition(int new_position) {
|
|
|
|
position = new_position;
|
|
|
|
line->CheckMarkers();
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
/// @class AudioTimingControllerDialogue
|
|
|
|
/// @brief Default timing mode for dialogue subtitles
|
|
|
|
///
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Displays a start and end marker for an active subtitle line, and possibly
|
|
|
|
/// some of the inactive lines. The markers for the active line can be dragged,
|
|
|
|
/// updating the audio selection and the start/end time of that line. In
|
|
|
|
/// addition, any markers for inactive lines that start/end at the same time
|
|
|
|
/// as the active line starts/ends can optionally be dragged along with the
|
|
|
|
/// active line's markers, updating those lines as well.
|
2014-03-13 02:39:07 +01:00
|
|
|
class AudioTimingControllerDialogue final : public AudioTimingController {
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The rendering style for the active line's start marker
|
2014-05-12 18:30:14 +02:00
|
|
|
Pen style_left{"Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness"};
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The rendering style for the active line's end marker
|
2014-05-12 18:30:14 +02:00
|
|
|
Pen style_right{"Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness"};
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The rendering style for the start and end markers of inactive lines
|
2014-05-12 18:30:14 +02:00
|
|
|
Pen style_inactive{"Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness"};
|
2011-11-18 23:58:12 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The currently active line
|
|
|
|
TimeableLine active_line;
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Inactive lines which are currently modifiable
|
|
|
|
std::list<TimeableLine> inactive_lines;
|
|
|
|
|
2012-03-12 01:07:27 +01:00
|
|
|
/// Selected lines which are currently modifiable
|
|
|
|
std::list<TimeableLine> selected_lines;
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// All audio markers for active and inactive lines, sorted by position
|
|
|
|
std::vector<DialogueTimingMarker*> markers;
|
2012-02-02 00:59:23 +01:00
|
|
|
|
2011-11-16 20:56:00 +01:00
|
|
|
/// Marker provider for video keyframes
|
|
|
|
AudioMarkerProviderKeyframes keyframes_provider;
|
|
|
|
|
2012-02-02 00:59:12 +01:00
|
|
|
/// Marker provider for video playback position
|
|
|
|
VideoPositionMarkerProvider video_position_provider;
|
|
|
|
|
2012-04-27 21:07:29 +02:00
|
|
|
/// Marker provider for seconds lines
|
|
|
|
SecondsMarkerProvider seconds_provider;
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// The set of lines which have been modified and need to have their
|
|
|
|
/// changes applied on commit
|
|
|
|
std::set<TimeableLine*> modified_lines;
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2010-12-08 09:10:00 +01:00
|
|
|
/// Commit id for coalescing purposes when in auto commit mode
|
2014-03-22 00:01:24 +01:00
|
|
|
int commit_id =-1;
|
2010-12-08 09:10:00 +01:00
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
/// The owning project context
|
|
|
|
agi::Context *context;
|
|
|
|
|
2014-05-14 15:40:01 +02:00
|
|
|
/// The time which was clicked on for alt-dragging mode
|
|
|
|
int clicked_ms;
|
2014-05-12 21:07:46 +02:00
|
|
|
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
/// Index of marker serving as tap marker
|
|
|
|
/// For AudioTimingControllerDialogue:
|
|
|
|
/// - 0 is left marker
|
|
|
|
/// - 1 is right marker
|
|
|
|
size_t tap_marker_idx = 0;
|
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
/// Autocommit option
|
2014-05-12 18:30:14 +02:00
|
|
|
const agi::OptionValue *auto_commit = OPT_GET("Audio/Auto/Commit");
|
|
|
|
const agi::OptionValue *inactive_line_mode = OPT_GET("Audio/Inactive Lines Display Mode");
|
|
|
|
const agi::OptionValue *inactive_line_comments = OPT_GET("Audio/Display/Draw/Inactive Comments");
|
|
|
|
const agi::OptionValue *drag_timing = OPT_GET("Audio/Drag Timing");
|
2011-11-18 23:58:12 +01:00
|
|
|
|
|
|
|
agi::signal::Connection commit_connection;
|
|
|
|
agi::signal::Connection audio_open_connection;
|
|
|
|
agi::signal::Connection inactive_line_mode_connection;
|
2012-01-10 21:03:31 +01:00
|
|
|
agi::signal::Connection inactive_line_comment_connection;
|
2012-10-05 05:22:54 +02:00
|
|
|
agi::signal::Connection active_line_connection;
|
|
|
|
agi::signal::Connection selection_connection;
|
2011-11-18 23:58:12 +01:00
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
/// Update the audio controller's selection
|
|
|
|
void UpdateSelection();
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Regenerate the list of timeable inactive lines
|
2011-11-18 23:58:12 +01:00
|
|
|
void RegenerateInactiveLines();
|
|
|
|
|
2012-03-12 01:07:27 +01:00
|
|
|
/// Regenerate the list of timeable selected lines
|
|
|
|
void RegenerateSelectedLines();
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Add a line to the list of timeable inactive lines
|
2014-03-25 01:15:14 +01:00
|
|
|
void AddInactiveLine(Selection const& sel, AssDialogue *diag);
|
2012-01-10 02:16:39 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Regenerate the list of active and inactive line markers
|
|
|
|
void RegenerateMarkers();
|
|
|
|
|
2012-03-12 01:07:33 +01:00
|
|
|
/// Get the start markers for the active line and all selected lines
|
|
|
|
std::vector<AudioMarker*> GetLeftMarkers();
|
|
|
|
|
|
|
|
/// Get the end markers for the active line and all selected lines
|
|
|
|
std::vector<AudioMarker*> GetRightMarkers();
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// @brief Set the position of markers and announce the change to the world
|
|
|
|
/// @param upd_markers Markers to move
|
|
|
|
/// @param ms New position of the markers
|
2014-05-14 20:52:41 +02:00
|
|
|
void SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms, int snap_range);
|
2010-12-08 09:10:00 +01:00
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
/// Try to snap all of the active markers to any inactive markers
|
2012-02-02 00:58:58 +01:00
|
|
|
/// @param snap_range Maximum distance to snap in milliseconds
|
2014-05-14 20:52:41 +02:00
|
|
|
/// @param active Markers which should be snapped
|
|
|
|
/// @return The distance the markers were shifted by
|
|
|
|
int SnapMarkers(int snap_range, std::vector<AudioMarker*> const& markers) const;
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
/// Commit all pending changes to the file
|
|
|
|
/// @param user_triggered Is this a user-initiated commit or an autocommit
|
|
|
|
void DoCommit(bool user_triggered);
|
2012-01-08 02:35:11 +01:00
|
|
|
|
2014-03-05 17:28:14 +01:00
|
|
|
void OnSelectedSetChanged();
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2010-12-08 09:10:00 +01:00
|
|
|
// AssFile events
|
|
|
|
void OnFileChanged(int type);
|
|
|
|
|
2010-12-08 04:36:10 +01:00
|
|
|
public:
|
|
|
|
// AudioMarkerProvider interface
|
2013-11-21 18:13:36 +01:00
|
|
|
void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const override;
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
int GetTapMarkerPosition() const override;
|
|
|
|
size_t GetTapMarkerIndex() const override;
|
2010-12-08 04:36:10 +01:00
|
|
|
|
|
|
|
// AudioTimingController interface
|
2013-11-21 18:13:36 +01:00
|
|
|
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const override;
|
|
|
|
void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const override { }
|
|
|
|
void Next(NextMode mode) override;
|
|
|
|
void Prev() override;
|
|
|
|
void Revert() override;
|
|
|
|
void AddLeadIn() override;
|
|
|
|
void AddLeadOut() override;
|
|
|
|
void ModifyLength(int delta, bool shift_following) override;
|
|
|
|
void ModifyStart(int delta) override;
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
void MoveTapMarker(int ms) override;
|
|
|
|
bool NextTapMarker() override;
|
2014-05-14 15:40:01 +02:00
|
|
|
bool IsNearbyMarker(int ms, int sensitivity, bool alt_down) const override;
|
2014-05-12 21:07:46 +02:00
|
|
|
std::vector<AudioMarker*> OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range) override;
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
std::vector<AudioMarker*> OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range) override;
|
2013-11-21 18:13:36 +01:00
|
|
|
void OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range) override;
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2014-05-15 16:42:44 +02:00
|
|
|
// We have no warning messages currently, maybe add the old "Modified" message back later?
|
|
|
|
wxString GetWarningMessage() const override { return wxString(); }
|
|
|
|
TimeRange GetIdealVisibleTimeRange() const override { return active_line; }
|
|
|
|
TimeRange GetPrimaryPlaybackRange() const override { return active_line; }
|
|
|
|
TimeRange GetActiveLineRange() const override { return active_line; }
|
|
|
|
void Commit() override { DoCommit(true); }
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
/// Constructor
|
|
|
|
/// @param c Project context
|
2011-09-28 21:44:44 +02:00
|
|
|
AudioTimingControllerDialogue(agi::Context *c);
|
2010-12-08 04:36:10 +01:00
|
|
|
};
|
|
|
|
|
2011-09-28 21:44:44 +02:00
|
|
|
AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
2014-05-12 18:30:14 +02:00
|
|
|
: active_line(AudioStyle_Primary, &style_left, &style_right)
|
2012-02-10 01:04:24 +01:00
|
|
|
, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Dialogue Mode")
|
2012-02-02 00:59:12 +01:00
|
|
|
, video_position_provider(c)
|
2011-09-28 21:44:44 +02:00
|
|
|
, context(c)
|
2011-11-18 23:58:12 +01:00
|
|
|
, commit_connection(c->ass->AddCommitListener(&AudioTimingControllerDialogue::OnFileChanged, this))
|
|
|
|
, inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
2012-01-10 21:03:31 +01:00
|
|
|
, inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
2014-05-15 16:42:44 +02:00
|
|
|
, active_line_connection(c->selectionController->AddActiveLineListener(&AudioTimingControllerDialogue::Revert, this))
|
2012-10-05 05:22:54 +02:00
|
|
|
, selection_connection(c->selectionController->AddSelectionListener(&AudioTimingControllerDialogue::OnSelectedSetChanged, this))
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2014-04-24 00:48:39 +02:00
|
|
|
keyframes_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
|
|
|
|
video_position_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
|
|
|
|
seconds_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
|
2011-11-18 23:58:22 +01:00
|
|
|
|
|
|
|
Revert();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2012-02-02 00:58:58 +01:00
|
|
|
void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-01-08 02:35:25 +01:00
|
|
|
// The order matters here; later markers are painted on top of earlier
|
|
|
|
// markers, so the markers that we want to end up on top need to appear last
|
2011-11-18 23:58:12 +01:00
|
|
|
|
2012-04-27 21:07:29 +02:00
|
|
|
seconds_provider.GetMarkers(range, out_markers);
|
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
// Copy inactive line markers in the range
|
2012-02-10 01:04:24 +01:00
|
|
|
copy(
|
2014-03-22 00:11:56 +01:00
|
|
|
boost::lower_bound(markers, range.begin(), marker_ptr_cmp()),
|
|
|
|
boost::upper_bound(markers, range.end(), marker_ptr_cmp()),
|
2012-02-10 01:04:24 +01:00
|
|
|
back_inserter(out_markers));
|
2012-01-08 02:35:25 +01:00
|
|
|
|
|
|
|
keyframes_provider.GetMarkers(range, out_markers);
|
2012-02-02 00:59:12 +01:00
|
|
|
video_position_provider.GetMarkers(range, out_markers);
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
int AudioTimingControllerDialogue::GetTapMarkerPosition() const
|
|
|
|
{
|
|
|
|
assert(tap_marker_idx <= 1);
|
|
|
|
|
|
|
|
if (tap_marker_idx == 0) {
|
|
|
|
return *active_line.GetLeftMarker();
|
2018-10-28 20:01:13 +01:00
|
|
|
}
|
|
|
|
else {
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
return *active_line.GetRightMarker();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioTimingControllerDialogue::GetTapMarkerIndex() const
|
|
|
|
{
|
|
|
|
assert(tap_marker_idx <= 1);
|
|
|
|
return tap_marker_idx;
|
|
|
|
}
|
|
|
|
|
2014-03-05 17:28:14 +01:00
|
|
|
void AudioTimingControllerDialogue::OnSelectedSetChanged()
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-03-12 01:07:27 +01:00
|
|
|
RegenerateSelectedLines();
|
|
|
|
RegenerateInactiveLines();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2010-12-08 09:10:00 +01:00
|
|
|
void AudioTimingControllerDialogue::OnFileChanged(int type) {
|
2011-09-15 07:16:32 +02:00
|
|
|
if (type & AssFile::COMMIT_DIAG_TIME)
|
|
|
|
Revert();
|
2011-11-18 23:58:12 +01:00
|
|
|
else if (type & AssFile::COMMIT_DIAG_ADDREM)
|
|
|
|
RegenerateInactiveLines();
|
2010-12-08 09:10:00 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2011-11-18 23:56:45 +01:00
|
|
|
void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
|
|
|
|
{
|
2012-02-10 01:04:24 +01:00
|
|
|
active_line.GetStyleRange(&ranges);
|
2014-03-22 00:01:24 +01:00
|
|
|
for (auto const& line : selected_lines)
|
|
|
|
line.GetStyleRange(&ranges);
|
|
|
|
for (auto const& line : inactive_lines)
|
|
|
|
line.GetStyleRange(&ranges);
|
2011-11-18 23:56:45 +01:00
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-04-27 21:07:07 +02:00
|
|
|
void AudioTimingControllerDialogue::Next(NextMode mode)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-04-27 21:07:07 +02:00
|
|
|
if (mode == TIMING_UNIT)
|
|
|
|
{
|
|
|
|
context->selectionController->NextLine();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int new_end_ms = *active_line.GetRightMarker();
|
|
|
|
|
|
|
|
cmd::call("grid/line/next/create", context);
|
|
|
|
|
2012-05-04 04:53:03 +02:00
|
|
|
if (mode == LINE_RESET_DEFAULT || active_line.GetLine()->End == 0) {
|
2012-04-27 21:07:07 +02:00
|
|
|
const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
|
|
|
|
// Setting right first here so that they don't get switched and the
|
|
|
|
// same marker gets set twice
|
|
|
|
active_line.GetRightMarker()->SetPosition(new_end_ms + default_duration);
|
|
|
|
active_line.GetLeftMarker()->SetPosition(new_end_ms);
|
2014-03-22 00:11:56 +01:00
|
|
|
boost::sort(markers, marker_ptr_cmp());
|
2012-04-27 21:07:07 +02:00
|
|
|
modified_lines.insert(&active_line);
|
|
|
|
UpdateSelection();
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::Prev()
|
|
|
|
{
|
2011-09-28 21:44:44 +02:00
|
|
|
context->selectionController->PrevLine();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
void AudioTimingControllerDialogue::DoCommit(bool user_triggered)
|
|
|
|
{
|
2010-12-08 04:36:10 +01:00
|
|
|
// Store back new times
|
2012-02-10 01:04:24 +01:00
|
|
|
if (modified_lines.size())
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2014-03-22 00:01:24 +01:00
|
|
|
for (auto line : modified_lines)
|
|
|
|
line->Apply();
|
2010-12-08 09:10:00 +01:00
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
commit_connection.Block();
|
2010-12-08 09:10:00 +01:00
|
|
|
if (user_triggered)
|
|
|
|
{
|
2011-09-28 21:44:44 +02:00
|
|
|
context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
|
2010-12-08 09:10:00 +01:00
|
|
|
commit_id = -1; // never coalesce with a manually triggered commit
|
|
|
|
}
|
|
|
|
else
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
2013-11-21 18:13:36 +01:00
|
|
|
AssDialogue *amend = modified_lines.size() == 1 ? (*modified_lines.begin())->GetLine() : nullptr;
|
2012-02-10 01:04:24 +01:00
|
|
|
commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, amend);
|
|
|
|
}
|
2010-12-08 09:10:00 +01:00
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
commit_connection.Unblock();
|
2012-02-10 01:04:24 +01:00
|
|
|
modified_lines.clear();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::Revert()
|
|
|
|
{
|
2012-05-16 16:23:10 +02:00
|
|
|
commit_id = -1;
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
tap_marker_idx = 0;
|
2012-05-16 16:23:10 +02:00
|
|
|
|
2011-09-28 21:44:44 +02:00
|
|
|
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-02-10 01:04:24 +01:00
|
|
|
modified_lines.clear();
|
2012-05-04 04:53:03 +02:00
|
|
|
if (active_line.SetLine(line))
|
|
|
|
{
|
|
|
|
AnnounceUpdatedPrimaryRange();
|
|
|
|
if (inactive_line_mode->GetInt() == 0)
|
|
|
|
AnnounceUpdatedStyleRanges();
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
AnnounceUpdatedTapMarker();
|
2012-05-04 04:53:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
modified_lines.insert(&active_line);
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
2012-02-10 01:04:24 +01:00
|
|
|
|
2012-01-10 21:03:31 +01:00
|
|
|
RegenerateInactiveLines();
|
2012-03-12 01:07:27 +01:00
|
|
|
RegenerateSelectedLines();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2012-02-22 23:00:54 +01:00
|
|
|
void AudioTimingControllerDialogue::AddLeadIn()
|
|
|
|
{
|
|
|
|
DialogueTimingMarker *m = active_line.GetLeftMarker();
|
2014-05-14 20:52:41 +02:00
|
|
|
SetMarkers({ m }, *m - OPT_GET("Audio/Lead/IN")->GetInt(), 0);
|
2012-02-22 23:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::AddLeadOut()
|
|
|
|
{
|
|
|
|
DialogueTimingMarker *m = active_line.GetRightMarker();
|
2014-05-14 20:52:41 +02:00
|
|
|
SetMarkers({ m }, *m + OPT_GET("Audio/Lead/OUT")->GetInt(), 0);
|
2012-02-22 23:00:54 +01:00
|
|
|
}
|
|
|
|
|
2012-07-14 15:35:15 +02:00
|
|
|
void AudioTimingControllerDialogue::ModifyLength(int delta, bool) {
|
|
|
|
DialogueTimingMarker *m = active_line.GetRightMarker();
|
2013-12-12 00:11:06 +01:00
|
|
|
SetMarkers({ m },
|
2014-05-14 20:52:41 +02:00
|
|
|
std::max<int>(*m + delta * 10, *active_line.GetLeftMarker()), 0);
|
2012-07-14 15:35:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::ModifyStart(int delta) {
|
|
|
|
DialogueTimingMarker *m = active_line.GetLeftMarker();
|
2013-12-12 00:11:06 +01:00
|
|
|
SetMarkers({ m },
|
2014-05-14 20:52:41 +02:00
|
|
|
std::min<int>(*m + delta * 10, *active_line.GetRightMarker()), 0);
|
2012-07-14 15:35:15 +02:00
|
|
|
}
|
|
|
|
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
void AudioTimingControllerDialogue::MoveTapMarker(int ms) {
|
|
|
|
// Fix rounding error
|
|
|
|
ms = (ms + 5) / 10 * 10;
|
|
|
|
|
|
|
|
DialogueTimingMarker *left = active_line.GetLeftMarker();
|
|
|
|
DialogueTimingMarker *right = active_line.GetRightMarker();
|
|
|
|
|
|
|
|
clicked_ms = INT_MIN;
|
|
|
|
if (tap_marker_idx == 0) {
|
|
|
|
// Moving left marker (start time of the line)
|
|
|
|
if (ms > *right) SetMarkers({ right }, ms, 0);
|
|
|
|
SetMarkers({ left }, ms, 0);
|
2018-10-28 20:01:13 +01:00
|
|
|
}
|
|
|
|
else {
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
// Moving right marker (end time of the line)
|
|
|
|
if (ms < *left) SetMarkers({ left }, ms, 0);
|
|
|
|
SetMarkers({ right }, ms, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AudioTimingControllerDialogue::NextTapMarker() {
|
|
|
|
if (tap_marker_idx == 0) {
|
|
|
|
tap_marker_idx = 1;
|
|
|
|
AnnounceUpdatedTapMarker();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-14 15:40:01 +02:00
|
|
|
bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity, bool alt_down) const
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-02-10 01:04:24 +01:00
|
|
|
assert(sensitivity >= 0);
|
2014-05-14 15:40:01 +02:00
|
|
|
return alt_down || active_line.ContainsMarker(TimeRange(ms-sensitivity, ms+sensitivity));
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2014-05-12 21:07:46 +02:00
|
|
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
|
|
|
assert(sensitivity >= 0);
|
2012-02-10 01:04:24 +01:00
|
|
|
assert(snap_range >= 0);
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-03-12 01:07:33 +01:00
|
|
|
std::vector<AudioMarker*> ret;
|
|
|
|
|
2014-05-14 21:13:40 +02:00
|
|
|
clicked_ms = INT_MIN;
|
2014-05-14 15:40:01 +02:00
|
|
|
if (alt_down)
|
|
|
|
{
|
|
|
|
clicked_ms = ms;
|
|
|
|
active_line.GetMarkers(&ret);
|
|
|
|
for (auto const& line : selected_lines)
|
|
|
|
line.GetMarkers(&ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
DialogueTimingMarker *left = active_line.GetLeftMarker();
|
|
|
|
DialogueTimingMarker *right = active_line.GetRightMarker();
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
int dist_l = tabs(*left - ms);
|
|
|
|
int dist_r = tabs(*right - ms);
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
if (dist_l > sensitivity && dist_r > sensitivity)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-02-10 01:04:24 +01:00
|
|
|
// Clicked far from either marker:
|
|
|
|
// Insta-set the left marker to the clicked position and return the
|
|
|
|
// right as the dragged one, such that if the user does start dragging,
|
|
|
|
// he will create a new selection from scratch
|
2012-03-12 01:07:33 +01:00
|
|
|
std::vector<AudioMarker*> jump = GetLeftMarkers();
|
2012-04-12 02:04:31 +02:00
|
|
|
ret = drag_timing->GetBool() ? GetRightMarkers() : jump;
|
2012-03-12 01:07:33 +01:00
|
|
|
// Get ret before setting as setting may swap left/right
|
2014-05-14 20:52:41 +02:00
|
|
|
SetMarkers(jump, ms, snap_range);
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
// Also change tap marker to left marker
|
|
|
|
tap_marker_idx = 0;
|
2012-02-10 01:04:24 +01:00
|
|
|
return ret;
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
DialogueTimingMarker *clicked = dist_l <= dist_r ? left : right;
|
|
|
|
|
|
|
|
if (ctrl_down)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2012-02-10 01:04:24 +01:00
|
|
|
// The use of GetPosition here is important, as otherwise it'll start
|
|
|
|
// after lines ending at the same time as the active line begins
|
2014-03-22 00:11:56 +01:00
|
|
|
auto it = boost::lower_bound(markers, clicked->GetPosition(), marker_ptr_cmp());
|
2014-05-12 21:07:46 +02:00
|
|
|
for (; it != markers.end() && !(*clicked < **it); ++it)
|
2012-02-10 01:04:24 +01:00
|
|
|
ret.push_back(*it);
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
2012-02-10 01:04:24 +01:00
|
|
|
else
|
|
|
|
ret.push_back(clicked);
|
2010-12-08 04:36:10 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
// Left-click within drag range should still move the left marker to the
|
|
|
|
// clicked position, but not the right marker
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
if (clicked == left) {
|
2014-05-14 20:52:41 +02:00
|
|
|
SetMarkers(ret, ms, snap_range);
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Also change tap marker
|
|
|
|
if (clicked == left) {
|
|
|
|
tap_marker_idx = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tap_marker_idx = 1;
|
|
|
|
}
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
return ret;
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
if (ctrl_down) {
|
2018-10-28 20:01:13 +01:00
|
|
|
// Ctrl-right-click: play audio
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
context->audioController->PlayToEnd(ms);
|
|
|
|
return {};
|
2018-10-28 20:01:13 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Normal right-click: move right marker
|
Audio/Timing: implement tap-to-time
Tap-to-time provides the user the ability to tap to the lyrics/syllables
of the song in order to time lines or karaoke. It consists of these
extra UI interactions:
- **Indicator**: tap marker: a designated marker that can be moved to
the current audio position; indicated in:
- the audio display by a green arrow underneath a marker
- the karaoke display by a green-colored syllable
- **Control**: tap marker: the tap marker can be changed by selecting
syllables on audio display in karaoke mode, or clicking the markers on
audio display in dialogue mode
- **Control**: ctrl-right-click audio display: starts playing the audio
from that exact position until the end of the file
- **Option**: Timing/Tap To Time: enables the tap marker indicator and
commands
- **Button**: time_opt_tap_to_time: toggles the Timing/Tap To Time option
- **Button**: time_tap_connect (hotkey I): a command that:
- moves the tap marker's position to the current playing audio
position
- sets the next marker to be the tap marker
- if the tap marker is already the last marker AND BOTH autocommit AND
next-line-on-commit is ON, will move onto the next line
- if moved on to the next line, also sets the start marker to the current
audio position, so the two lines are connected, and moves to the
next tap marker (essentially reinvoking time_tap_connect once)
- **Button**: time_tap_no_connect (hotkey O): similar to
time_tap_connect, except it will not set the next line's start
position even if moved to the next line
Expected workflow:
1) User loads song lyrics
2) User splits each line into syllables
3) User turns on tap-to-time, autocommit, and next-line-on-commit
4) User plays audio from beginning, tapping time_tap_connect to each
syllable, occasionally tapping time_tap_no_connect when a break between
lines is desired
5) If user messes up a line, they can set the tap marker to where they
want to restart from, and ctrl-right-click to start the audio a few
seconds before it
6) Syllables can be split/merged at will, and adjustments to timing can
be done using normal karaoke timing controls
2018-10-21 09:42:33 +02:00
|
|
|
clicked_ms = INT_MIN;
|
|
|
|
std::vector<AudioMarker*> ret = GetRightMarkers();
|
|
|
|
SetMarkers(ret, ms, snap_range);
|
|
|
|
tap_marker_idx = 1;
|
|
|
|
return ret;
|
|
|
|
}
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:13 +01:00
|
|
|
void AudioTimingControllerDialogue::OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range)
|
2010-12-08 04:36:10 +01:00
|
|
|
{
|
2014-05-14 20:52:41 +02:00
|
|
|
SetMarkers(markers, new_position, snap_range);
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::UpdateSelection()
|
|
|
|
{
|
2010-12-08 09:09:16 +01:00
|
|
|
AnnounceUpdatedPrimaryRange();
|
2011-11-18 23:56:45 +01:00
|
|
|
AnnounceUpdatedStyleRanges();
|
2010-12-08 04:36:10 +01:00
|
|
|
}
|
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms, int snap_range)
|
2010-12-08 09:10:00 +01:00
|
|
|
{
|
2014-05-12 21:07:46 +02:00
|
|
|
if (upd_markers.empty()) return;
|
|
|
|
|
2014-05-14 21:13:40 +02:00
|
|
|
int shift = clicked_ms != INT_MIN ? ms - clicked_ms : 0;
|
2014-05-14 20:52:41 +02:00
|
|
|
if (shift) clicked_ms = ms;
|
2014-05-12 21:07:46 +02:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
// Since we're moving markers, the sorted list of markers will need to be
|
|
|
|
// resorted. To avoid resorting the entire thing, find the subrange that
|
|
|
|
// is effected.
|
|
|
|
int min_ms = ms;
|
|
|
|
int max_ms = ms;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (AudioMarker *upd_marker : upd_markers)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
2014-05-12 21:07:46 +02:00
|
|
|
auto marker = static_cast<DialogueTimingMarker*>(upd_marker);
|
|
|
|
if (shift < 0) {
|
|
|
|
min_ms = std::min<int>(*marker + shift, min_ms);
|
|
|
|
max_ms = std::max<int>(*marker, max_ms);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
min_ms = std::min<int>(*marker, min_ms);
|
|
|
|
max_ms = std::max<int>(*marker + shift, max_ms);
|
|
|
|
}
|
2012-02-10 01:04:24 +01:00
|
|
|
}
|
|
|
|
|
2014-03-22 00:11:56 +01:00
|
|
|
auto begin = boost::lower_bound(markers, min_ms, marker_ptr_cmp());
|
2012-11-04 04:53:03 +01:00
|
|
|
auto end = upper_bound(begin, markers.end(), max_ms, marker_ptr_cmp());
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
// Update the markers
|
2014-05-12 21:07:46 +02:00
|
|
|
for (auto upd_marker : upd_markers)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
2014-05-12 21:07:46 +02:00
|
|
|
auto marker = static_cast<DialogueTimingMarker*>(upd_marker);
|
2014-05-14 21:13:40 +02:00
|
|
|
marker->SetPosition(clicked_ms != INT_MIN ? *marker + shift : ms);
|
2012-02-10 01:04:24 +01:00
|
|
|
modified_lines.insert(marker->GetLine());
|
|
|
|
}
|
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
int snap = SnapMarkers(snap_range, upd_markers);
|
2014-05-14 21:13:40 +02:00
|
|
|
if (clicked_ms != INT_MIN)
|
2014-05-14 20:52:41 +02:00
|
|
|
clicked_ms += snap;
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
// Resort the range
|
|
|
|
sort(begin, end, marker_ptr_cmp());
|
|
|
|
|
|
|
|
if (auto_commit->GetBool()) DoCommit(false);
|
2010-12-08 09:10:00 +01:00
|
|
|
UpdateSelection();
|
2012-02-10 01:04:24 +01:00
|
|
|
|
2012-02-02 23:57:53 +01:00
|
|
|
AnnounceMarkerMoved();
|
2010-12-08 09:10:00 +01:00
|
|
|
}
|
2011-11-18 23:58:12 +01:00
|
|
|
|
|
|
|
void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
|
|
|
{
|
2014-05-15 16:42:44 +02:00
|
|
|
using pred = bool(*)(AssDialogue const&);
|
|
|
|
auto predicate = inactive_line_comments->GetBool()
|
2014-05-19 15:53:44 +02:00
|
|
|
? static_cast<pred>([](AssDialogue const&) { return true; })
|
|
|
|
: static_cast<pred>([](AssDialogue const& d) { return !d.Comment; });
|
2012-01-10 21:03:31 +01:00
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
bool was_empty = inactive_lines.empty();
|
|
|
|
inactive_lines.clear();
|
2012-02-02 00:59:23 +01:00
|
|
|
|
2014-03-25 01:15:14 +01:00
|
|
|
auto const& sel = context->selectionController->GetSelectedSet();
|
2012-05-05 04:10:57 +02:00
|
|
|
|
2012-01-10 02:16:39 +01:00
|
|
|
switch (int mode = inactive_line_mode->GetInt())
|
2011-11-18 23:58:12 +01:00
|
|
|
{
|
2012-01-10 02:16:39 +01:00
|
|
|
case 1: // Previous line only
|
|
|
|
case 2: // Previous and next lines
|
2011-11-18 23:58:12 +01:00
|
|
|
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
|
|
|
{
|
2014-04-04 17:11:09 +02:00
|
|
|
auto current_line = context->ass->iterator_to(*line);
|
2014-03-07 18:02:24 +01:00
|
|
|
if (current_line == context->ass->Events.end())
|
2012-07-09 01:22:27 +02:00
|
|
|
break;
|
2012-01-10 02:16:39 +01:00
|
|
|
|
2014-03-22 00:41:22 +01:00
|
|
|
if (current_line != context->ass->Events.begin())
|
|
|
|
{
|
|
|
|
auto prev = current_line;
|
|
|
|
while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
|
|
|
|
if (predicate(*prev))
|
|
|
|
AddInactiveLine(sel, &*prev);
|
|
|
|
}
|
2012-01-10 02:16:39 +01:00
|
|
|
|
|
|
|
if (mode == 2)
|
2011-11-18 23:58:12 +01:00
|
|
|
{
|
2014-03-09 16:45:36 +01:00
|
|
|
auto next = std::find_if(++current_line, context->ass->Events.end(), predicate);
|
2014-03-07 18:02:24 +01:00
|
|
|
if (next != context->ass->Events.end())
|
2014-03-07 19:58:51 +01:00
|
|
|
AddInactiveLine(sel, &*next);
|
2011-11-18 23:58:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2012-01-10 02:16:39 +01:00
|
|
|
case 3: // All inactive lines
|
2011-11-18 23:58:12 +01:00
|
|
|
{
|
|
|
|
AssDialogue *active_line = context->selectionController->GetActiveLine();
|
2014-03-07 18:02:24 +01:00
|
|
|
for (auto& line : context->ass->Events)
|
2011-11-18 23:58:12 +01:00
|
|
|
{
|
2012-11-04 04:53:03 +01:00
|
|
|
if (&line != active_line && predicate(line))
|
2014-03-07 19:58:51 +01:00
|
|
|
AddInactiveLine(sel, &line);
|
2011-11-18 23:58:12 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2012-02-02 00:59:23 +01:00
|
|
|
if (was_empty)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
|
|
|
RegenerateMarkers();
|
2011-11-18 23:58:12 +01:00
|
|
|
return;
|
2012-02-10 01:04:24 +01:00
|
|
|
}
|
2011-11-18 23:58:12 +01:00
|
|
|
}
|
2012-01-10 02:16:39 +01:00
|
|
|
|
2011-11-18 23:58:12 +01:00
|
|
|
AnnounceUpdatedStyleRanges();
|
2012-02-10 01:04:24 +01:00
|
|
|
|
|
|
|
RegenerateMarkers();
|
|
|
|
}
|
|
|
|
|
2014-03-25 01:15:14 +01:00
|
|
|
void AudioTimingControllerDialogue::AddInactiveLine(Selection const& sel, AssDialogue *diag)
|
2012-02-10 01:04:24 +01:00
|
|
|
{
|
2012-03-12 01:07:27 +01:00
|
|
|
if (sel.count(diag)) return;
|
|
|
|
|
2012-11-28 16:35:26 +01:00
|
|
|
inactive_lines.emplace_back(AudioStyle_Inactive, &style_inactive, &style_inactive);
|
2012-02-10 01:04:24 +01:00
|
|
|
inactive_lines.back().SetLine(diag);
|
|
|
|
}
|
|
|
|
|
2012-03-12 01:07:27 +01:00
|
|
|
void AudioTimingControllerDialogue::RegenerateSelectedLines()
|
|
|
|
{
|
|
|
|
bool was_empty = selected_lines.empty();
|
|
|
|
selected_lines.clear();
|
|
|
|
|
|
|
|
AssDialogue *active = context->selectionController->GetActiveLine();
|
2014-03-22 00:41:22 +01:00
|
|
|
for (auto line : context->selectionController->GetSelectedSet())
|
2012-03-12 01:07:27 +01:00
|
|
|
{
|
2012-11-04 04:53:03 +01:00
|
|
|
if (line == active) continue;
|
2012-03-12 01:07:27 +01:00
|
|
|
|
2012-11-28 16:35:26 +01:00
|
|
|
selected_lines.emplace_back(AudioStyle_Selected, &style_inactive, &style_inactive);
|
2012-11-04 04:53:03 +01:00
|
|
|
selected_lines.back().SetLine(line);
|
2012-03-12 01:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!selected_lines.empty() || !was_empty)
|
|
|
|
{
|
|
|
|
AnnounceUpdatedStyleRanges();
|
|
|
|
RegenerateMarkers();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-10 01:04:24 +01:00
|
|
|
void AudioTimingControllerDialogue::RegenerateMarkers()
|
|
|
|
{
|
|
|
|
markers.clear();
|
|
|
|
|
|
|
|
active_line.GetMarkers(&markers);
|
2014-03-22 00:01:24 +01:00
|
|
|
for (auto const& line : selected_lines)
|
|
|
|
line.GetMarkers(&markers);
|
|
|
|
for (auto const& line : inactive_lines)
|
|
|
|
line.GetMarkers(&markers);
|
2014-04-01 18:02:53 +02:00
|
|
|
boost::sort(markers, marker_ptr_cmp());
|
2012-02-10 01:04:24 +01:00
|
|
|
|
2012-02-02 23:57:53 +01:00
|
|
|
AnnounceMarkerMoved();
|
2011-11-18 23:58:12 +01:00
|
|
|
}
|
2012-01-08 02:35:11 +01:00
|
|
|
|
2012-03-12 01:07:33 +01:00
|
|
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::GetLeftMarkers()
|
|
|
|
{
|
|
|
|
std::vector<AudioMarker*> ret;
|
|
|
|
ret.reserve(selected_lines.size() + 1);
|
|
|
|
ret.push_back(active_line.GetLeftMarker());
|
2014-03-22 00:01:24 +01:00
|
|
|
for (auto& line : selected_lines)
|
|
|
|
ret.push_back(line.GetLeftMarker());
|
2012-03-12 01:07:33 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::GetRightMarkers()
|
|
|
|
{
|
|
|
|
std::vector<AudioMarker*> ret;
|
|
|
|
ret.reserve(selected_lines.size() + 1);
|
|
|
|
ret.push_back(active_line.GetRightMarker());
|
2014-03-22 00:01:24 +01:00
|
|
|
for (auto& line : selected_lines)
|
|
|
|
ret.push_back(line.GetRightMarker());
|
2012-03-12 01:07:33 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
int AudioTimingControllerDialogue::SnapMarkers(int snap_range, std::vector<AudioMarker*> const& active) const
|
2012-01-08 02:35:11 +01:00
|
|
|
{
|
2014-05-15 16:13:09 +02:00
|
|
|
if (snap_range <= 0 || active.empty()) return 0;
|
|
|
|
|
|
|
|
auto marker_range = [&] {
|
|
|
|
int front = active.front()->GetPosition();
|
|
|
|
int min = front;
|
|
|
|
int max = front;
|
|
|
|
for (auto m : active)
|
|
|
|
{
|
|
|
|
auto pos = m->GetPosition();
|
|
|
|
if (pos < min) min = pos;
|
|
|
|
if (pos > max) max = pos;
|
|
|
|
}
|
|
|
|
return TimeRange{min - snap_range, max + snap_range};
|
|
|
|
}();
|
|
|
|
|
|
|
|
std::vector<int> inactive_markers;
|
|
|
|
inactive_markers.reserve(inactive_lines.size() * 2 + selected_lines.size() * 2 + 2 - active.size());
|
|
|
|
|
2015-03-01 20:09:43 +01:00
|
|
|
// Add a marker to the set to check for snaps if it's in the right time
|
|
|
|
// range, isn't at the same place as a marker already in the set, and isn't
|
|
|
|
// one of the markers being moved
|
2014-05-15 16:13:09 +02:00
|
|
|
auto add_inactive = [&](const DialogueTimingMarker *m, bool check)
|
|
|
|
{
|
|
|
|
if (!marker_range.contains(*m)) return;
|
|
|
|
if (!inactive_markers.empty() && inactive_markers.back() == *m) return;
|
|
|
|
if (check && boost::find(active, m) != end(active)) return;
|
|
|
|
inactive_markers.push_back(*m);
|
|
|
|
};
|
2012-01-08 02:35:11 +01:00
|
|
|
|
2015-03-01 20:09:43 +01:00
|
|
|
bool moving_entire_selection = clicked_ms != INT_MIN;
|
2014-05-14 21:42:44 +02:00
|
|
|
for (auto const& line : inactive_lines)
|
2014-05-15 16:13:09 +02:00
|
|
|
{
|
2015-03-01 20:09:43 +01:00
|
|
|
// If we're alt-dragging the entire selection, there can't be any
|
|
|
|
// markers from inactive lines in the active set, so no need to check
|
|
|
|
// for them
|
|
|
|
add_inactive(line.GetLeftMarker(), !moving_entire_selection);
|
|
|
|
add_inactive(line.GetRightMarker(), !moving_entire_selection);
|
2014-05-15 16:13:09 +02:00
|
|
|
}
|
|
|
|
|
2015-03-01 20:09:43 +01:00
|
|
|
// And similarly, there can't be any inactive markers from selected lines
|
|
|
|
if (!moving_entire_selection)
|
2014-05-15 16:13:09 +02:00
|
|
|
{
|
|
|
|
for (auto const& line : selected_lines)
|
|
|
|
{
|
|
|
|
add_inactive(line.GetLeftMarker(), true);
|
|
|
|
add_inactive(line.GetRightMarker(), true);
|
|
|
|
}
|
|
|
|
add_inactive(active_line.GetLeftMarker(), true);
|
|
|
|
add_inactive(active_line.GetRightMarker(), true);
|
|
|
|
}
|
2014-05-14 21:42:44 +02:00
|
|
|
|
2015-03-01 20:09:43 +01:00
|
|
|
int snap_distance = INT_MAX;
|
2014-05-15 16:13:09 +02:00
|
|
|
auto check = [&](int marker, int pos)
|
|
|
|
{
|
|
|
|
auto dist = marker - pos;
|
2015-03-01 20:09:43 +01:00
|
|
|
if (tabs(dist) < tabs(snap_distance))
|
2014-05-15 16:13:09 +02:00
|
|
|
snap_distance = dist;
|
|
|
|
};
|
|
|
|
|
2014-05-14 20:52:41 +02:00
|
|
|
int prev = -1;
|
2014-05-15 16:13:09 +02:00
|
|
|
AudioMarkerVector snap_markers;
|
2014-05-14 20:52:41 +02:00
|
|
|
for (const auto active_marker : active)
|
2012-01-08 02:35:11 +01:00
|
|
|
{
|
2014-05-14 20:52:41 +02:00
|
|
|
auto pos = active_marker->GetPosition();
|
|
|
|
if (pos == prev) continue;
|
|
|
|
|
2014-05-15 16:13:09 +02:00
|
|
|
snap_markers.clear();
|
2014-05-14 21:42:44 +02:00
|
|
|
TimeRange range(pos - snap_range, pos + snap_range);
|
2014-05-15 16:13:09 +02:00
|
|
|
keyframes_provider.GetMarkers(range, snap_markers);
|
|
|
|
video_position_provider.GetMarkers(range, snap_markers);
|
|
|
|
|
|
|
|
for (const auto marker : snap_markers)
|
|
|
|
{
|
|
|
|
check(marker->GetPosition(), pos);
|
|
|
|
if (snap_distance == 0) return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = boost::lower_bound(inactive_markers, range.begin()); it != end(inactive_markers); ++it)
|
2012-01-08 02:35:11 +01:00
|
|
|
{
|
2014-05-15 16:13:09 +02:00
|
|
|
check(*it, pos);
|
|
|
|
if (snap_distance == 0) return 0;
|
|
|
|
if (*it > pos) break;
|
2012-01-08 02:35:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-01 20:09:43 +01:00
|
|
|
if (tabs(snap_distance) > snap_range)
|
2014-05-14 20:52:41 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (auto m : active)
|
|
|
|
static_cast<DialogueTimingMarker *>(m)->SetPosition(m->GetPosition() + snap_distance);
|
|
|
|
return snap_distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace {
|
|
|
|
|
|
|
|
std::unique_ptr<AudioTimingController> CreateDialogueTimingController(agi::Context *c)
|
|
|
|
{
|
|
|
|
return agi::make_unique<AudioTimingControllerDialogue>(c);
|
2012-01-08 02:35:11 +01:00
|
|
|
}
|