Change AudioController's public API from samples to milliseconds

The sample rate of the currently open audio is not something that things
which do not interact with the raw audio data should have to care about,
or even know about.

Originally committed to SVN as r6426.
This commit is contained in:
Thomas Goyne 2012-02-01 23:58:58 +00:00
parent bba825ed0d
commit 01b92aa4e3
18 changed files with 413 additions and 514 deletions

View file

@ -245,5 +245,5 @@ void AudioBox::ScrollAudioBy(int pixel_amount) {
void AudioBox::ScrollToActiveLine() {
if (controller->GetTimingController())
audioDisplay->ScrollSampleRangeInView(controller->GetTimingController()->GetIdealVisibleSampleRange());
audioDisplay->ScrollTimeRangeInView(controller->GetTimingController()->GetIdealVisibleTimeRange());
}

View file

@ -34,7 +34,6 @@
/// @ingroup audio_ui
///
#include "config.h"
#ifndef AGI_PRE
@ -61,7 +60,7 @@
class VideoPositionMarker : public AudioMarker {
Pen style;
int64_t position;
int position;
public:
VideoPositionMarker()
@ -70,20 +69,16 @@ public:
{
}
void SetPosition(int64_t new_pos)
{
position = new_pos;
}
void SetPosition(int new_pos) { position = new_pos; }
int64_t GetPosition() const { return position; }
int GetPosition() const { return position; }
FeetStyle GetFeet() const { return Feet_None; }
bool CanSnap() const { return true; }
wxPen GetStyle() const { return style; }
operator int64_t() const { return position; }
operator int() const { return position; }
};
class VideoPositionMarkerProvider : public AudioMarkerProvider {
AudioController *ac;
VideoContext *vc;
VideoPositionMarker marker;
@ -99,7 +94,7 @@ class VideoPositionMarkerProvider : public AudioMarkerProvider {
}
else
{
marker.SetPosition(ac->SamplesFromMilliseconds(vc->TimeAtFrame(frame_number)));
marker.SetPosition(vc->TimeAtFrame(frame_number));
}
AnnounceMarkerMoved();
}
@ -120,15 +115,14 @@ class VideoPositionMarkerProvider : public AudioMarkerProvider {
public:
VideoPositionMarkerProvider(agi::Context *c)
: ac(c->audioController)
, vc(c->videoController)
: vc(c->videoController)
, video_seek_slot(vc->AddSeekListener(&VideoPositionMarkerProvider::Update, this))
, enable_opt_changed_slot(OPT_SUB("Audio/Display/Draw/Video Position", &VideoPositionMarkerProvider::OptChanged, this))
{
OptChanged(*OPT_GET("Audio/Display/Draw/Video Position"));
}
void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const
void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const
{
if (range.contains(marker))
{
@ -177,7 +171,7 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
}
else
{
AnnouncePlaybackPosition(pos);
AnnouncePlaybackPosition(MillisecondsFromSamples(pos));
}
}
@ -409,11 +403,11 @@ void AudioController::OnSubtitlesSave()
}
}
void AudioController::PlayRange(const SampleRange &range)
void AudioController::PlayRange(const TimeRange &range)
{
if (!IsAudioOpen()) return;
player->Play(range.begin(), range.length());
player->Play(SamplesFromMilliseconds(range.begin()), SamplesFromMilliseconds(range.length()));
playback_mode = PM_Range;
playback_timer.Start(20);
@ -428,26 +422,25 @@ void AudioController::PlayPrimaryRange()
playback_mode = PM_PrimaryRange;
}
void AudioController::PlayToEndOfPrimary(int64_t start_sample)
void AudioController::PlayToEndOfPrimary(int start_ms)
{
if (!IsAudioOpen()) return;
player->Play(start_sample, GetPrimaryPlaybackRange().end() - start_sample);
playback_mode = PM_PrimaryRange;
playback_timer.Start(20);
AnnouncePlaybackPosition(start_sample);
PlayRange(TimeRange(start_ms, GetPrimaryPlaybackRange().end()));
if (playback_mode == PM_Range)
playback_mode = PM_PrimaryRange;
}
void AudioController::PlayToEnd(int64_t start_sample)
void AudioController::PlayToEnd(int start_ms)
{
if (!IsAudioOpen()) return;
int64_t start_sample = SamplesFromMilliseconds(start_ms);
player->Play(start_sample, provider->GetNumSamples()-start_sample);
playback_mode = PM_ToEnd;
playback_timer.Start(20);
AnnouncePlaybackPosition(start_sample);
AnnouncePlaybackPosition(start_ms);
}
@ -469,23 +462,30 @@ bool AudioController::IsPlaying()
}
int64_t AudioController::GetPlaybackPosition()
int AudioController::GetPlaybackPosition()
{
if (!IsPlaying()) return 0;
return player->GetCurrentPosition();
return MillisecondsFromSamples(player->GetCurrentPosition());
}
int AudioController::GetDuration() const
{
if (!provider) return 0;
return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
}
void AudioController::ResyncPlaybackPosition(int64_t new_position)
void AudioController::ResyncPlaybackPosition(int new_position)
{
if (!IsPlaying()) return;
player->SetCurrentPosition(new_position);
player->SetCurrentPosition(SamplesFromMilliseconds(new_position));
}
SampleRange AudioController::GetPrimaryPlaybackRange() const
TimeRange AudioController::GetPrimaryPlaybackRange() const
{
if (timing_controller)
{
@ -493,19 +493,19 @@ SampleRange AudioController::GetPrimaryPlaybackRange() const
}
else
{
return SampleRange(0, 0);
return TimeRange(0, 0);
}
}
void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const
void AudioController::GetMarkers(const TimeRange &range, AudioMarkerVector &markers) const
{
/// @todo Find all sources of markers
if (timing_controller) timing_controller->GetMarkers(range, markers);
if (video_position_marker_provider.get()) video_position_marker_provider->GetMarkers(range, markers);
}
void AudioController::GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const
void AudioController::GetLabels(const TimeRange &range, std::vector<AudioLabel> &labels) const
{
if (timing_controller) timing_controller->GetLabels(range, labels);
}
@ -551,15 +551,17 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
return millisamples / sr;
}
void AudioController::SaveClip(wxString const& filename, SampleRange const& range) const
void AudioController::SaveClip(wxString const& filename, TimeRange const& range) const
{
if (filename.empty() || range.begin() > provider->GetNumSamples() || range.length() == 0) return;
int64_t start_sample = SamplesFromMilliseconds(range.begin());
int64_t end_sample = SamplesFromMilliseconds(range.end());
if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return;
agi::io::Save outfile(STD_STR(filename), true);
std::ofstream& out(outfile.Get());
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
size_t bufsize = range.length() * bytes_per_sample;
size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
int intval;
short shortval;
@ -580,8 +582,8 @@ void AudioController::SaveClip(wxString const& filename, SampleRange const& rang
//samples per read
size_t spr = 65536 / bytes_per_sample;
std::vector<char> buf(bufsize);
for(int64_t i = range.begin(); i < range.end(); i += spr) {
size_t len = std::min<size_t>(spr, range.end() - i);
for(int64_t i = start_sample; i < end_sample; i += spr) {
size_t len = std::min<size_t>(spr, end_sample - i);
provider->GetAudio(&buf[0], i, len);
out.write(&buf[0], len * bytes_per_sample);
}

View file

@ -65,47 +65,44 @@ class AudioMarkerProvider;
typedef std::vector<const AudioMarker*> AudioMarkerVector;
/// @class SampleRange
/// @brief Represents an immutable range of audio samples
class SampleRange {
int64_t _begin;
int64_t _end;
/// @class TimeRange
/// @brief Represents an immutable range of time
class TimeRange {
int _begin;
int _end;
public:
/// @brief Constructor
/// @param begin Index of the first sample to include in the range
/// @param end Index of one past the last sample to include in the range
SampleRange(int64_t begin, int64_t end)
: _begin(begin)
, _end(end)
/// @param begin Index of the first millisecond to include in the range
/// @param end Index of one past the last millisecond to include in the range
TimeRange(int begin, int end) : _begin(begin), _end(end)
{
assert(end >= begin);
}
/// @brief Copy constructor, optionally adjusting the range
/// @param src The range to duplicate
/// @param begin_adjust Number of samples to add to the start of the range
/// @param end_adjust Number of samples to add to the end of the range
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0)
/// @param begin_adjust Number of milliseconds to add to the start of the range
/// @param end_adjust Number of milliseconds to add to the end of the range
TimeRange(const TimeRange &src, int begin_adjust = 0, int end_adjust = 0)
{
_begin = src._begin + begin_adjust;
_end = src._end + end_adjust;
assert(_end >= _begin);
}
/// Get the number of samples in the range
int64_t length() const { return _end - _begin; }
/// Get the index of the first sample in the range
int64_t begin() const { return _begin; }
/// Get the index of one past the last sample in the range
int64_t end() const { return _end; }
/// Get the length of the range in milliseconds
int length() const { return _end - _begin; }
/// Get the start time of the range in milliseconds
int begin() const { return _begin; }
/// Get the exclusive end time of the range in milliseconds
int end() const { return _end; }
/// Determine whether the range contains a given sample index
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); }
/// Determine whether the range contains a given time in milliseconds
bool contains(int ms) const { return ms >= begin() && ms < end(); }
/// Determine whether there is an overlap between two ranges
bool overlaps(const SampleRange &other) const
bool overlaps(const TimeRange &other) const
{
return other.contains(_begin) || contains(other._begin);
}
@ -121,8 +118,8 @@ public:
/// Virtual destructor, does nothing
virtual ~AudioMarkerProvider() { }
/// @brief Return markers in a sample range
virtual void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const = 0;
/// @brief Return markers in a time range
virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0;
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
};
@ -134,22 +131,22 @@ protected:
/// One or more of the labels provided by this object have changed
agi::signal::Signal<> AnnounceLabelChanged;
public:
/// A label for a range of samples on the audio display
/// A label for a range of time on the audio display
struct AudioLabel {
/// Text of the label
wxString text;
/// Range which this label applies to
SampleRange range;
AudioLabel(wxString const& text, SampleRange const& range) : text(text), range(range) { }
TimeRange range;
AudioLabel(wxString const& text, TimeRange const& range) : text(text), range(range) { }
};
/// Virtual destructor, does nothing
virtual ~AudioLabelProvider() { }
/// @brief Get labels in a sample range
/// @param range Range of samples to get labels for
/// @brief Get labels in a time range
/// @param range Range of times to get labels for
/// @param[out] out Vector which should be filled with the labels
virtual void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const = 0;
virtual void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const = 0;
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
};
@ -190,7 +187,7 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
agi::signal::Signal<> AnnounceAudioClose;
/// Playback is in progress and the current position was updated
agi::signal::Signal<int64_t> AnnouncePlaybackPosition;
agi::signal::Signal<int> AnnouncePlaybackPosition;
/// Playback has stopped
agi::signal::Signal<> AnnouncePlaybackStop;
@ -258,8 +255,17 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
void OnComputerResuming(wxPowerEvent &event);
#endif
public:
/// @brief Convert a count of audio samples to a time in milliseconds
/// @param samples Sample count to convert
/// @return The number of milliseconds equivalent to the sample-count, rounded down
int64_t MillisecondsFromSamples(int64_t samples) const;
/// @brief Convert a time in milliseconds to a count of audio samples
/// @param ms Time in milliseconds to convert
/// @return The index of the first sample that is wholly inside the millisecond
int64_t SamplesFromMilliseconds(int64_t ms) const;
public:
/// @brief Constructor
AudioController(agi::Context *context);
@ -294,7 +300,7 @@ public:
///
/// The end of the played back range may be requested changed, but is not
/// changed automatically from any other operations.
void PlayRange(const SampleRange &range);
void PlayRange(const TimeRange &range);
/// @brief Start or restart audio playback, playing the primary playback range
///
@ -304,19 +310,19 @@ public:
void PlayPrimaryRange();
/// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range
/// @param start_sample Index of the sample to start playback at
/// @param start_ms Time in milliseconds to start playback at
///
/// This behaves like PlayPrimaryRange, but the start point can differ from
/// the beginning of the primary range.
void PlayToEndOfPrimary(int64_t start_sample);
void PlayToEndOfPrimary(int start_ms);
/// @brief Start or restart audio playback, playing from a point to the end of stream
/// @param start_sample Index of the sample to start playback at
/// @param start_ms Time in milliseconds to start playback at
///
/// Playback to end cannot be converted to a range playback like range
/// playback can, it will continue until the end is reached, it is stopped,
/// or restarted.
void PlayToEnd(int64_t start_sample);
void PlayToEnd(int start_ms);
/// @brief Stop all audio playback
void Stop();
@ -326,35 +332,39 @@ public:
bool IsPlaying();
/// @brief Get the current playback position
/// @return Approximate current sample index being heard by the user
/// @return Approximate current time in milliseconds being heard by the user
///
/// Returns 0 if playback is stopped. The return value is only approximate.
int64_t GetPlaybackPosition();
int GetPlaybackPosition();
/// Get the duration of the currently open audio in milliseconds, or 0 if none
/// @return Duration in milliseconds
int GetDuration() const;
/// @brief If playing, restart playback from the specified position
/// @param new_position Sample index to restart playback from
/// @param new_position Time to restart playback from
///
/// This function can be used to re-synchronise audio playback to another
/// source that might not be able to keep up with the full speed, such as
/// video playback in high resolution or with complex subtitles.
///
/// This function only does something if audio is already playing.
void ResyncPlaybackPosition(int64_t new_position);
void ResyncPlaybackPosition(int new_position);
/// @brief Get the primary playback range
/// @return An immutable SampleRange object
SampleRange GetPrimaryPlaybackRange() const;
/// @return An immutable TimeRange object
TimeRange GetPrimaryPlaybackRange() const;
/// @brief Get all markers inside a range
/// @param range The sample range to retrieve markers for
/// @param range The time range to retrieve markers for
/// @param markers Vector to fill found markers into
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
void GetMarkers(const TimeRange &range, AudioMarkerVector &markers) const;
/// @brief Get all labels inside a range
/// @param range The sample range to retrieve labels for
/// @param range The time range to retrieve labels for
/// @param labels Vector to fill found labels into
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const;
void GetLabels(const TimeRange &range, std::vector<AudioLabel> &labels) const;
/// @brief Get the playback audio volume
@ -376,20 +386,10 @@ public:
/// dialogue line.
void SetTimingController(AudioTimingController *new_controller);
/// @brief Convert a count of audio samples to a time in milliseconds
/// @param samples Sample count to convert
/// @return The number of milliseconds equivalent to the sample-count, rounded down
int64_t MillisecondsFromSamples(int64_t samples) const;
/// @brief Convert a time in milliseconds to a count of audio samples
/// @param ms Time in milliseconds to convert
/// @return The index of the first sample that is wholly inside the millisecond
int64_t SamplesFromMilliseconds(int64_t ms) const;
/// @brief Save a portion of the decoded loaded audio to a wav file
/// @param filename File to save to
/// @param range Range of samples to save
void SaveClip(wxString const& filename, SampleRange const& range) const;
/// @param range Time range to save
void SaveClip(wxString const& filename, TimeRange const& range) const;
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
@ -414,8 +414,8 @@ public:
};
/// @brief Get the marker's position
/// @return The marker's position in samples
virtual int64_t GetPosition() const = 0;
/// @return The marker's position in milliseconds
virtual int GetPosition() const = 0;
/// @brief Get the marker's drawing style
/// @return A pen object describing the marker's drawing style

View file

@ -55,7 +55,6 @@
#include "audio_timing.h"
#include "block_cache.h"
#include "compat.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
#include "main.h"
@ -250,10 +249,9 @@ const int AudioDisplayScrollbar::min_width;
class AudioDisplayTimeline : public AudioDisplayInteractionObject {
int64_t num_samples;
int samplerate;
int samples_per_pixel;
int pixel_left;
int duration; ///< Total duration in ms
double ms_per_pixel; ///< Milliseconds per pixel
int pixel_left; ///< Leftmost visible pixel (i.e. scroll position)
wxRect bounds;
@ -276,27 +274,21 @@ class AudioDisplayTimeline : public AudioDisplayInteractionObject {
int scale_major_modulo; ///< If minor_scale_mark_index % scale_major_modulo == 0 the mark is a major mark
double scale_minor_divisor; ///< Absolute scale-mark index multiplied by this number gives sample index for scale mark
AudioDisplay *display;
AudioDisplay *display; ///< Containing audio display
UIColours colours; ///< Colour provider
public:
AudioDisplayTimeline(AudioDisplay *display)
: num_samples(0)
, samplerate(44100)
, samples_per_pixel(1)
: duration(0)
, ms_per_pixel(1.0)
, pixel_left(0)
, dragging(false)
, display(display)
{
}
int GetHeight() const
{
int width, height;
display->GetTextExtent("0123456789:.", &width, &height);
return height + 4;
bounds.height = height + 4;
}
void SetColourScheme(std::string const& name)
@ -308,57 +300,55 @@ public:
{
// The size is without anything that goes below the timeline (like scrollbar)
bounds.width = display_size.x;
bounds.height = GetHeight();
bounds.x = 0;
bounds.y = 0;
}
int GetHeight() const { return bounds.height; }
const wxRect & GetBounds() const { return bounds; }
void ChangeAudio(int64_t new_length, int new_samplerate)
void ChangeAudio(int new_duration)
{
num_samples = new_length;
samplerate = new_samplerate;
duration = new_duration;
}
void ChangeZoom(int new_pixel_samples)
void ChangeZoom(double new_ms_per_pixel)
{
samples_per_pixel = new_pixel_samples;
ms_per_pixel = new_ms_per_pixel;
// Pixels per second
double px_sec = (double)samplerate / (double)samples_per_pixel;
double px_sec = 1000.0 / ms_per_pixel;
if (px_sec > 3000) {
scale_minor = Sc_Millisecond;
scale_minor_divisor = (double)samplerate / 1000;
scale_minor_divisor = 1.0;
scale_major_modulo = 10;
} else if (px_sec > 300) {
scale_minor = Sc_Centisecond;
scale_minor_divisor = (double)samplerate / 100;
scale_minor_divisor = 10.0;
scale_major_modulo = 10;
} else if (px_sec > 30) {
scale_minor = Sc_Decisecond;
scale_minor_divisor = (double)samplerate / 10;
scale_minor_divisor = 100.0;
scale_major_modulo = 10;
} else if (px_sec > 3) {
scale_minor = Sc_Second;
scale_minor_divisor = (double)samplerate;
scale_minor_divisor = 1000.0;
scale_major_modulo = 10;
} else if (px_sec > 1.0/3.0) {
scale_minor = Sc_Decasecond;
scale_minor_divisor = (double)samplerate * 10;
scale_minor_divisor = 10000.0;
scale_major_modulo = 6;
} else if (px_sec > 1.0/9.0) {
scale_minor = Sc_Minute;
scale_minor_divisor = (double)samplerate * 60;
scale_minor_divisor = 60000.0;
scale_major_modulo = 10;
} else if (px_sec > 1.0/90.0) {
scale_minor = Sc_Decaminute;
scale_minor_divisor = (double)samplerate * 600;
scale_minor_divisor = 600000.0;
scale_major_modulo = 6;
} else {
scale_minor = Sc_Hour;
scale_minor_divisor = (double)samplerate * 3600;
scale_minor_divisor = 3600000.0;
scale_major_modulo = 10;
}
}
@ -408,19 +398,19 @@ public:
dc.SetTextForeground(colours.Light());
// Figure out the first scale mark to show
int64_t sample_left = pixel_left * samples_per_pixel;
int next_scale_mark = (int)(sample_left / scale_minor_divisor);
if (next_scale_mark * scale_minor_divisor < sample_left)
int ms_left = int(pixel_left * ms_per_pixel);
int next_scale_mark = int(ms_left / scale_minor_divisor);
if (next_scale_mark * scale_minor_divisor < ms_left)
next_scale_mark += 1;
assert(next_scale_mark * scale_minor_divisor >= sample_left);
assert(next_scale_mark * scale_minor_divisor >= ms_left);
// Draw scale marks
int next_scale_mark_pos;
int last_text_right = -1;
int last_hour = -1, last_minute = -1;
if (num_samples / samplerate < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
if (duration < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
do {
next_scale_mark_pos = (int)(next_scale_mark * scale_minor_divisor / samples_per_pixel) - pixel_left;
next_scale_mark_pos = int(next_scale_mark * scale_minor_divisor / ms_per_pixel) - pixel_left;
bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
if (mark_is_major)
@ -431,7 +421,7 @@ public:
// Print time labels on major scale marks
if (mark_is_major && next_scale_mark_pos > last_text_right)
{
double mark_time = next_scale_mark * scale_minor_divisor / samplerate;
double mark_time = next_scale_mark * scale_minor_divisor / 1000.0;
int mark_hour = (int)(mark_time / 3600);
int mark_minute = (int)(mark_time / 60) % 60;
double mark_second = mark_time - mark_hour*3600 - mark_minute*60;
@ -504,8 +494,8 @@ public:
{
timing_controller->OnMarkerDrag(
marker,
display->SamplesFromRelativeX(event.GetPosition().x),
default_snap != event.ShiftDown() ? display->SamplesFromAbsoluteX(snap_range) : 0);
display->TimeFromRelativeX(event.GetPosition().x),
default_snap != event.ShiftDown() ? display->TimeFromAbsoluteX(snap_range) : 0);
}
// We lose the marker drag if the button used to initiate it goes up
@ -514,14 +504,14 @@ public:
};
class AudioStyleRangeMerger : public AudioRenderingStyleRanges {
typedef std::map<int64_t, AudioRenderingStyle> style_map;
typedef std::map<int, AudioRenderingStyle> style_map;
public:
typedef style_map::iterator iterator;
private:
style_map points;
void Split(int64_t point)
void Split(int point)
{
iterator it = points.lower_bound(point);
if (it == points.end() || it->first != point)
@ -531,7 +521,7 @@ private:
}
}
void Restyle(int64_t start, int64_t end, AudioRenderingStyle style)
void Restyle(int start, int end, AudioRenderingStyle style)
{
assert(points.lower_bound(end) != points.end());
for (iterator pt = points.lower_bound(start); pt->first < end; ++pt)
@ -547,7 +537,7 @@ public:
points[0] = AudioStyle_Normal;
}
void AddRange(int64_t start, int64_t end, AudioRenderingStyle style)
void AddRange(int start, int end, AudioRenderingStyle style)
{
if (start < 0) start = 0;
@ -567,7 +557,6 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::C
, audio_open_connection(controller->AddAudioOpenListener(&AudioDisplay::OnAudioOpen, this))
, context(context)
, audio_renderer(new AudioRenderer)
, provider(0)
, controller(controller)
, scrollbar(new AudioDisplayScrollbar(this))
, timeline(new AudioDisplayTimeline(this))
@ -627,29 +616,11 @@ void AudioDisplay::ScrollPixelToLeft(int pixel_position)
}
void AudioDisplay::ScrollPixelToCenter(int pixel_position)
{
ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2);
}
void AudioDisplay::ScrollSampleToLeft(int64_t sample_position)
{
ScrollPixelToLeft(AbsoluteXFromSamples(sample_position));
}
void AudioDisplay::ScrollSampleToCenter(int64_t sample_position)
{
ScrollPixelToCenter(AbsoluteXFromSamples(sample_position));
}
void AudioDisplay::ScrollSampleRangeInView(const SampleRange &range)
void AudioDisplay::ScrollTimeRangeInView(const TimeRange &range)
{
int client_width = GetClientRect().GetWidth();
int range_begin = AbsoluteXFromSamples(range.begin());
int range_end = AbsoluteXFromSamples(range.end());
int range_begin = AbsoluteXFromTime(range.begin());
int range_end = AbsoluteXFromTime(range.end());
int range_len = range_end - range_begin;
// Is everything already in view?
@ -690,35 +661,24 @@ void AudioDisplay::SetZoomLevel(int new_zoom_level)
{
zoom_level = new_zoom_level;
if (!provider)
{
pixel_samples = 1;
return;
}
const int samples_per_second = provider ? provider->GetSampleRate() : 48000;
const int base_pixels_per_second = 50; /// @todo Make this customisable
const int base_samples_per_pixel = samples_per_second / base_pixels_per_second;
const int factor = GetZoomLevelFactor(zoom_level);
const int base_pixels_per_second = 50; /// @todo Make this customisable
const double base_ms_per_pixel = 1000.0 / base_pixels_per_second;
const double new_ms_per_pixel = 100.0 * base_ms_per_pixel / factor;
const int new_samples_per_pixel = std::max(1, 100 * base_samples_per_pixel / factor);
if (pixel_samples != new_samples_per_pixel)
if (ms_per_pixel != new_ms_per_pixel)
{
int client_width = GetClientSize().GetWidth();
int64_t center_sample = int64_t(scroll_left + client_width / 2) * pixel_samples;
double center_time = (scroll_left + client_width / 2) * ms_per_pixel;
pixel_samples = new_samples_per_pixel;
audio_renderer->SetSamplesPerPixel(pixel_samples);
pixel_audio_width = provider->GetNumSamples() / pixel_samples + 1;
ms_per_pixel = new_ms_per_pixel;
pixel_audio_width = std::max(1, int(controller->GetDuration() / ms_per_pixel));
audio_renderer->SetMillisecondsPerPixel(ms_per_pixel);
scrollbar->ChangeLengths(pixel_audio_width, client_width);
timeline->ChangeZoom(pixel_samples);
ScrollSampleToCenter(center_sample);
timeline->ChangeZoom(ms_per_pixel);
ScrollTimeToCenter(center_time);
Refresh();
}
}
@ -770,13 +730,6 @@ void AudioDisplay::SetAmplitudeScale(float scale)
Refresh();
}
float AudioDisplay::GetAmplitudeScale() const
{
return audio_renderer->GetAmplitudeScale();
}
void AudioDisplay::ReloadRenderingSettings()
{
std::string colour_scheme_name;
@ -831,13 +784,6 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
{
wxAutoBufferedPaintDC dc(this);
if (!provider)
{
dc.SetBackground(*wxBLACK_BRUSH);
dc.Clear();
return;
}
wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height);
bool redraw_scrollbar = false;
bool redraw_timeline = false;
@ -857,13 +803,13 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
if (audio_bounds.Intersects(updrect))
{
SampleRange updsamples(
SamplesFromRelativeX(updrect.x - foot_size),
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
TimeRange updtime(
std::max(0, TimeFromRelativeX(updrect.x - foot_size)),
std::max(0, TimeFromRelativeX(updrect.x + updrect.width + foot_size)));
PaintAudio(dc, updsamples, updrect);
PaintMarkers(dc, updsamples);
PaintLabels(dc, updsamples);
PaintAudio(dc, updtime, updrect);
PaintMarkers(dc, updtime);
PaintLabels(dc, updtime);
}
}
@ -878,10 +824,10 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
timeline->Paint(dc);
}
void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
void AudioDisplay::PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect)
{
std::map<int64_t, int>::iterator pt = style_ranges.upper_bound(updsamples.begin());
std::map<int64_t, int>::iterator pe = style_ranges.upper_bound(updsamples.end());
std::map<int, int>::iterator pt = style_ranges.upper_bound(updtime.begin());
std::map<int, int>::iterator pe = style_ranges.upper_bound(updtime.end());
if (pt != style_ranges.begin())
--pt;
@ -889,8 +835,8 @@ void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
while (pt != pe)
{
AudioRenderingStyle range_style = static_cast<AudioRenderingStyle>(pt->second);
int range_x1 = std::max(updrect.x, RelativeXFromSamples(pt->first));
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromSamples(pt->first);
int range_x1 = std::max(updrect.x, RelativeXFromTime(pt->first));
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromTime(pt->first);
if (range_x2 > range_x1)
{
@ -899,10 +845,10 @@ void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
}
}
void AudioDisplay::PaintMarkers(wxDC &dc, SampleRange updsamples)
void AudioDisplay::PaintMarkers(wxDC &dc, TimeRange updtime)
{
AudioMarkerVector markers;
controller->GetMarkers(updsamples, markers);
controller->GetMarkers(updtime, markers);
if (markers.empty()) return;
wxDCPenChanger pen_retainer(dc, wxPen());
@ -910,7 +856,7 @@ void AudioDisplay::PaintMarkers(wxDC &dc, SampleRange updsamples)
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
{
const AudioMarker *marker = *marker_i;
int marker_x = RelativeXFromSamples(marker->GetPosition());
int marker_x = RelativeXFromTime(marker->GetPosition());
dc.SetPen(marker->GetStyle());
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
@ -935,10 +881,10 @@ void AudioDisplay::PaintFoot(wxDC &dc, int marker_x, int dir)
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
}
void AudioDisplay::PaintLabels(wxDC &dc, SampleRange updsamples)
void AudioDisplay::PaintLabels(wxDC &dc, TimeRange updtime)
{
std::vector<AudioLabelProvider::AudioLabel> labels;
controller->GetLabels(updsamples, labels);
controller->GetLabels(updtime, labels);
if (labels.empty()) return;
wxDCFontChanger fc(dc);
@ -949,8 +895,8 @@ void AudioDisplay::PaintLabels(wxDC &dc, SampleRange updsamples)
for (size_t i = 0; i < labels.size(); ++i)
{
wxSize extent = dc.GetTextExtent(labels[i].text);
int left = RelativeXFromSamples(labels[i].range.begin());
int width = AbsoluteXFromSamples(labels[i].range.length());
int left = RelativeXFromTime(labels[i].range.begin());
int width = AbsoluteXFromTime(labels[i].range.length());
// If it doesn't fit, truncate
if (width < extent.GetWidth())
@ -1034,7 +980,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
if (show_time)
{
AssTime new_label_time = controller->MillisecondsFromSamples(SamplesFromAbsoluteX(track_cursor_pos));
AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos);
track_cursor_label = new_label_time.GetASSFormated();
track_cursor_label_rect.x += new_pos - old_pos;
RefreshRect(track_cursor_label_rect, false);
@ -1118,9 +1064,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
if (event.MiddleIsDown())
{
context->videoController->JumpToTime(
controller->MillisecondsFromSamples(SamplesFromRelativeX(mousepos.x)),
agi::vfr::EXACT);
context->videoController->JumpToTime(TimeFromRelativeX(mousepos.x), agi::vfr::EXACT);
return;
}
@ -1131,15 +1075,15 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
AudioTimingController *timing = controller->GetTimingController();
if (!timing) return;
int drag_sensitivity = pixel_samples * OPT_GET("Audio/Start Drag Sensitivity")->GetInt();
int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? pixel_samples * OPT_GET("Audio/Snap/Distance")->GetInt() : 0;
int drag_sensitivity = int(OPT_GET("Audio/Start Drag Sensitivity")->GetInt() * ms_per_pixel);
int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? int(OPT_GET("Audio/Snap/Distance")->GetInt() * ms_per_pixel) : 0;
// Not scrollbar, not timeline, no button action
if (event.Moving())
{
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
int timepos = TimeFromRelativeX(mousepos.x);
if (timing->IsNearbyMarker(samplepos, drag_sensitivity))
if (timing->IsNearbyMarker(timepos, drag_sensitivity))
SetCursor(wxCursor(wxCURSOR_SIZEWE));
else
SetCursor(wxNullCursor);
@ -1147,10 +1091,10 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
if (event.LeftDown() || event.RightDown())
{
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
int timepos = TimeFromRelativeX(mousepos.x);
AudioMarker *marker = event.LeftDown() ?
timing->OnLeftClick(samplepos, drag_sensitivity, snap_sensitivity) :
timing->OnRightClick(samplepos, drag_sensitivity, snap_sensitivity);
timing->OnLeftClick(timepos, drag_sensitivity, snap_sensitivity) :
timing->OnRightClick(timepos, drag_sensitivity, snap_sensitivity);
if (marker)
{
@ -1176,8 +1120,11 @@ void AudioDisplay::OnSize(wxSizeEvent &)
timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
scrollbar->SetDisplaySize(size);
SampleRange sel(controller->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
if (controller->GetTimingController())
{
TimeRange sel(controller->GetTimingController()->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
}
audio_height = size.GetHeight();
audio_height -= scrollbar->GetBounds().GetHeight();
@ -1199,17 +1146,15 @@ void AudioDisplay::OnFocus(wxFocusEvent &)
void AudioDisplay::OnAudioOpen(AudioProvider *provider)
{
this->provider = provider;
if (!audio_renderer_provider)
ReloadRenderingSettings();
audio_renderer->SetAudioProvider(provider);
audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
if (provider)
timeline->ChangeAudio(provider->GetNumSamples(), provider->GetSampleRate());
timeline->ChangeAudio(controller->GetDuration());
ms_per_pixel = 0;
SetZoomLevel(zoom_level);
Refresh();
@ -1238,9 +1183,9 @@ void AudioDisplay::OnAudioOpen(AudioProvider *provider)
}
}
void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
void AudioDisplay::OnPlaybackPosition(int ms)
{
int pixel_position = AbsoluteXFromSamples(sample_position);
int pixel_position = AbsoluteXFromTime(ms);
SetTrackCursor(pixel_position, false);
if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool())
@ -1260,12 +1205,12 @@ void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
void AudioDisplay::OnSelectionChanged()
{
SampleRange sel(controller->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
TimeRange sel(controller->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
if (OPT_GET("Audio/Auto/Scroll")->GetBool())
{
ScrollSampleRangeInView(sel);
ScrollTimeRangeInView(sel);
}
RefreshRect(scrollbar->GetBounds(), false);
@ -1278,16 +1223,16 @@ void AudioDisplay::OnStyleRangesChanged()
AudioStyleRangeMerger asrm;
controller->GetTimingController()->GetRenderingStyles(asrm);
std::map<int64_t, int> old_style_ranges;
std::map<int, int> old_style_ranges;
swap(old_style_ranges, style_ranges);
style_ranges.insert(asrm.begin(), asrm.end());
std::map<int64_t, int>::iterator old_style_it = old_style_ranges.begin();
std::map<int64_t, int>::iterator new_style_it = style_ranges.begin();
std::map<int, int>::iterator old_style_it = old_style_ranges.begin();
std::map<int, int>::iterator new_style_it = style_ranges.begin();
int old_style = old_style_it->second;
int new_style = new_style_it->second;
int64_t range_start = 0;
int range_start = 0;
// Repaint each range which has changed
while (old_style_it != old_style_ranges.end() || new_style_it != style_ranges.end())
@ -1313,20 +1258,20 @@ void AudioDisplay::OnStyleRangesChanged()
// Fill in the last style range
if (old_style != new_style)
{
Redraw(range_start, SamplesFromRelativeX(GetClientSize().GetWidth()));
Redraw(range_start, TimeFromRelativeX(GetClientSize().GetWidth()));
}
}
void AudioDisplay::Redraw(int64_t sample_start, int64_t sample_end)
void AudioDisplay::Redraw(int time_start, int time_end)
{
if (sample_start == sample_end) return;
if (time_start == time_end) return;
sample_start = RelativeXFromSamples(sample_start) - foot_size;
sample_end = RelativeXFromSamples(sample_end) + foot_size;
time_start = RelativeXFromTime(time_start) - foot_size;
time_end = RelativeXFromTime(time_end) + foot_size;
if (sample_end >= 0 && sample_start <= GetClientSize().GetWidth())
if (time_end >= 0 && time_start <= GetClientSize().GetWidth())
{
RefreshRect(wxRect(sample_start, audio_top, sample_end - sample_start, audio_height), false);
RefreshRect(wxRect(time_start, audio_top, time_end - time_start, audio_height), false);
}
}

View file

@ -110,9 +110,6 @@ class AudioDisplay: public wxWindow {
/// The current audio renderer
agi::scoped_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
/// Our current audio provider
AudioProvider *provider;
/// The controller managing us
AudioController *controller;
@ -139,8 +136,8 @@ class AudioDisplay: public wxWindow {
/// Total width of the audio in pixels
int pixel_audio_width;
/// Horizontal zoom measured in audio samples per pixel
int pixel_samples;
/// Horizontal zoom measured in millisecond per pixels
double ms_per_pixel;
/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
float scale_amplitude;
@ -171,7 +168,7 @@ class AudioDisplay: public wxWindow {
void RemoveTrackCursor();
/// Previous style ranges for optimizing redraw when ranges change
std::map<int64_t, int> style_ranges;
std::map<int, int> style_ranges;
/// @brief Reload all rendering settings from Options and reset caches
///
@ -179,21 +176,21 @@ class AudioDisplay: public wxWindow {
/// in Options and need to be reloaded to take effect.
void ReloadRenderingSettings();
/// @brief Repaint a range of samples
/// @param sample_start First sample to repaint
/// @param sample_end Last sample to repaint
void Redraw(int64_t sample_start, int64_t sample_end);
/// @brief Repaint a time range
/// @param ms_start Beginning of range to repaint
/// @param ms_end End of range to repaint
void Redraw(int ms_start, int ms_end);
/// Paint the audio data for a range of samples
/// Paint the audio data for a time range
/// @param dc DC to paint to
/// @param updsamples Sample range to repaint
/// @param updtime Time range to repaint
/// @param updrect Pixel range to repaint
void PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect);
void PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect);
/// Paint the markers in a range of samples
/// Paint the markers in a time range
/// @param dc DC to paint to
/// @param updsamples Sample range to repaint
void PaintMarkers(wxDC &dc, SampleRange updsamples);
/// @param updtime Time range to repaint
void PaintMarkers(wxDC &dc, TimeRange updtime);
/// Draw a single foot for a marker
/// @param dc DC to paint to
@ -201,10 +198,10 @@ class AudioDisplay: public wxWindow {
/// @param dir -1 for left, 1 for right
void PaintFoot(wxDC &dc, int marker_x, int dir);
/// Paint the labels in a range of samples
/// Paint the labels in a time range
/// @param dc DC to paint to
/// @param updsamples Sample range to repaint
void PaintLabels(wxDC &dc, SampleRange updsamples);
/// @param updtime Time range to repaint
void PaintLabels(wxDC &dc, TimeRange updtime);
/// Paint the track cursor
/// @param dc DC to paint to
@ -223,7 +220,7 @@ class AudioDisplay: public wxWindow {
// AudioControllerAudioEventListener implementation
void OnAudioOpen(AudioProvider *provider);
void OnPlaybackPosition(int64_t sample_position);
void OnPlaybackPosition(int ms_position);
void OnSelectionChanged();
void OnStyleRangesChanged();
void OnMarkerMoved();
@ -248,46 +245,49 @@ public:
/// @brief Scroll the audio display
/// @param pixel_position Absolute pixel to put in center of the audio display
void ScrollPixelToCenter(int pixel_position);
void ScrollPixelToCenter(int pixel_position) { ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2); }
/// @brief Scroll the audio display
/// @param sample_position Audio sample to put at left edge of the audio display
void ScrollSampleToLeft(int64_t sample_position);
/// @param ms Time in milliseconds to put at left edge of the audio display
void ScrollTimeToLeft(int ms) { ScrollPixelToLeft(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display
/// @param sample_position Audio sample to put in center of the audio display
void ScrollSampleToCenter(int64_t sample_position);
/// @param ms Time in milliseconds to put in center of the audio display
void ScrollTimeToCenter(int ms) { ScrollPixelToCenter(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display
/// @param range Range of audio samples to ensure is in view
/// @param range Time range to ensure is in view
///
/// If the entire range is already visible inside the display, nothing is scrolled. If
/// just one of the two endpoints is visible, the display is scrolled such that the
/// visible endpoint stays in view but more of the rest of the range becomes visible.
/// If the entire range is already visible inside the display, nothing is
/// scrolled. If just one of the two endpoints is visible, the display is
/// scrolled such that the visible endpoint stays in view but more of the
/// rest of the range becomes visible.
///
/// If the entire range fits inside the display, the display is centered over the range.
/// For this calculation, the display is considered smaller by some margins, see below.
/// If the entire range fits inside the display, the display is centered
/// over the range. For this calculation, the display is considered
/// smaller by some margins, see below.
///
/// If the range does not fit within the display with margins subtracted, the start of
/// the range is ensured visible and as much of the rest of the range is brought into
/// view.
/// If the range does not fit within the display with margins subtracted,
/// the start of the range is ensured visible and as much of the rest of
/// the range is brought into view.
///
/// For the purpose of this function, a 5 percent margin is assumed at each end of the
/// audio display such that a range endpoint that is ensured to be in view never gets
/// closer to the edge of the display than the margin. The edge that is not ensured to
/// be in view might be outside of view or might be closer to the display edge than the
/// For the purpose of this function, a 5 percent margin is assumed at each
/// end of the audio display such that a range endpoint that is ensured to
/// be in view never gets closer to the edge of the display than the
/// margin. The edge that is not ensured to be in view might be outside of
/// view or might be closer to the display edge than the
/// margin.
void ScrollSampleRangeInView(const SampleRange &range);
void ScrollTimeRangeInView(const TimeRange &range);
/// @brief Change the zoom level
/// @param new_zoom_level The new zoom level to use
///
/// A zoom level of 0 is the default zoom level, all other levels are based on this.
/// Negative zoom levels zoom out, positive zoom in.
/// A zoom level of 0 is the default zoom level, all other levels are based
/// on this. Negative zoom levels zoom out, positive zoom in.
///
/// The zoom levels generally go from +30 to -30. It is possible to zoom in more than
/// +30
/// The zoom levels generally go from +30 to -30. It is possible to zoom in
/// more than +30.
void SetZoomLevel(int new_zoom_level);
/// @brief Get the zoom level
@ -320,21 +320,15 @@ public:
/// @param scale New amplitude scale factor, 1.0 is no scaling
void SetAmplitudeScale(float scale);
/// @brief Get amplitude scale factor
/// @return The amplitude scaling factor
float GetAmplitudeScale() const;
/// @brief Get a sample index from an X coordinate relative to current scroll
int64_t SamplesFromRelativeX(int x) const { return (scroll_left + x) * pixel_samples; }
/// @brief Get a sample index from an absolute X coordinate
int64_t SamplesFromAbsoluteX(int x) const { return x * pixel_samples; }
/// @brief Get an X coordinate relative to the current scroll from a sample index
int RelativeXFromSamples(int64_t samples) const { return samples/pixel_samples - scroll_left; }
/// @brief Get an absolute X coordinate from a sample index
int AbsoluteXFromSamples(int64_t samples) const { return samples/pixel_samples; }
/// Get a time in milliseconds from an X coordinate relative to current scroll
int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
/// Get a time in milliseconds from an absolute X coordinate
int TimeFromAbsoluteX(int x) const { return int(x * ms_per_pixel); }
/// Get an X coordinate relative to the current scroll from a time in milliseconds
int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
/// Get an absolute X coordinate from a time in milliseconds
int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
DECLARE_EVENT_TABLE()
};

View file

@ -36,21 +36,20 @@
class AudioMarkerKeyframe : public AudioMarker {
Pen *style;
int64_t position;
int position;
public:
AudioMarkerKeyframe(Pen *style, int64_t position) : style(style), position(position) { }
int64_t GetPosition() const { return position; }
AudioMarkerKeyframe(Pen *style, int position) : style(style), position(position) { }
int GetPosition() const { return position; }
FeetStyle GetFeet() const { return Feet_None; }
bool CanSnap() const { return true; }
wxPen GetStyle() const { return *style; }
operator int64_t() const { return position; }
operator int() const { return position; }
};
AudioMarkerProviderKeyframes::AudioMarkerProviderKeyframes(agi::Context *c, const char *opt_name)
: controller(c->audioController)
, vc(c->videoController)
: vc(c->videoController)
, keyframe_slot(vc->AddKeyframesListener(&AudioMarkerProviderKeyframes::Update, this))
, audio_open_slot(controller->AddAudioOpenListener(&AudioMarkerProviderKeyframes::Update, this))
, audio_open_slot(c->audioController->AddAudioOpenListener(&AudioMarkerProviderKeyframes::Update, this))
, timecode_slot(vc->AddTimecodesListener(&AudioMarkerProviderKeyframes::Update, this))
, enabled_slot(OPT_SUB(opt_name, &AudioMarkerProviderKeyframes::Update, this))
, enabled_opt(OPT_GET(opt_name))
@ -76,13 +75,12 @@ void AudioMarkerProviderKeyframes::Update() {
markers.clear();
markers.reserve(keyframes.size());
for (size_t i = 0; i < keyframes.size(); ++i) {
markers.push_back(AudioMarkerKeyframe(style.get(),
controller->SamplesFromMilliseconds(timecodes.TimeAtFrame(keyframes[i]))));
markers.push_back(AudioMarkerKeyframe(style.get(), timecodes.TimeAtFrame(keyframes[i])));
}
AnnounceMarkerMoved();
}
void AudioMarkerProviderKeyframes::GetMarkers(SampleRange const& range, AudioMarkerVector &out) const {
void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
// Find first and last keyframes inside the range
std::vector<AudioMarkerKeyframe>::const_iterator
a = lower_bound(markers.begin(), markers.end(), range.begin()),

View file

@ -40,8 +40,6 @@ namespace agi {
/// Marker provider for video keyframes
class AudioMarkerProviderKeyframes : public AudioMarkerProvider {
/// Audio controller for time -> sample conversions
AudioController *controller;
/// Video controller to get keyframes from
VideoContext *vc;
@ -71,5 +69,5 @@ public:
/// Get all keyframe markers within a range
/// @param range Range of samples to get markers for
/// @param[out] out Vector to fill with markers in the range
void GetMarkers(SampleRange const& range, AudioMarkerVector &out) const;
void GetMarkers(TimeRange const& range, AudioMarkerVector &out) const;
};

View file

@ -79,7 +79,7 @@ size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const
AudioRenderer::AudioRenderer()
: pixel_samples(0)
: pixel_ms(0)
, pixel_height(0)
, amplitude_scale(0)
, cache_bitmap_width(32) // arbitrary value for now
@ -91,7 +91,7 @@ AudioRenderer::AudioRenderer()
bitmaps.resize(AudioStyle_MAX, AudioRendererBitmapCache(256, AudioRendererBitmapCacheBitmapFactory(this)));
// Make sure there's *some* values for those fields, and in the caches
SetSamplesPerPixel(1);
SetMillisecondsPerPixel(1);
SetHeight(1);
}
@ -99,14 +99,14 @@ AudioRenderer::~AudioRenderer()
{
}
void AudioRenderer::SetSamplesPerPixel(int _pixel_samples)
void AudioRenderer::SetMillisecondsPerPixel(double new_pixel_ms)
{
if (pixel_samples == _pixel_samples) return;
if (pixel_ms == new_pixel_ms) return;
pixel_samples = _pixel_samples;
pixel_ms = new_pixel_ms;
if (renderer)
renderer->SetSamplesPerPixel(pixel_samples);
renderer->SetMillisecondsPerPixel(pixel_ms);
ResetBlockCount();
}
@ -147,7 +147,7 @@ void AudioRenderer::SetRenderer(AudioRendererBitmapProvider *_renderer)
{
renderer->SetProvider(provider);
renderer->SetAmplitudeScale(amplitude_scale);
renderer->SetSamplesPerPixel(pixel_samples);
renderer->SetMillisecondsPerPixel(pixel_ms);
}
}
@ -181,7 +181,8 @@ void AudioRenderer::ResetBlockCount()
{
if (provider)
{
size_t rendered_width = (size_t)((provider->GetNumSamples() + pixel_samples - 1) / pixel_samples);
double duration = provider->GetNumSamples() * 1000.0 / provider->GetSampleRate();
size_t rendered_width = (size_t)ceil(duration / pixel_ms);
cache_numblocks = rendered_width / cache_bitmap_width;
for_each(bitmaps, bind(&AudioRendererBitmapCache::SetBlockCount, _1, cache_numblocks));
}
@ -263,13 +264,13 @@ void AudioRendererBitmapProvider::SetProvider(AudioProvider *_provider)
}
void AudioRendererBitmapProvider::SetSamplesPerPixel(int _pixel_samples)
void AudioRendererBitmapProvider::SetMillisecondsPerPixel(double new_pixel_ms)
{
if (pixel_samples == _pixel_samples) return;
if (pixel_ms == new_pixel_ms) return;
pixel_samples = _pixel_samples;
pixel_ms = new_pixel_ms;
OnSetSamplesPerPixel();
OnSetMillisecondsPerPixel();
}

View file

@ -97,8 +97,8 @@ typedef DataBlockCache<wxBitmap, 8, AudioRendererBitmapCacheBitmapFactory> Audio
class AudioRenderer {
friend struct AudioRendererBitmapCacheBitmapFactory;
/// Horizontal zoom level, samples per pixel
int pixel_samples;
/// Horizontal zoom level, milliseconds per pixel
double pixel_ms;
/// Rendering height in pixels
int pixel_height;
/// Vertical zoom level/amplitude scale
@ -152,10 +152,10 @@ public:
~AudioRenderer();
/// @brief Set horizontal zoom
/// @param pixel_samples Audio samples per pixel to render at
/// @param pixel_ms Milliseconds per pixel to render audio at
///
/// Changing the zoom level invalidates all cached bitmaps.
void SetSamplesPerPixel(int pixel_samples);
void SetMillisecondsPerPixel(double pixel_ms);
/// @brief Set rendering height
/// @param pixel_height Height in pixels to render at
@ -184,18 +184,6 @@ public:
/// Changing the max cache size does not trigger cache aging.
void SetCacheMaxSize(size_t max_size);
/// @brief Get horizontal zoom
/// @return Audio samples per pixel rendering at
int GetSamplesPerPixel() const { return pixel_samples; }
/// @brief Get rendering height
/// @return Height in pixels rendering at
int GetHeight() const { return pixel_height; }
/// @brief Get vertical zoom
/// @return The amplitude scaling factor
float GetAmplitudeScale() const { return amplitude_scale; }
/// @brief Change renderer
/// @param renderer New renderer to use
///
@ -257,8 +245,8 @@ class AudioRendererBitmapProvider {
protected:
/// Audio provider to use for rendering
AudioProvider *provider;
/// Horizontal zoom in samples per pixel
int pixel_samples;
/// Horizontal zoom in milliseconds per pixel
double pixel_ms;
/// Vertical zoom/amplitude scale factor
float amplitude_scale;
@ -270,7 +258,7 @@ protected:
/// @brief Called when horizontal zoom changes
///
/// Implementations can override this method to do something when the horizontal zoom is changed
virtual void OnSetSamplesPerPixel() { }
virtual void OnSetMillisecondsPerPixel() { }
/// @brief Called when vertical zoom changes
///
@ -279,7 +267,7 @@ protected:
public:
/// @brief Constructor
AudioRendererBitmapProvider() : provider(0), pixel_samples(0), amplitude_scale(0) { };
AudioRendererBitmapProvider() : provider(0), pixel_ms(0), amplitude_scale(0) { };
/// @brief Destructor
virtual ~AudioRendererBitmapProvider() { }
@ -307,8 +295,8 @@ public:
void SetProvider(AudioProvider *provider);
/// @brief Change horizontal zoom
/// @param pixel_samples Samples per pixel to zoom to
void SetSamplesPerPixel(int pixel_samples);
/// @param pixel_ms Milliseconds per pixel to zoom to
void SetMillisecondsPerPixel(double new_pixel_ms);
/// @brief Change vertical zoom
/// @param amplitude_scale Scaling factor to zoom to

View file

@ -266,7 +266,7 @@ void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle
for (int ax = start; ax < end; ++ax)
{
// Derived audio data
size_t block_index = (size_t)(ax * pixel_samples) >> derivation_dist;
size_t block_index = (size_t)(ax * pixel_ms * provider->GetSampleRate() / 1000) >> derivation_dist;
float *power = cache->Get(block_index);
// Prepare bitmap writing

View file

@ -82,6 +82,8 @@ void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle
const AudioColorScheme *pal = GetColorScheme(style);
int pixel_samples = (pixel_ms * provider->GetSampleRate() + 500) / 1000;
// Fill the background
dc.SetBrush(wxBrush(pal->get(0.0f)));
dc.SetPen(*wxTRANSPARENT_PEN);
@ -174,7 +176,7 @@ void AudioWaveformRenderer::OnSetProvider()
audio_buffer = 0;
}
void AudioWaveformRenderer::OnSetSamplesPerPixel()
void AudioWaveformRenderer::OnSetMillisecondsPerPixel()
{
delete[] audio_buffer;
audio_buffer = 0;

View file

@ -65,7 +65,7 @@ class AudioWaveformRenderer : public AudioRendererBitmapProvider {
const AudioColorScheme *GetColorScheme(AudioRenderingStyle style) const;
void OnSetProvider();
void OnSetSamplesPerPixel();
void OnSetMillisecondsPerPixel();
public:
/// @brief Constructor

View file

@ -58,8 +58,8 @@ protected:
~AudioRenderingStyleRanges() { }
public:
/// @brief Add a range to the line
/// @param start First sample index in range
/// @param end One past last sample index in range
/// @param start First milisecond in range
/// @param end One past last milisecond in range
/// @param style Style of the range added
virtual void AddRange(int64_t start, int64_t end, AudioRenderingStyle style) = 0;
virtual void AddRange(int start, int end, AudioRenderingStyle style) = 0;
};

View file

@ -67,18 +67,18 @@ public:
/// @return The warning message to show, may be empty if there is none
virtual wxString GetWarningMessage() const = 0;
/// @brief Get the sample range the user is most likely to want to see for the current state
/// @return A sample range
/// @brief Get the time range the user is most likely to want to see for the current state
/// @return A time range
///
/// This is used for "bring working area into view" operations.
virtual SampleRange GetIdealVisibleSampleRange() const = 0;
virtual TimeRange GetIdealVisibleTimeRange() const = 0;
/// @brief Get the primary playback range
/// @return A sample range
/// @return A time range
///
/// Get the sample range the user is most likely to want to play back
/// Get the time range the user is most likely to want to play back
/// currently.
virtual SampleRange GetPrimaryPlaybackRange() const = 0;
virtual TimeRange GetPrimaryPlaybackRange() const = 0;
/// @brief Get all rendering style ranges
/// @param[out] ranges Rendering ranges will be added to this
@ -106,37 +106,37 @@ public:
virtual void Revert() = 0;
/// @brief Determine if a position is close to a draggable marker
/// @param sample The audio sample index to test
/// @param sensitivity Distance in samples to consider markers as nearby
/// @return True if a marker is close by the given sample, as defined by sensitivity
/// @param ms The time in milliseconds to test
/// @param sensitivity Distance in milliseconds to consider markers as nearby
/// @return True if a marker is close by the given time, as defined by sensitivity
///
/// This is solely for hit-testing against draggable markers, for
/// controlling the mouse cursor.
virtual bool IsNearbyMarker(int64_t sample, int sensitivity) const = 0;
virtual bool IsNearbyMarker(int ms, int sensitivity) const = 0;
/// @brief The user pressed the left button down at an empty place in the audio
/// @param sample The audio sample index the user clicked
/// @param sensitivity Distance in samples to consider existing markers
/// @param snap_range Maximum snapping range in samples
/// @param ms The time in milliseconds the user clicked
/// @param sensitivity Distance in milliseconds to consider existing markers
/// @param snap_range Maximum snapping range in milliseconds
/// @return An audio marker or 0. If a marker is returned and the user
/// starts dragging the mouse after pressing down the button, the returned
/// marker is being dragged.
virtual AudioMarker * OnLeftClick(int64_t sample, int sensitivity, int64_t snap_range) = 0;
virtual AudioMarker * OnLeftClick(int ms, int sensitivity, int snap_range) = 0;
/// @brief The user pressed the right button down at an empty place in the audio
/// @param sample The audio sample index the user clicked
/// @param sensitivity Distance in samples to consider existing markers
/// @param snap_range Maximum snapping range in samples
/// @param ms The time in milliseconds the user clicked
/// @param sensitivity Distance in milliseconds to consider existing markers
/// @param snap_range Maximum snapping range in milliseconds
/// @return An audio marker or 0. If a marker is returned and the user
/// starts dragging the mouse after pressing down the button, the returned
/// marker is being dragged.
virtual AudioMarker * OnRightClick(int64_t sample, int sensitivity, int64_t snap_range) = 0;
virtual AudioMarker * OnRightClick(int ms, int sensitivity, int snap_range) = 0;
/// @brief The user dragged a timing marker
/// @param marker The marker being dragged
/// @param new_position Sample position the marker was dragged to
/// @param snap_range Maximum snapping range in samples
virtual void OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t snap_range) = 0;
/// @param new_position Time position the marker was dragged to
/// @param snap_range Maximum snapping range in milliseconds
virtual void OnMarkerDrag(AudioMarker *marker, int new_position, int snap_range) = 0;
/// @brief Destructor
virtual ~AudioTimingController() { }

View file

@ -42,7 +42,6 @@
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_time.h"
#include "audio_controller.h"
#include "audio_marker_provider_keyframes.h"
#include "audio_renderer.h"
#include "audio_timing.h"
@ -61,8 +60,8 @@ class AudioMarkerDialogueTiming : public AudioMarker {
/// The other marker for the dialogue line's pair
AudioMarkerDialogueTiming *other;
/// Current sample position of this marker
int64_t position;
/// Current ms position of this marker
int position;
/// Draw style for the marker
wxPen style;
@ -77,7 +76,7 @@ class AudioMarkerDialogueTiming : public AudioMarker {
public:
// AudioMarker interface
int64_t GetPosition() const { return position; }
int GetPosition() const { return position; }
wxPen GetStyle() const { return style; }
FeetStyle GetFeet() const { return feet; }
bool CanSnap() const { return false; }
@ -86,12 +85,12 @@ public:
// Specific interface
/// @brief Move the marker to a new position
/// @param new_position The position to move the marker to, in audio samples
/// @param new_position The position to move the marker to, in milliseconds
///
/// If the marker moves to the opposite side of the other marker in the pair,
/// the styles of the two markers will be changed to match the new start/end
/// relationship of them.
void SetPosition(int64_t new_position);
void SetPosition(int new_position);
/// @brief Constructor
@ -109,23 +108,23 @@ public:
static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2);
/// Implicit decay to the position of the marker
operator int64_t() const { return position; }
operator int() const { return position; }
};
/// @class InactiveLineMarker
/// @brief Markers for the beginning and ends of inactive lines
class InactiveLineMarker : public AudioMarker {
int64_t position;
int position;
Pen style;
FeetStyle feet;
public:
int64_t GetPosition() const { return position; }
int GetPosition() const { return position; }
wxPen GetStyle() const { return style; }
FeetStyle GetFeet() const { return feet; }
bool CanSnap() const { return true; }
InactiveLineMarker(int64_t position, bool start)
InactiveLineMarker(int position, bool start)
: position(position)
, style("Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness")
, feet(start ? Feet_Right : Feet_Left)
@ -133,7 +132,7 @@ public:
}
/// Implicit decay to the position of the marker
operator int64_t() const { return position; }
operator int() const { return position; }
};
@ -200,13 +199,13 @@ class AudioTimingControllerDialogue : public AudioTimingController, private Sele
/// @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);
/// @param ms New position of the marker
void SetMarker(AudioMarkerDialogueTiming *marker, int ms);
/// Snap a position to a nearby marker, if any
/// @param position Position to snap
/// @param snap_range Maximum distance to snap in samples
int64_t SnapPosition(int64_t position, int64_t snap_range) const;
/// @param snap_range Maximum distance to snap in milliseconds
int SnapPosition(int position, int snap_range) const;
// SubtitleSelectionListener interface
void OnActiveLineChanged(AssDialogue *new_line);
@ -217,22 +216,22 @@ class AudioTimingControllerDialogue : public AudioTimingController, private Sele
public:
// AudioMarkerProvider interface
void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const;
void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const;
// AudioTimingController interface
wxString GetWarningMessage() const;
SampleRange GetIdealVisibleSampleRange() const;
SampleRange GetPrimaryPlaybackRange() const;
TimeRange GetIdealVisibleTimeRange() const;
TimeRange GetPrimaryPlaybackRange() const;
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const;
void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const { }
void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const { }
void Next();
void Prev();
void Commit();
void Revert();
bool IsNearbyMarker(int64_t sample, int sensitivity) const;
AudioMarker * OnLeftClick(int64_t sample, int sensitivity, int64_t snap_range);
AudioMarker * OnRightClick(int64_t sample, int sensitivity, int64_t snap_range);
void OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t snap_range);
bool IsNearbyMarker(int ms, int sensitivity) const;
AudioMarker * OnLeftClick(int ms, int sensitivity, int snap_range);
AudioMarker * OnRightClick(int ms, int sensitivity, int snap_range);
void OnMarkerDrag(AudioMarker *marker, int new_position, int snap_range);
public:
// Specific interface
@ -248,7 +247,7 @@ AudioTimingController *CreateDialogueTimingController(agi::Context *c)
}
// AudioMarkerDialogueTiming
void AudioMarkerDialogueTiming::SetPosition(int64_t new_position)
void AudioMarkerDialogueTiming::SetPosition(int new_position)
{
position = new_position;
@ -302,12 +301,9 @@ AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
, inactive_line_mode(OPT_GET("Audio/Inactive Lines Display Mode"))
, inactive_line_comments(OPT_GET("Audio/Display/Draw/Inactive Comments"))
, 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))
, inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
{
assert(c->audioController != 0);
AudioMarkerDialogueTiming::InitPair(&active_markers[0], &active_markers[1]);
c->selectionController->AddSelectionListener(this);
@ -342,7 +338,7 @@ const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
return &std::max(active_markers[0], active_markers[1]);
}
void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const
void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const
{
// 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
@ -365,10 +361,7 @@ void AudioTimingControllerDialogue::GetMarkers(const SampleRange &range, AudioMa
void AudioTimingControllerDialogue::OnActiveLineChanged(AssDialogue *new_line)
{
if (context->audioController->IsAudioOpen())
{
Revert();
}
Revert();
}
void AudioTimingControllerDialogue::OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed)
@ -393,14 +386,14 @@ wxString AudioTimingControllerDialogue::GetWarningMessage() const
return wxString();
}
SampleRange AudioTimingControllerDialogue::GetIdealVisibleSampleRange() const
TimeRange AudioTimingControllerDialogue::GetIdealVisibleTimeRange() const
{
return GetPrimaryPlaybackRange();
}
SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
TimeRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
{
return SampleRange(*GetLeftMarker(), *GetRightMarker());
return TimeRange(*GetLeftMarker(), *GetRightMarker());
}
void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
@ -424,8 +417,8 @@ void AudioTimingControllerDialogue::Prev()
void AudioTimingControllerDialogue::Commit()
{
int new_start_ms = context->audioController->MillisecondsFromSamples(*GetLeftMarker());
int new_end_ms = context->audioController->MillisecondsFromSamples(*GetRightMarker());
int new_start_ms = *GetLeftMarker();
int new_end_ms = *GetRightMarker();
// If auto committing is enabled, timing_modified will be true iif it is an
// auto commit, as there is never pending changes to commit when the button
@ -464,8 +457,8 @@ void AudioTimingControllerDialogue::Commit()
Next();
if (context->selectionController->GetActiveLine()->End == 0) {
const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
active_markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms));
active_markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(new_end_ms + default_duration));
active_markers[0].SetPosition(new_end_ms);
active_markers[1].SetPosition(new_end_ms + default_duration);
timing_modified = true;
UpdateSelection();
}
@ -478,8 +471,8 @@ void AudioTimingControllerDialogue::Revert()
{
if (line->Start != 0 || line->End != 0)
{
active_markers[0].SetPosition(context->audioController->SamplesFromMilliseconds(line->Start));
active_markers[1].SetPosition(context->audioController->SamplesFromMilliseconds(line->End));
active_markers[0].SetPosition(line->Start);
active_markers[1].SetPosition(line->End);
timing_modified = false;
AnnounceUpdatedPrimaryRange();
if (inactive_line_mode->GetInt() == 0)
@ -489,30 +482,30 @@ void AudioTimingControllerDialogue::Revert()
RegenerateInactiveLines();
}
bool AudioTimingControllerDialogue::IsNearbyMarker(int64_t sample, int sensitivity) const
bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity) const
{
SampleRange range(sample-sensitivity, sample+sensitivity);
TimeRange range(ms-sensitivity, ms+sensitivity);
return range.contains(active_markers[0]) || range.contains(active_markers[1]);
}
AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sensitivity, int64_t snap_range)
AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int ms, int sensitivity, int snap_range)
{
assert(sensitivity >= 0);
int64_t dist_l, dist_r;
int dist_l, dist_r;
AudioMarkerDialogueTiming *left = GetLeftMarker();
AudioMarkerDialogueTiming *right = GetRightMarker();
dist_l = tabs(*left - sample);
dist_r = tabs(*right - sample);
dist_l = tabs(*left - ms);
dist_r = tabs(*right - ms);
if (dist_l < dist_r && dist_l <= sensitivity)
{
// Clicked near the left marker:
// Insta-move it and start dragging it
SetMarker(left, SnapPosition(sample, snap_range));
SetMarker(left, SnapPosition(ms, snap_range));
return left;
}
@ -526,18 +519,18 @@ AudioMarker * AudioTimingControllerDialogue::OnLeftClick(int64_t sample, int sen
// 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
SetMarker(left, SnapPosition(sample, snap_range));
SetMarker(left, SnapPosition(ms, snap_range));
return right;
}
AudioMarker * AudioTimingControllerDialogue::OnRightClick(int64_t sample, int sensitivity, int64_t snap_range)
AudioMarker * AudioTimingControllerDialogue::OnRightClick(int ms, int sensitivity, int snap_range)
{
AudioMarkerDialogueTiming *right = GetRightMarker();
SetMarker(right, SnapPosition(sample, snap_range));
SetMarker(right, SnapPosition(ms, snap_range));
return right;
}
void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t snap_range)
void AudioTimingControllerDialogue::OnMarkerDrag(AudioMarker *marker, int new_position, int snap_range)
{
assert(marker == &active_markers[0] || marker == &active_markers[1]);
@ -550,9 +543,9 @@ void AudioTimingControllerDialogue::UpdateSelection()
AnnounceUpdatedStyleRanges();
}
void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int64_t sample)
void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int ms)
{
marker->SetPosition(sample);
marker->SetPosition(ms);
timing_modified = true;
if (auto_commit->GetBool()) Commit();
UpdateSelection();
@ -619,15 +612,15 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
AnnounceUpdatedStyleRanges();
}
int64_t AudioTimingControllerDialogue::SnapPosition(int64_t position, int64_t snap_range) const
int AudioTimingControllerDialogue::SnapPosition(int position, int snap_range) const
{
if (snap_range <= 0)
return position;
SampleRange snap_sample_range(position - snap_range, position + snap_range);
TimeRange snap_time_range(position - snap_range, position + snap_range);
const AudioMarker *snap_marker = 0;
AudioMarkerVector potential_snaps;
GetMarkers(snap_sample_range, potential_snaps);
GetMarkers(snap_time_range, potential_snaps);
for (AudioMarkerVector::iterator mi = potential_snaps.begin(); mi != potential_snaps.end(); ++mi)
{
if ((*mi)->CanSnap())
@ -646,8 +639,6 @@ int64_t AudioTimingControllerDialogue::SnapPosition(int64_t position, int64_t sn
void AudioTimingControllerDialogue::AddInactiveMarkers(AssDialogue *line)
{
inactive_markers.push_back(InactiveLineMarker(
context->audioController->SamplesFromMilliseconds(line->Start), true));
inactive_markers.push_back(InactiveLineMarker(
context->audioController->SamplesFromMilliseconds(line->End), false));
inactive_markers.push_back(InactiveLineMarker(line->Start, true));
inactive_markers.push_back(InactiveLineMarker(line->End, false));
}

View file

@ -43,27 +43,27 @@
/// @class KaraokeMarker
/// @brief AudioMarker implementation for AudioTimingControllerKaraoke
class KaraokeMarker : public AudioMarker {
int64_t position;
int position;
Pen *pen;
FeetStyle style;
public:
int64_t GetPosition() const { return position; }
int GetPosition() const { return position; }
wxPen GetStyle() const { return *pen; }
FeetStyle GetFeet() const { return style; }
bool CanSnap() const { return false; }
void Move(int64_t new_pos) { position = new_pos; }
void Move(int new_pos) { position = new_pos; }
KaraokeMarker(int64_t position, Pen *pen, FeetStyle style)
KaraokeMarker(int position, Pen *pen, FeetStyle style)
: position(position)
, pen(pen)
, style(style)
{
}
KaraokeMarker(int64_t position) : position(position) { }
operator int64_t() const { return position; }
KaraokeMarker(int position) : position(position) { }
operator int() const { return position; }
};
/// @class AudioTimingControllerKaraoke
@ -111,27 +111,24 @@ class AudioTimingControllerKaraoke : public AudioTimingController {
void OnAutoCommitChange(agi::OptionValue const& opt);
void OnAutoNextChange(agi::OptionValue const& opt);
int64_t ToMS(int64_t samples) const { return c->audioController->MillisecondsFromSamples(samples); }
int64_t ToSamples(int64_t ms) const { return c->audioController->SamplesFromMilliseconds(ms); }
void DoCommit();
public:
// AudioTimingController implementation
void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const;
void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const;
wxString GetWarningMessage() const { return ""; }
SampleRange GetIdealVisibleSampleRange() const;
TimeRange GetIdealVisibleTimeRange() const;
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const;
SampleRange GetPrimaryPlaybackRange() const;
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &out_labels) const;
TimeRange GetPrimaryPlaybackRange() const;
void GetLabels(const TimeRange &range, std::vector<AudioLabel> &out_labels) const;
void Next();
void Prev();
void Commit();
void Revert();
bool IsNearbyMarker(int64_t sample, int sensitivity) const;
AudioMarker * OnLeftClick(int64_t sample, int sensitivity, int64_t);
AudioMarker * OnRightClick(int64_t, int, int64_t) { return 0; }
void OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t);
bool IsNearbyMarker(int ms, int sensitivity) const;
AudioMarker * OnLeftClick(int ms, int sensitivity, int);
AudioMarker * OnRightClick(int, int, int) { return 0; }
void OnMarkerDrag(AudioMarker *marker, int new_position, int);
AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed);
};
@ -150,8 +147,8 @@ AudioTimingControllerKaraoke::AudioTimingControllerKaraoke(agi::Context *c, AssK
, separator_pen("Colour/Audio Display/Syllable Boundaries", "Audio/Line Boundaries Thickness", wxPENSTYLE_DOT)
, start_pen("Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness")
, end_pen("Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness")
, start_marker(ToSamples(active_line->Start), &start_pen, AudioMarker::Feet_Right)
, end_marker(ToSamples(active_line->End), &end_pen, AudioMarker::Feet_Left)
, start_marker(active_line->Start, &start_pen, AudioMarker::Feet_Right)
, end_marker(active_line->End, &end_pen, AudioMarker::Feet_Left)
, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Karaoke Mode")
, auto_commit(OPT_GET("Audio/Auto/Commit")->GetBool())
, auto_next(OPT_GET("Audio/Next Line on Commit")->GetBool())
@ -210,21 +207,21 @@ void AudioTimingControllerKaraoke::Prev() {
void AudioTimingControllerKaraoke::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
{
SampleRange sr = GetPrimaryPlaybackRange();
TimeRange sr = GetPrimaryPlaybackRange();
ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Selected);
}
SampleRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
return SampleRange(
TimeRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
return TimeRange(
cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
cur_syl < markers.size() ? markers[cur_syl] : end_marker);
}
SampleRange AudioTimingControllerKaraoke::GetIdealVisibleSampleRange() const {
return SampleRange(start_marker, end_marker);
TimeRange AudioTimingControllerKaraoke::GetIdealVisibleTimeRange() const {
return TimeRange(start_marker, end_marker);
}
void AudioTimingControllerKaraoke::GetMarkers(SampleRange const& range, AudioMarkerVector &out) const {
void AudioTimingControllerKaraoke::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
size_t i;
for (i = 0; i < markers.size() && markers[i] < range.begin(); ++i) ;
for (; i < markers.size() && markers[i] < range.end(); ++i)
@ -256,8 +253,8 @@ void AudioTimingControllerKaraoke::Revert() {
cur_syl = 0;
commit_id = -1;
start_marker.Move(ToSamples(active_line->Start));
end_marker.Move(ToSamples(active_line->End));
start_marker.Move(active_line->Start);
end_marker.Move(active_line->End);
markers.clear();
labels.clear();
@ -266,10 +263,9 @@ void AudioTimingControllerKaraoke::Revert() {
labels.reserve(kara->size());
for (AssKaraoke::iterator it = kara->begin(); it != kara->end(); ++it) {
int64_t sample = ToSamples(it->start_time);
if (it != kara->begin())
markers.push_back(KaraokeMarker(sample, &separator_pen, AudioMarker::Feet_None));
labels.push_back(AudioLabel(it->text, SampleRange(sample, ToSamples(it->start_time + it->duration))));
markers.push_back(KaraokeMarker(it->start_time, &separator_pen, AudioMarker::Feet_None));
labels.push_back(AudioLabel(it->text, TimeRange(it->start_time, it->start_time + it->duration)));
}
AnnounceUpdatedPrimaryRange();
@ -277,8 +273,8 @@ void AudioTimingControllerKaraoke::Revert() {
AnnounceMarkerMoved();
}
bool AudioTimingControllerKaraoke::IsNearbyMarker(int64_t sample, int sensitivity) const {
SampleRange range(sample - sensitivity, sample + sensitivity);
bool AudioTimingControllerKaraoke::IsNearbyMarker(int ms, int sensitivity) const {
TimeRange range(ms - sensitivity, ms + sensitivity);
for (size_t i = 0; i < markers.size(); ++i)
if (range.contains(markers[i]))
@ -287,10 +283,10 @@ bool AudioTimingControllerKaraoke::IsNearbyMarker(int64_t sample, int sensitivit
return false;
}
AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int64_t sample, int sensitivity, int64_t) {
SampleRange range(sample - sensitivity, sample + sensitivity);
AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int ms, int sensitivity, int) {
TimeRange range(ms - sensitivity, ms + sensitivity);
size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), sample));
size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
if (syl < markers.size() && range.contains(markers[syl]))
return &markers[syl];
if (syl > 0 && range.contains(markers[syl - 1]))
@ -304,7 +300,7 @@ AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int64_t sample, int sensi
return 0;
}
void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_position, int64_t) {
void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int new_position, int) {
KaraokeMarker *marker = static_cast<KaraokeMarker*>(m);
// No rearranging of syllables allowed
new_position = mid(
@ -315,7 +311,7 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
marker->Move(new_position);
size_t syl = marker - &markers.front() + 1;
kara->SetStartTime(syl, ToMS(new_position));
kara->SetStartTime(syl, new_position);
if (syl == cur_syl || syl == cur_syl + 1) {
AnnounceUpdatedPrimaryRange();
@ -324,8 +320,8 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
AnnounceMarkerMoved();
labels[syl - 1].range = SampleRange(labels[syl - 1].range.begin(), new_position);
labels[syl].range = SampleRange(new_position, labels[syl].range.end());
labels[syl - 1].range = TimeRange(labels[syl - 1].range.begin(), new_position);
labels[syl].range = TimeRange(new_position, labels[syl].range.end());
AnnounceLabelChanged();
if (auto_commit)
@ -334,7 +330,7 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
commit_id = -1;
}
void AudioTimingControllerKaraoke::GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const {
void AudioTimingControllerKaraoke::GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const {
for (size_t i = 0; i < labels.size(); ++i) {
if (range.overlaps(labels[i].range))
out.push_back(labels[i]);

View file

@ -223,8 +223,7 @@ struct audio_save_clip : public Command {
for (Selection::iterator it = sel.begin(); it != sel.end(); ++it) {
c->audioController->SaveClip(
wxFileSelector(_("Save audio clip"), "", "", "wav", "", wxFD_SAVE|wxFD_OVERWRITE_PROMPT, c->parent),
SampleRange(c->audioController->SamplesFromMilliseconds((*it)->Start),
c->audioController->SamplesFromMilliseconds((*it)->End)));
TimeRange((*it)->Start, (*it)->End));
}
}
};
@ -268,10 +267,8 @@ struct audio_play_before : public validate_audio_open {
void operator()(agi::Context *c) {
c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayRange(SampleRange(
times.begin() - c->audioController->SamplesFromMilliseconds(500),
times.begin()));
int begin = c->audioController->GetPrimaryPlaybackRange().begin();
c->audioController->PlayRange(TimeRange(begin - 500, begin));
}
};
@ -284,10 +281,8 @@ struct audio_play_after : public validate_audio_open {
void operator()(agi::Context *c) {
c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayRange(SampleRange(
times.end(),
times.end() + c->audioController->SamplesFromMilliseconds(500)));
int end = c->audioController->GetPrimaryPlaybackRange().end();
c->audioController->PlayRange(TimeRange(end, end + 500));
}
};
@ -300,11 +295,8 @@ struct audio_play_end : public validate_audio_open {
void operator()(agi::Context *c) {
c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayToEndOfPrimary(
times.end() - std::min(
c->audioController->SamplesFromMilliseconds(500),
times.length()));
TimeRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayToEndOfPrimary(times.end() - std::min(500, times.length()));
}
};
@ -317,12 +309,10 @@ struct audio_play_begin : public validate_audio_open {
void operator()(agi::Context *c) {
c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayRange(SampleRange(
TimeRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayRange(TimeRange(
times.begin(),
times.begin() + std::min(
c->audioController->SamplesFromMilliseconds(500),
times.length())));
times.begin() + std::min(500, times.length())));
}
};

View file

@ -320,9 +320,7 @@ void VideoContext::NextFrame() {
JumpToFrame(frame_n + 1);
// Start playing audio
if (playAudioOnStep->GetBool()) {
context->audioController->PlayRange(SampleRange(
context->audioController->SamplesFromMilliseconds(TimeAtFrame(frame_n - 1)),
context->audioController->SamplesFromMilliseconds(TimeAtFrame(frame_n))));
context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n - 1), TimeAtFrame(frame_n)));
}
}
@ -333,9 +331,7 @@ void VideoContext::PrevFrame() {
JumpToFrame(frame_n - 1);
// Start playing audio
if (playAudioOnStep->GetBool()) {
context->audioController->PlayRange(SampleRange(
context->audioController->SamplesFromMilliseconds(TimeAtFrame(frame_n)),
context->audioController->SamplesFromMilliseconds(TimeAtFrame(frame_n + 1))));
context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n), TimeAtFrame(frame_n + 1)));
}
}
@ -352,7 +348,7 @@ void VideoContext::Play() {
endFrame = GetLength() - 1;
// Start playing audio
context->audioController->PlayToEnd(context->audioController->SamplesFromMilliseconds(startMS));
context->audioController->PlayToEnd(startMS);
// Start timer
playTime.Start();
@ -366,9 +362,7 @@ void VideoContext::PlayLine() {
if (!curline) return;
// Start playing audio
context->audioController->PlayRange(SampleRange(
context->audioController->SamplesFromMilliseconds(curline->Start),
context->audioController->SamplesFromMilliseconds(curline->End)));
context->audioController->PlayRange(TimeRange(curline->Start, curline->End));
// Round-trip conversion to convert start to exact
int startFrame = FrameAtTime(context->selectionController->GetActiveLine()->Start,agi::vfr::START);