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() { void AudioBox::ScrollToActiveLine() {
if (controller->GetTimingController()) if (controller->GetTimingController())
audioDisplay->ScrollSampleRangeInView(controller->GetTimingController()->GetIdealVisibleSampleRange()); audioDisplay->ScrollTimeRangeInView(controller->GetTimingController()->GetIdealVisibleTimeRange());
} }

View file

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

View file

@ -65,47 +65,44 @@ class AudioMarkerProvider;
typedef std::vector<const AudioMarker*> AudioMarkerVector; typedef std::vector<const AudioMarker*> AudioMarkerVector;
/// @class TimeRange
/// @class SampleRange /// @brief Represents an immutable range of time
/// @brief Represents an immutable range of audio samples class TimeRange {
class SampleRange { int _begin;
int64_t _begin; int _end;
int64_t _end;
public: public:
/// @brief Constructor /// @brief Constructor
/// @param begin Index of the first sample to include in the range /// @param begin Index of the first millisecond to include in the range
/// @param end Index of one past the last sample to include in the range /// @param end Index of one past the last millisecond to include in the range
SampleRange(int64_t begin, int64_t end) TimeRange(int begin, int end) : _begin(begin), _end(end)
: _begin(begin)
, _end(end)
{ {
assert(end >= begin); assert(end >= begin);
} }
/// @brief Copy constructor, optionally adjusting the range /// @brief Copy constructor, optionally adjusting the range
/// @param src The range to duplicate /// @param src The range to duplicate
/// @param begin_adjust Number of samples to add to the start of the range /// @param begin_adjust Number of milliseconds to add to the start of the range
/// @param end_adjust Number of samples to add to the end of the range /// @param end_adjust Number of milliseconds to add to the end of the range
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0) TimeRange(const TimeRange &src, int begin_adjust = 0, int end_adjust = 0)
{ {
_begin = src._begin + begin_adjust; _begin = src._begin + begin_adjust;
_end = src._end + end_adjust; _end = src._end + end_adjust;
assert(_end >= _begin); assert(_end >= _begin);
} }
/// Get the number of samples in the range /// Get the length of the range in milliseconds
int64_t length() const { return _end - _begin; } int length() const { return _end - _begin; }
/// Get the index of the first sample in the range /// Get the start time of the range in milliseconds
int64_t begin() const { return _begin; } int begin() const { return _begin; }
/// Get the index of one past the last sample in the range /// Get the exclusive end time of the range in milliseconds
int64_t end() const { return _end; } int end() const { return _end; }
/// Determine whether the range contains a given sample index /// Determine whether the range contains a given time in milliseconds
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); } bool contains(int ms) const { return ms >= begin() && ms < end(); }
/// Determine whether there is an overlap between two ranges /// 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); return other.contains(_begin) || contains(other._begin);
} }
@ -121,8 +118,8 @@ public:
/// Virtual destructor, does nothing /// Virtual destructor, does nothing
virtual ~AudioMarkerProvider() { } virtual ~AudioMarkerProvider() { }
/// @brief Return markers in a sample range /// @brief Return markers in a time range
virtual void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const = 0; virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0;
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener) DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
}; };
@ -134,22 +131,22 @@ protected:
/// One or more of the labels provided by this object have changed /// One or more of the labels provided by this object have changed
agi::signal::Signal<> AnnounceLabelChanged; agi::signal::Signal<> AnnounceLabelChanged;
public: 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 { struct AudioLabel {
/// Text of the label /// Text of the label
wxString text; wxString text;
/// Range which this label applies to /// Range which this label applies to
SampleRange range; TimeRange range;
AudioLabel(wxString const& text, SampleRange const& range) : text(text), range(range) { } AudioLabel(wxString const& text, TimeRange const& range) : text(text), range(range) { }
}; };
/// Virtual destructor, does nothing /// Virtual destructor, does nothing
virtual ~AudioLabelProvider() { } virtual ~AudioLabelProvider() { }
/// @brief Get labels in a sample range /// @brief Get labels in a time range
/// @param range Range of samples to get labels for /// @param range Range of times to get labels for
/// @param[out] out Vector which should be filled with the labels /// @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) DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
}; };
@ -190,7 +187,7 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
agi::signal::Signal<> AnnounceAudioClose; agi::signal::Signal<> AnnounceAudioClose;
/// Playback is in progress and the current position was updated /// Playback is in progress and the current position was updated
agi::signal::Signal<int64_t> AnnouncePlaybackPosition; agi::signal::Signal<int> AnnouncePlaybackPosition;
/// Playback has stopped /// Playback has stopped
agi::signal::Signal<> AnnouncePlaybackStop; agi::signal::Signal<> AnnouncePlaybackStop;
@ -258,8 +255,17 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
void OnComputerResuming(wxPowerEvent &event); void OnComputerResuming(wxPowerEvent &event);
#endif #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 /// @brief Constructor
AudioController(agi::Context *context); AudioController(agi::Context *context);
@ -294,7 +300,7 @@ public:
/// ///
/// The end of the played back range may be requested changed, but is not /// The end of the played back range may be requested changed, but is not
/// changed automatically from any other operations. /// 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 /// @brief Start or restart audio playback, playing the primary playback range
/// ///
@ -304,19 +310,19 @@ public:
void PlayPrimaryRange(); void PlayPrimaryRange();
/// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range /// @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 /// This behaves like PlayPrimaryRange, but the start point can differ from
/// the beginning of the primary range. /// 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 /// @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 to end cannot be converted to a range playback like range
/// playback can, it will continue until the end is reached, it is stopped, /// playback can, it will continue until the end is reached, it is stopped,
/// or restarted. /// or restarted.
void PlayToEnd(int64_t start_sample); void PlayToEnd(int start_ms);
/// @brief Stop all audio playback /// @brief Stop all audio playback
void Stop(); void Stop();
@ -326,35 +332,39 @@ public:
bool IsPlaying(); bool IsPlaying();
/// @brief Get the current playback position /// @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. /// 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 /// @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 /// 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 /// source that might not be able to keep up with the full speed, such as
/// video playback in high resolution or with complex subtitles. /// video playback in high resolution or with complex subtitles.
/// ///
/// This function only does something if audio is already playing. /// 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 /// @brief Get the primary playback range
/// @return An immutable SampleRange object /// @return An immutable TimeRange object
SampleRange GetPrimaryPlaybackRange() const; TimeRange GetPrimaryPlaybackRange() const;
/// @brief Get all markers inside a range /// @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 /// @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 /// @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 /// @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 /// @brief Get the playback audio volume
@ -376,20 +386,10 @@ public:
/// dialogue line. /// dialogue line.
void SetTimingController(AudioTimingController *new_controller); 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 /// @brief Save a portion of the decoded loaded audio to a wav file
/// @param filename File to save to /// @param filename File to save to
/// @param range Range of samples to save /// @param range Time range to save
void SaveClip(wxString const& filename, SampleRange const& range) const; void SaveClip(wxString const& filename, TimeRange const& range) const;
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener) DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener) DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
@ -414,8 +414,8 @@ public:
}; };
/// @brief Get the marker's position /// @brief Get the marker's position
/// @return The marker's position in samples /// @return The marker's position in milliseconds
virtual int64_t GetPosition() const = 0; virtual int GetPosition() const = 0;
/// @brief Get the marker's drawing style /// @brief Get the marker's drawing style
/// @return A pen object describing 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 "audio_timing.h"
#include "block_cache.h" #include "block_cache.h"
#include "compat.h" #include "compat.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h" #include "include/aegisub/hotkey.h"
#include "main.h" #include "main.h"
@ -250,10 +249,9 @@ const int AudioDisplayScrollbar::min_width;
class AudioDisplayTimeline : public AudioDisplayInteractionObject { class AudioDisplayTimeline : public AudioDisplayInteractionObject {
int64_t num_samples; int duration; ///< Total duration in ms
int samplerate; double ms_per_pixel; ///< Milliseconds per pixel
int samples_per_pixel; int pixel_left; ///< Leftmost visible pixel (i.e. scroll position)
int pixel_left;
wxRect bounds; 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 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 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 UIColours colours; ///< Colour provider
public: public:
AudioDisplayTimeline(AudioDisplay *display) AudioDisplayTimeline(AudioDisplay *display)
: num_samples(0) : duration(0)
, samplerate(44100) , ms_per_pixel(1.0)
, samples_per_pixel(1)
, pixel_left(0) , pixel_left(0)
, dragging(false) , dragging(false)
, display(display) , display(display)
{
}
int GetHeight() const
{ {
int width, height; int width, height;
display->GetTextExtent("0123456789:.", &width, &height); display->GetTextExtent("0123456789:.", &width, &height);
return height + 4; bounds.height = height + 4;
} }
void SetColourScheme(std::string const& name) void SetColourScheme(std::string const& name)
@ -308,57 +300,55 @@ public:
{ {
// The size is without anything that goes below the timeline (like scrollbar) // The size is without anything that goes below the timeline (like scrollbar)
bounds.width = display_size.x; bounds.width = display_size.x;
bounds.height = GetHeight();
bounds.x = 0; bounds.x = 0;
bounds.y = 0; bounds.y = 0;
} }
int GetHeight() const { return bounds.height; }
const wxRect & GetBounds() const { return bounds; } const wxRect & GetBounds() const { return bounds; }
void ChangeAudio(int64_t new_length, int new_samplerate) void ChangeAudio(int new_duration)
{ {
num_samples = new_length; duration = new_duration;
samplerate = new_samplerate;
} }
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 = 1000.0 / ms_per_pixel;
double px_sec = (double)samplerate / (double)samples_per_pixel;
if (px_sec > 3000) { if (px_sec > 3000) {
scale_minor = Sc_Millisecond; scale_minor = Sc_Millisecond;
scale_minor_divisor = (double)samplerate / 1000; scale_minor_divisor = 1.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} else if (px_sec > 300) { } else if (px_sec > 300) {
scale_minor = Sc_Centisecond; scale_minor = Sc_Centisecond;
scale_minor_divisor = (double)samplerate / 100; scale_minor_divisor = 10.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} else if (px_sec > 30) { } else if (px_sec > 30) {
scale_minor = Sc_Decisecond; scale_minor = Sc_Decisecond;
scale_minor_divisor = (double)samplerate / 10; scale_minor_divisor = 100.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} else if (px_sec > 3) { } else if (px_sec > 3) {
scale_minor = Sc_Second; scale_minor = Sc_Second;
scale_minor_divisor = (double)samplerate; scale_minor_divisor = 1000.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} else if (px_sec > 1.0/3.0) { } else if (px_sec > 1.0/3.0) {
scale_minor = Sc_Decasecond; scale_minor = Sc_Decasecond;
scale_minor_divisor = (double)samplerate * 10; scale_minor_divisor = 10000.0;
scale_major_modulo = 6; scale_major_modulo = 6;
} else if (px_sec > 1.0/9.0) { } else if (px_sec > 1.0/9.0) {
scale_minor = Sc_Minute; scale_minor = Sc_Minute;
scale_minor_divisor = (double)samplerate * 60; scale_minor_divisor = 60000.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} else if (px_sec > 1.0/90.0) { } else if (px_sec > 1.0/90.0) {
scale_minor = Sc_Decaminute; scale_minor = Sc_Decaminute;
scale_minor_divisor = (double)samplerate * 600; scale_minor_divisor = 600000.0;
scale_major_modulo = 6; scale_major_modulo = 6;
} else { } else {
scale_minor = Sc_Hour; scale_minor = Sc_Hour;
scale_minor_divisor = (double)samplerate * 3600; scale_minor_divisor = 3600000.0;
scale_major_modulo = 10; scale_major_modulo = 10;
} }
} }
@ -408,19 +398,19 @@ public:
dc.SetTextForeground(colours.Light()); dc.SetTextForeground(colours.Light());
// Figure out the first scale mark to show // Figure out the first scale mark to show
int64_t sample_left = pixel_left * samples_per_pixel; int ms_left = int(pixel_left * ms_per_pixel);
int next_scale_mark = (int)(sample_left / scale_minor_divisor); int next_scale_mark = int(ms_left / scale_minor_divisor);
if (next_scale_mark * scale_minor_divisor < sample_left) if (next_scale_mark * scale_minor_divisor < ms_left)
next_scale_mark += 1; 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 // Draw scale marks
int next_scale_mark_pos; int next_scale_mark_pos;
int last_text_right = -1; int last_text_right = -1;
int last_hour = -1, last_minute = -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 { 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; bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
if (mark_is_major) if (mark_is_major)
@ -431,7 +421,7 @@ public:
// Print time labels on major scale marks // Print time labels on major scale marks
if (mark_is_major && next_scale_mark_pos > last_text_right) 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_hour = (int)(mark_time / 3600);
int mark_minute = (int)(mark_time / 60) % 60; int mark_minute = (int)(mark_time / 60) % 60;
double mark_second = mark_time - mark_hour*3600 - mark_minute*60; double mark_second = mark_time - mark_hour*3600 - mark_minute*60;
@ -504,8 +494,8 @@ public:
{ {
timing_controller->OnMarkerDrag( timing_controller->OnMarkerDrag(
marker, marker,
display->SamplesFromRelativeX(event.GetPosition().x), display->TimeFromRelativeX(event.GetPosition().x),
default_snap != event.ShiftDown() ? display->SamplesFromAbsoluteX(snap_range) : 0); default_snap != event.ShiftDown() ? display->TimeFromAbsoluteX(snap_range) : 0);
} }
// We lose the marker drag if the button used to initiate it goes up // We lose the marker drag if the button used to initiate it goes up
@ -514,14 +504,14 @@ public:
}; };
class AudioStyleRangeMerger : public AudioRenderingStyleRanges { class AudioStyleRangeMerger : public AudioRenderingStyleRanges {
typedef std::map<int64_t, AudioRenderingStyle> style_map; typedef std::map<int, AudioRenderingStyle> style_map;
public: public:
typedef style_map::iterator iterator; typedef style_map::iterator iterator;
private: private:
style_map points; style_map points;
void Split(int64_t point) void Split(int point)
{ {
iterator it = points.lower_bound(point); iterator it = points.lower_bound(point);
if (it == points.end() || it->first != 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()); assert(points.lower_bound(end) != points.end());
for (iterator pt = points.lower_bound(start); pt->first < end; ++pt) for (iterator pt = points.lower_bound(start); pt->first < end; ++pt)
@ -547,7 +537,7 @@ public:
points[0] = AudioStyle_Normal; 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; 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)) , audio_open_connection(controller->AddAudioOpenListener(&AudioDisplay::OnAudioOpen, this))
, context(context) , context(context)
, audio_renderer(new AudioRenderer) , audio_renderer(new AudioRenderer)
, provider(0)
, controller(controller) , controller(controller)
, scrollbar(new AudioDisplayScrollbar(this)) , scrollbar(new AudioDisplayScrollbar(this))
, timeline(new AudioDisplayTimeline(this)) , timeline(new AudioDisplayTimeline(this))
@ -627,29 +616,11 @@ void AudioDisplay::ScrollPixelToLeft(int pixel_position)
} }
void AudioDisplay::ScrollPixelToCenter(int pixel_position) void AudioDisplay::ScrollTimeRangeInView(const TimeRange &range)
{
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)
{ {
int client_width = GetClientRect().GetWidth(); int client_width = GetClientRect().GetWidth();
int range_begin = AbsoluteXFromSamples(range.begin()); int range_begin = AbsoluteXFromTime(range.begin());
int range_end = AbsoluteXFromSamples(range.end()); int range_end = AbsoluteXFromTime(range.end());
int range_len = range_end - range_begin; int range_len = range_end - range_begin;
// Is everything already in view? // Is everything already in view?
@ -690,35 +661,24 @@ void AudioDisplay::SetZoomLevel(int new_zoom_level)
{ {
zoom_level = 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 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 (ms_per_pixel != new_ms_per_pixel)
if (pixel_samples != new_samples_per_pixel)
{ {
int client_width = GetClientSize().GetWidth(); 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; ms_per_pixel = new_ms_per_pixel;
audio_renderer->SetSamplesPerPixel(pixel_samples); pixel_audio_width = std::max(1, int(controller->GetDuration() / ms_per_pixel));
pixel_audio_width = provider->GetNumSamples() / pixel_samples + 1;
audio_renderer->SetMillisecondsPerPixel(ms_per_pixel);
scrollbar->ChangeLengths(pixel_audio_width, client_width); scrollbar->ChangeLengths(pixel_audio_width, client_width);
timeline->ChangeZoom(pixel_samples); timeline->ChangeZoom(ms_per_pixel);
ScrollSampleToCenter(center_sample);
ScrollTimeToCenter(center_time);
Refresh(); Refresh();
} }
} }
@ -770,13 +730,6 @@ void AudioDisplay::SetAmplitudeScale(float scale)
Refresh(); Refresh();
} }
float AudioDisplay::GetAmplitudeScale() const
{
return audio_renderer->GetAmplitudeScale();
}
void AudioDisplay::ReloadRenderingSettings() void AudioDisplay::ReloadRenderingSettings()
{ {
std::string colour_scheme_name; std::string colour_scheme_name;
@ -831,13 +784,6 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
{ {
wxAutoBufferedPaintDC dc(this); wxAutoBufferedPaintDC dc(this);
if (!provider)
{
dc.SetBackground(*wxBLACK_BRUSH);
dc.Clear();
return;
}
wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height); wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height);
bool redraw_scrollbar = false; bool redraw_scrollbar = false;
bool redraw_timeline = false; bool redraw_timeline = false;
@ -857,13 +803,13 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
if (audio_bounds.Intersects(updrect)) if (audio_bounds.Intersects(updrect))
{ {
SampleRange updsamples( TimeRange updtime(
SamplesFromRelativeX(updrect.x - foot_size), std::max(0, TimeFromRelativeX(updrect.x - foot_size)),
SamplesFromRelativeX(updrect.x + updrect.width + foot_size)); std::max(0, TimeFromRelativeX(updrect.x + updrect.width + foot_size)));
PaintAudio(dc, updsamples, updrect); PaintAudio(dc, updtime, updrect);
PaintMarkers(dc, updsamples); PaintMarkers(dc, updtime);
PaintLabels(dc, updsamples); PaintLabels(dc, updtime);
} }
} }
@ -878,10 +824,10 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
timeline->Paint(dc); 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<int, int>::iterator pt = style_ranges.upper_bound(updtime.begin());
std::map<int64_t, int>::iterator pe = style_ranges.upper_bound(updsamples.end()); std::map<int, int>::iterator pe = style_ranges.upper_bound(updtime.end());
if (pt != style_ranges.begin()) if (pt != style_ranges.begin())
--pt; --pt;
@ -889,8 +835,8 @@ void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
while (pt != pe) while (pt != pe)
{ {
AudioRenderingStyle range_style = static_cast<AudioRenderingStyle>(pt->second); AudioRenderingStyle range_style = static_cast<AudioRenderingStyle>(pt->second);
int range_x1 = std::max(updrect.x, RelativeXFromSamples(pt->first)); int range_x1 = std::max(updrect.x, RelativeXFromTime(pt->first));
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromSamples(pt->first); int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromTime(pt->first);
if (range_x2 > range_x1) 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; AudioMarkerVector markers;
controller->GetMarkers(updsamples, markers); controller->GetMarkers(updtime, markers);
if (markers.empty()) return; if (markers.empty()) return;
wxDCPenChanger pen_retainer(dc, wxPen()); 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) for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
{ {
const AudioMarker *marker = *marker_i; const AudioMarker *marker = *marker_i;
int marker_x = RelativeXFromSamples(marker->GetPosition()); int marker_x = RelativeXFromTime(marker->GetPosition());
dc.SetPen(marker->GetStyle()); dc.SetPen(marker->GetStyle());
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height); 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); 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; std::vector<AudioLabelProvider::AudioLabel> labels;
controller->GetLabels(updsamples, labels); controller->GetLabels(updtime, labels);
if (labels.empty()) return; if (labels.empty()) return;
wxDCFontChanger fc(dc); wxDCFontChanger fc(dc);
@ -949,8 +895,8 @@ void AudioDisplay::PaintLabels(wxDC &dc, SampleRange updsamples)
for (size_t i = 0; i < labels.size(); ++i) for (size_t i = 0; i < labels.size(); ++i)
{ {
wxSize extent = dc.GetTextExtent(labels[i].text); wxSize extent = dc.GetTextExtent(labels[i].text);
int left = RelativeXFromSamples(labels[i].range.begin()); int left = RelativeXFromTime(labels[i].range.begin());
int width = AbsoluteXFromSamples(labels[i].range.length()); int width = AbsoluteXFromTime(labels[i].range.length());
// If it doesn't fit, truncate // If it doesn't fit, truncate
if (width < extent.GetWidth()) if (width < extent.GetWidth())
@ -1034,7 +980,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
if (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 = new_label_time.GetASSFormated();
track_cursor_label_rect.x += new_pos - old_pos; track_cursor_label_rect.x += new_pos - old_pos;
RefreshRect(track_cursor_label_rect, false); RefreshRect(track_cursor_label_rect, false);
@ -1118,9 +1064,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
if (event.MiddleIsDown()) if (event.MiddleIsDown())
{ {
context->videoController->JumpToTime( context->videoController->JumpToTime(TimeFromRelativeX(mousepos.x), agi::vfr::EXACT);
controller->MillisecondsFromSamples(SamplesFromRelativeX(mousepos.x)),
agi::vfr::EXACT);
return; return;
} }
@ -1131,15 +1075,15 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
AudioTimingController *timing = controller->GetTimingController(); AudioTimingController *timing = controller->GetTimingController();
if (!timing) return; if (!timing) return;
int drag_sensitivity = pixel_samples * OPT_GET("Audio/Start Drag Sensitivity")->GetInt(); 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() ? pixel_samples * OPT_GET("Audio/Snap/Distance")->GetInt() : 0; 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 // Not scrollbar, not timeline, no button action
if (event.Moving()) 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)); SetCursor(wxCursor(wxCURSOR_SIZEWE));
else else
SetCursor(wxNullCursor); SetCursor(wxNullCursor);
@ -1147,10 +1091,10 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
if (event.LeftDown() || event.RightDown()) if (event.LeftDown() || event.RightDown())
{ {
int64_t samplepos = SamplesFromRelativeX(mousepos.x); int timepos = TimeFromRelativeX(mousepos.x);
AudioMarker *marker = event.LeftDown() ? AudioMarker *marker = event.LeftDown() ?
timing->OnLeftClick(samplepos, drag_sensitivity, snap_sensitivity) : timing->OnLeftClick(timepos, drag_sensitivity, snap_sensitivity) :
timing->OnRightClick(samplepos, drag_sensitivity, snap_sensitivity); timing->OnRightClick(timepos, drag_sensitivity, snap_sensitivity);
if (marker) if (marker)
{ {
@ -1176,8 +1120,11 @@ void AudioDisplay::OnSize(wxSizeEvent &)
timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y)); timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
scrollbar->SetDisplaySize(size); scrollbar->SetDisplaySize(size);
SampleRange sel(controller->GetPrimaryPlaybackRange()); if (controller->GetTimingController())
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length())); {
TimeRange sel(controller->GetTimingController()->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
}
audio_height = size.GetHeight(); audio_height = size.GetHeight();
audio_height -= scrollbar->GetBounds().GetHeight(); audio_height -= scrollbar->GetBounds().GetHeight();
@ -1199,17 +1146,15 @@ void AudioDisplay::OnFocus(wxFocusEvent &)
void AudioDisplay::OnAudioOpen(AudioProvider *provider) void AudioDisplay::OnAudioOpen(AudioProvider *provider)
{ {
this->provider = provider;
if (!audio_renderer_provider) if (!audio_renderer_provider)
ReloadRenderingSettings(); ReloadRenderingSettings();
audio_renderer->SetAudioProvider(provider); audio_renderer->SetAudioProvider(provider);
audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024); audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
if (provider) timeline->ChangeAudio(controller->GetDuration());
timeline->ChangeAudio(provider->GetNumSamples(), provider->GetSampleRate());
ms_per_pixel = 0;
SetZoomLevel(zoom_level); SetZoomLevel(zoom_level);
Refresh(); 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); SetTrackCursor(pixel_position, false);
if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool()) if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool())
@ -1260,12 +1205,12 @@ void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
void AudioDisplay::OnSelectionChanged() void AudioDisplay::OnSelectionChanged()
{ {
SampleRange sel(controller->GetPrimaryPlaybackRange()); TimeRange sel(controller->GetPrimaryPlaybackRange());
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length())); scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
if (OPT_GET("Audio/Auto/Scroll")->GetBool()) if (OPT_GET("Audio/Auto/Scroll")->GetBool())
{ {
ScrollSampleRangeInView(sel); ScrollTimeRangeInView(sel);
} }
RefreshRect(scrollbar->GetBounds(), false); RefreshRect(scrollbar->GetBounds(), false);
@ -1278,16 +1223,16 @@ void AudioDisplay::OnStyleRangesChanged()
AudioStyleRangeMerger asrm; AudioStyleRangeMerger asrm;
controller->GetTimingController()->GetRenderingStyles(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); swap(old_style_ranges, style_ranges);
style_ranges.insert(asrm.begin(), asrm.end()); style_ranges.insert(asrm.begin(), asrm.end());
std::map<int64_t, int>::iterator old_style_it = old_style_ranges.begin(); std::map<int, 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 new_style_it = style_ranges.begin();
int old_style = old_style_it->second; int old_style = old_style_it->second;
int new_style = new_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 // Repaint each range which has changed
while (old_style_it != old_style_ranges.end() || new_style_it != style_ranges.end()) 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 // Fill in the last style range
if (old_style != new_style) 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; time_start = RelativeXFromTime(time_start) - foot_size;
sample_end = RelativeXFromSamples(sample_end) + 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 /// The current audio renderer
agi::scoped_ptr<AudioRendererBitmapProvider> audio_renderer_provider; agi::scoped_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
/// Our current audio provider
AudioProvider *provider;
/// The controller managing us /// The controller managing us
AudioController *controller; AudioController *controller;
@ -139,8 +136,8 @@ class AudioDisplay: public wxWindow {
/// Total width of the audio in pixels /// Total width of the audio in pixels
int pixel_audio_width; int pixel_audio_width;
/// Horizontal zoom measured in audio samples per pixel /// Horizontal zoom measured in millisecond per pixels
int pixel_samples; double ms_per_pixel;
/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral /// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
float scale_amplitude; float scale_amplitude;
@ -171,7 +168,7 @@ class AudioDisplay: public wxWindow {
void RemoveTrackCursor(); void RemoveTrackCursor();
/// Previous style ranges for optimizing redraw when ranges change /// 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 /// @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. /// in Options and need to be reloaded to take effect.
void ReloadRenderingSettings(); void ReloadRenderingSettings();
/// @brief Repaint a range of samples /// @brief Repaint a time range
/// @param sample_start First sample to repaint /// @param ms_start Beginning of range to repaint
/// @param sample_end Last sample to repaint /// @param ms_end End of range to repaint
void Redraw(int64_t sample_start, int64_t sample_end); 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 dc DC to paint to
/// @param updsamples Sample range to repaint /// @param updtime Time range to repaint
/// @param updrect Pixel 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 dc DC to paint to
/// @param updsamples Sample range to repaint /// @param updtime Time range to repaint
void PaintMarkers(wxDC &dc, SampleRange updsamples); void PaintMarkers(wxDC &dc, TimeRange updtime);
/// Draw a single foot for a marker /// Draw a single foot for a marker
/// @param dc DC to paint to /// @param dc DC to paint to
@ -201,10 +198,10 @@ class AudioDisplay: public wxWindow {
/// @param dir -1 for left, 1 for right /// @param dir -1 for left, 1 for right
void PaintFoot(wxDC &dc, int marker_x, int dir); 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 dc DC to paint to
/// @param updsamples Sample range to repaint /// @param updtime Time range to repaint
void PaintLabels(wxDC &dc, SampleRange updsamples); void PaintLabels(wxDC &dc, TimeRange updtime);
/// Paint the track cursor /// Paint the track cursor
/// @param dc DC to paint to /// @param dc DC to paint to
@ -223,7 +220,7 @@ class AudioDisplay: public wxWindow {
// AudioControllerAudioEventListener implementation // AudioControllerAudioEventListener implementation
void OnAudioOpen(AudioProvider *provider); void OnAudioOpen(AudioProvider *provider);
void OnPlaybackPosition(int64_t sample_position); void OnPlaybackPosition(int ms_position);
void OnSelectionChanged(); void OnSelectionChanged();
void OnStyleRangesChanged(); void OnStyleRangesChanged();
void OnMarkerMoved(); void OnMarkerMoved();
@ -248,46 +245,49 @@ public:
/// @brief Scroll the audio display /// @brief Scroll the audio display
/// @param pixel_position Absolute pixel to put in center of 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 /// @brief Scroll the audio display
/// @param sample_position Audio sample to put at left edge of the audio display /// @param ms Time in milliseconds to put at left edge of the audio display
void ScrollSampleToLeft(int64_t sample_position); void ScrollTimeToLeft(int ms) { ScrollPixelToLeft(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display /// @brief Scroll the audio display
/// @param sample_position Audio sample to put in center of the audio display /// @param ms Time in milliseconds to put in center of the audio display
void ScrollSampleToCenter(int64_t sample_position); void ScrollTimeToCenter(int ms) { ScrollPixelToCenter(AbsoluteXFromTime(ms)); }
/// @brief Scroll the audio display /// @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 /// If the entire range is already visible inside the display, nothing is
/// just one of the two endpoints is visible, the display is scrolled such that the /// scrolled. If just one of the two endpoints is visible, the display is
/// visible endpoint stays in view but more of the rest of the range becomes visible. /// 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. /// If the entire range fits inside the display, the display is centered
/// For this calculation, the display is considered smaller by some margins, see below. /// 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 /// If the range does not fit within the display with margins subtracted,
/// the range is ensured visible and as much of the rest of the range is brought into /// the start of the range is ensured visible and as much of the rest of
/// view. /// the range is brought into view.
/// ///
/// For the purpose of this function, a 5 percent margin is assumed at each end of the /// For the purpose of this function, a 5 percent margin is assumed at each
/// audio display such that a range endpoint that is ensured to be in view never gets /// end of the audio display such that a range endpoint that is ensured to
/// closer to the edge of the display than the margin. The edge that is not ensured to /// be in view never gets closer to the edge of the display than the
/// be in view might be outside of view or might be closer to the display edge 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. /// margin.
void ScrollSampleRangeInView(const SampleRange &range); void ScrollTimeRangeInView(const TimeRange &range);
/// @brief Change the zoom level /// @brief Change the zoom level
/// @param new_zoom_level The new zoom level to use /// @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. /// A zoom level of 0 is the default zoom level, all other levels are based
/// Negative zoom levels zoom out, positive zoom in. /// 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 /// The zoom levels generally go from +30 to -30. It is possible to zoom in
/// +30 /// more than +30.
void SetZoomLevel(int new_zoom_level); void SetZoomLevel(int new_zoom_level);
/// @brief Get the zoom level /// @brief Get the zoom level
@ -320,21 +320,15 @@ public:
/// @param scale New amplitude scale factor, 1.0 is no scaling /// @param scale New amplitude scale factor, 1.0 is no scaling
void SetAmplitudeScale(float scale); void SetAmplitudeScale(float scale);
/// @brief Get amplitude scale factor /// Get a time in milliseconds from an X coordinate relative to current scroll
/// @return The amplitude scaling factor int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
float GetAmplitudeScale() const; /// 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
/// @brief Get a sample index from an X coordinate relative to current scroll int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
int64_t SamplesFromRelativeX(int x) const { return (scroll_left + x) * pixel_samples; } /// Get an absolute X coordinate from a time in milliseconds
/// @brief Get a sample index from an absolute X coordinate int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
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; }
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View file

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

View file

@ -40,8 +40,6 @@ namespace agi {
/// Marker provider for video keyframes /// Marker provider for video keyframes
class AudioMarkerProviderKeyframes : public AudioMarkerProvider { class AudioMarkerProviderKeyframes : public AudioMarkerProvider {
/// Audio controller for time -> sample conversions
AudioController *controller;
/// Video controller to get keyframes from /// Video controller to get keyframes from
VideoContext *vc; VideoContext *vc;
@ -71,5 +69,5 @@ public:
/// Get all keyframe markers within a range /// Get all keyframe markers within a range
/// @param range Range of samples to get markers for /// @param range Range of samples to get markers for
/// @param[out] out Vector to fill with markers in the range /// @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() AudioRenderer::AudioRenderer()
: pixel_samples(0) : pixel_ms(0)
, pixel_height(0) , pixel_height(0)
, amplitude_scale(0) , amplitude_scale(0)
, cache_bitmap_width(32) // arbitrary value for now , cache_bitmap_width(32) // arbitrary value for now
@ -91,7 +91,7 @@ AudioRenderer::AudioRenderer()
bitmaps.resize(AudioStyle_MAX, AudioRendererBitmapCache(256, AudioRendererBitmapCacheBitmapFactory(this))); bitmaps.resize(AudioStyle_MAX, AudioRendererBitmapCache(256, AudioRendererBitmapCacheBitmapFactory(this)));
// Make sure there's *some* values for those fields, and in the caches // Make sure there's *some* values for those fields, and in the caches
SetSamplesPerPixel(1); SetMillisecondsPerPixel(1);
SetHeight(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) if (renderer)
renderer->SetSamplesPerPixel(pixel_samples); renderer->SetMillisecondsPerPixel(pixel_ms);
ResetBlockCount(); ResetBlockCount();
} }
@ -147,7 +147,7 @@ void AudioRenderer::SetRenderer(AudioRendererBitmapProvider *_renderer)
{ {
renderer->SetProvider(provider); renderer->SetProvider(provider);
renderer->SetAmplitudeScale(amplitude_scale); renderer->SetAmplitudeScale(amplitude_scale);
renderer->SetSamplesPerPixel(pixel_samples); renderer->SetMillisecondsPerPixel(pixel_ms);
} }
} }
@ -181,7 +181,8 @@ void AudioRenderer::ResetBlockCount()
{ {
if (provider) 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; cache_numblocks = rendered_width / cache_bitmap_width;
for_each(bitmaps, bind(&AudioRendererBitmapCache::SetBlockCount, _1, cache_numblocks)); 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 { class AudioRenderer {
friend struct AudioRendererBitmapCacheBitmapFactory; friend struct AudioRendererBitmapCacheBitmapFactory;
/// Horizontal zoom level, samples per pixel /// Horizontal zoom level, milliseconds per pixel
int pixel_samples; double pixel_ms;
/// Rendering height in pixels /// Rendering height in pixels
int pixel_height; int pixel_height;
/// Vertical zoom level/amplitude scale /// Vertical zoom level/amplitude scale
@ -152,10 +152,10 @@ public:
~AudioRenderer(); ~AudioRenderer();
/// @brief Set horizontal zoom /// @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. /// Changing the zoom level invalidates all cached bitmaps.
void SetSamplesPerPixel(int pixel_samples); void SetMillisecondsPerPixel(double pixel_ms);
/// @brief Set rendering height /// @brief Set rendering height
/// @param pixel_height Height in pixels to render at /// @param pixel_height Height in pixels to render at
@ -184,18 +184,6 @@ public:
/// Changing the max cache size does not trigger cache aging. /// Changing the max cache size does not trigger cache aging.
void SetCacheMaxSize(size_t max_size); 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 /// @brief Change renderer
/// @param renderer New renderer to use /// @param renderer New renderer to use
/// ///
@ -257,8 +245,8 @@ class AudioRendererBitmapProvider {
protected: protected:
/// Audio provider to use for rendering /// Audio provider to use for rendering
AudioProvider *provider; AudioProvider *provider;
/// Horizontal zoom in samples per pixel /// Horizontal zoom in milliseconds per pixel
int pixel_samples; double pixel_ms;
/// Vertical zoom/amplitude scale factor /// Vertical zoom/amplitude scale factor
float amplitude_scale; float amplitude_scale;
@ -270,7 +258,7 @@ protected:
/// @brief Called when horizontal zoom changes /// @brief Called when horizontal zoom changes
/// ///
/// Implementations can override this method to do something when the horizontal zoom is changed /// 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 /// @brief Called when vertical zoom changes
/// ///
@ -279,7 +267,7 @@ protected:
public: public:
/// @brief Constructor /// @brief Constructor
AudioRendererBitmapProvider() : provider(0), pixel_samples(0), amplitude_scale(0) { }; AudioRendererBitmapProvider() : provider(0), pixel_ms(0), amplitude_scale(0) { };
/// @brief Destructor /// @brief Destructor
virtual ~AudioRendererBitmapProvider() { } virtual ~AudioRendererBitmapProvider() { }
@ -307,8 +295,8 @@ public:
void SetProvider(AudioProvider *provider); void SetProvider(AudioProvider *provider);
/// @brief Change horizontal zoom /// @brief Change horizontal zoom
/// @param pixel_samples Samples per pixel to zoom to /// @param pixel_ms Milliseconds per pixel to zoom to
void SetSamplesPerPixel(int pixel_samples); void SetMillisecondsPerPixel(double new_pixel_ms);
/// @brief Change vertical zoom /// @brief Change vertical zoom
/// @param amplitude_scale Scaling factor to zoom to /// @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) for (int ax = start; ax < end; ++ax)
{ {
// Derived audio data // 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); float *power = cache->Get(block_index);
// Prepare bitmap writing // Prepare bitmap writing

View file

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

View file

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

View file

@ -58,8 +58,8 @@ protected:
~AudioRenderingStyleRanges() { } ~AudioRenderingStyleRanges() { }
public: public:
/// @brief Add a range to the line /// @brief Add a range to the line
/// @param start First sample index in range /// @param start First milisecond in range
/// @param end One past last sample index in range /// @param end One past last milisecond in range
/// @param style Style of the range added /// @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 /// @return The warning message to show, may be empty if there is none
virtual wxString GetWarningMessage() const = 0; virtual wxString GetWarningMessage() const = 0;
/// @brief Get the sample range the user is most likely to want to see for the current state /// @brief Get the time range the user is most likely to want to see for the current state
/// @return A sample range /// @return A time range
/// ///
/// This is used for "bring working area into view" operations. /// 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 /// @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. /// currently.
virtual SampleRange GetPrimaryPlaybackRange() const = 0; virtual TimeRange GetPrimaryPlaybackRange() const = 0;
/// @brief Get all rendering style ranges /// @brief Get all rendering style ranges
/// @param[out] ranges Rendering ranges will be added to this /// @param[out] ranges Rendering ranges will be added to this
@ -106,37 +106,37 @@ public:
virtual void Revert() = 0; virtual void Revert() = 0;
/// @brief Determine if a position is close to a draggable marker /// @brief Determine if a position is close to a draggable marker
/// @param sample The audio sample index to test /// @param ms The time in milliseconds to test
/// @param sensitivity Distance in samples to consider markers as nearby /// @param sensitivity Distance in milliseconds to consider markers as nearby
/// @return True if a marker is close by the given sample, as defined by sensitivity /// @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 /// This is solely for hit-testing against draggable markers, for
/// controlling the mouse cursor. /// 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 /// @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 ms The time in milliseconds the user clicked
/// @param sensitivity Distance in samples to consider existing markers /// @param sensitivity Distance in milliseconds to consider existing markers
/// @param snap_range Maximum snapping range in samples /// @param snap_range Maximum snapping range in milliseconds
/// @return An audio marker or 0. If a marker is returned and the user /// @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 /// starts dragging the mouse after pressing down the button, the returned
/// marker is being dragged. /// 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 /// @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 ms The time in milliseconds the user clicked
/// @param sensitivity Distance in samples to consider existing markers /// @param sensitivity Distance in milliseconds to consider existing markers
/// @param snap_range Maximum snapping range in samples /// @param snap_range Maximum snapping range in milliseconds
/// @return An audio marker or 0. If a marker is returned and the user /// @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 /// starts dragging the mouse after pressing down the button, the returned
/// marker is being dragged. /// 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 /// @brief The user dragged a timing marker
/// @param marker The marker being dragged /// @param marker The marker being dragged
/// @param new_position Sample position the marker was dragged to /// @param new_position Time position the marker was dragged to
/// @param snap_range Maximum snapping range in samples /// @param snap_range Maximum snapping range in milliseconds
virtual void OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t snap_range) = 0; virtual void OnMarkerDrag(AudioMarker *marker, int new_position, int snap_range) = 0;
/// @brief Destructor /// @brief Destructor
virtual ~AudioTimingController() { } virtual ~AudioTimingController() { }

View file

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

View file

@ -43,27 +43,27 @@
/// @class KaraokeMarker /// @class KaraokeMarker
/// @brief AudioMarker implementation for AudioTimingControllerKaraoke /// @brief AudioMarker implementation for AudioTimingControllerKaraoke
class KaraokeMarker : public AudioMarker { class KaraokeMarker : public AudioMarker {
int64_t position; int position;
Pen *pen; Pen *pen;
FeetStyle style; FeetStyle style;
public: public:
int64_t GetPosition() const { return position; } int GetPosition() const { return position; }
wxPen GetStyle() const { return *pen; } wxPen GetStyle() const { return *pen; }
FeetStyle GetFeet() const { return style; } FeetStyle GetFeet() const { return style; }
bool CanSnap() const { return false; } 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) : position(position)
, pen(pen) , pen(pen)
, style(style) , style(style)
{ {
} }
KaraokeMarker(int64_t position) : position(position) { } KaraokeMarker(int position) : position(position) { }
operator int64_t() const { return position; } operator int() const { return position; }
}; };
/// @class AudioTimingControllerKaraoke /// @class AudioTimingControllerKaraoke
@ -111,27 +111,24 @@ class AudioTimingControllerKaraoke : public AudioTimingController {
void OnAutoCommitChange(agi::OptionValue const& opt); void OnAutoCommitChange(agi::OptionValue const& opt);
void OnAutoNextChange(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(); void DoCommit();
public: public:
// AudioTimingController implementation // AudioTimingController implementation
void GetMarkers(const SampleRange &range, AudioMarkerVector &out_markers) const; void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const;
wxString GetWarningMessage() const { return ""; } wxString GetWarningMessage() const { return ""; }
SampleRange GetIdealVisibleSampleRange() const; TimeRange GetIdealVisibleTimeRange() const;
void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const; void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const;
SampleRange GetPrimaryPlaybackRange() const; TimeRange GetPrimaryPlaybackRange() const;
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &out_labels) const; void GetLabels(const TimeRange &range, std::vector<AudioLabel> &out_labels) const;
void Next(); void Next();
void Prev(); void Prev();
void Commit(); void Commit();
void Revert(); void Revert();
bool IsNearbyMarker(int64_t sample, int sensitivity) const; bool IsNearbyMarker(int ms, int sensitivity) const;
AudioMarker * OnLeftClick(int64_t sample, int sensitivity, int64_t); AudioMarker * OnLeftClick(int ms, int sensitivity, int);
AudioMarker * OnRightClick(int64_t, int, int64_t) { return 0; } AudioMarker * OnRightClick(int, int, int) { return 0; }
void OnMarkerDrag(AudioMarker *marker, int64_t new_position, int64_t); void OnMarkerDrag(AudioMarker *marker, int new_position, int);
AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed); 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) , 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") , start_pen("Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness")
, end_pen("Colour/Audio Display/Line boundary End", "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) , start_marker(active_line->Start, &start_pen, AudioMarker::Feet_Right)
, end_marker(ToSamples(active_line->End), &end_pen, AudioMarker::Feet_Left) , end_marker(active_line->End, &end_pen, AudioMarker::Feet_Left)
, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Karaoke Mode") , keyframes_provider(c, "Audio/Display/Draw/Keyframes in Karaoke Mode")
, auto_commit(OPT_GET("Audio/Auto/Commit")->GetBool()) , auto_commit(OPT_GET("Audio/Auto/Commit")->GetBool())
, auto_next(OPT_GET("Audio/Next Line on 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 void AudioTimingControllerKaraoke::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
{ {
SampleRange sr = GetPrimaryPlaybackRange(); TimeRange sr = GetPrimaryPlaybackRange();
ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Selected); ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Selected);
} }
SampleRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const { TimeRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
return SampleRange( return TimeRange(
cur_syl > 0 ? markers[cur_syl - 1] : start_marker, cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
cur_syl < markers.size() ? markers[cur_syl] : end_marker); cur_syl < markers.size() ? markers[cur_syl] : end_marker);
} }
SampleRange AudioTimingControllerKaraoke::GetIdealVisibleSampleRange() const { TimeRange AudioTimingControllerKaraoke::GetIdealVisibleTimeRange() const {
return SampleRange(start_marker, end_marker); 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; size_t i;
for (i = 0; i < markers.size() && markers[i] < range.begin(); ++i) ; for (i = 0; i < markers.size() && markers[i] < range.begin(); ++i) ;
for (; i < markers.size() && markers[i] < range.end(); ++i) for (; i < markers.size() && markers[i] < range.end(); ++i)
@ -256,8 +253,8 @@ void AudioTimingControllerKaraoke::Revert() {
cur_syl = 0; cur_syl = 0;
commit_id = -1; commit_id = -1;
start_marker.Move(ToSamples(active_line->Start)); start_marker.Move(active_line->Start);
end_marker.Move(ToSamples(active_line->End)); end_marker.Move(active_line->End);
markers.clear(); markers.clear();
labels.clear(); labels.clear();
@ -266,10 +263,9 @@ void AudioTimingControllerKaraoke::Revert() {
labels.reserve(kara->size()); labels.reserve(kara->size());
for (AssKaraoke::iterator it = kara->begin(); it != kara->end(); ++it) { for (AssKaraoke::iterator it = kara->begin(); it != kara->end(); ++it) {
int64_t sample = ToSamples(it->start_time);
if (it != kara->begin()) if (it != kara->begin())
markers.push_back(KaraokeMarker(sample, &separator_pen, AudioMarker::Feet_None)); markers.push_back(KaraokeMarker(it->start_time, &separator_pen, AudioMarker::Feet_None));
labels.push_back(AudioLabel(it->text, SampleRange(sample, ToSamples(it->start_time + it->duration)))); labels.push_back(AudioLabel(it->text, TimeRange(it->start_time, it->start_time + it->duration)));
} }
AnnounceUpdatedPrimaryRange(); AnnounceUpdatedPrimaryRange();
@ -277,8 +273,8 @@ void AudioTimingControllerKaraoke::Revert() {
AnnounceMarkerMoved(); AnnounceMarkerMoved();
} }
bool AudioTimingControllerKaraoke::IsNearbyMarker(int64_t sample, int sensitivity) const { bool AudioTimingControllerKaraoke::IsNearbyMarker(int ms, int sensitivity) const {
SampleRange range(sample - sensitivity, sample + sensitivity); TimeRange range(ms - sensitivity, ms + sensitivity);
for (size_t i = 0; i < markers.size(); ++i) for (size_t i = 0; i < markers.size(); ++i)
if (range.contains(markers[i])) if (range.contains(markers[i]))
@ -287,10 +283,10 @@ bool AudioTimingControllerKaraoke::IsNearbyMarker(int64_t sample, int sensitivit
return false; return false;
} }
AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int64_t sample, int sensitivity, int64_t) { AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int ms, int sensitivity, int) {
SampleRange range(sample - sensitivity, sample + sensitivity); 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])) if (syl < markers.size() && range.contains(markers[syl]))
return &markers[syl]; return &markers[syl];
if (syl > 0 && range.contains(markers[syl - 1])) if (syl > 0 && range.contains(markers[syl - 1]))
@ -304,7 +300,7 @@ AudioMarker *AudioTimingControllerKaraoke::OnLeftClick(int64_t sample, int sensi
return 0; 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); KaraokeMarker *marker = static_cast<KaraokeMarker*>(m);
// No rearranging of syllables allowed // No rearranging of syllables allowed
new_position = mid( new_position = mid(
@ -315,7 +311,7 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
marker->Move(new_position); marker->Move(new_position);
size_t syl = marker - &markers.front() + 1; 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) { if (syl == cur_syl || syl == cur_syl + 1) {
AnnounceUpdatedPrimaryRange(); AnnounceUpdatedPrimaryRange();
@ -324,8 +320,8 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
AnnounceMarkerMoved(); AnnounceMarkerMoved();
labels[syl - 1].range = SampleRange(labels[syl - 1].range.begin(), new_position); labels[syl - 1].range = TimeRange(labels[syl - 1].range.begin(), new_position);
labels[syl].range = SampleRange(new_position, labels[syl].range.end()); labels[syl].range = TimeRange(new_position, labels[syl].range.end());
AnnounceLabelChanged(); AnnounceLabelChanged();
if (auto_commit) if (auto_commit)
@ -334,7 +330,7 @@ void AudioTimingControllerKaraoke::OnMarkerDrag(AudioMarker *m, int64_t new_posi
commit_id = -1; 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) { for (size_t i = 0; i < labels.size(); ++i) {
if (range.overlaps(labels[i].range)) if (range.overlaps(labels[i].range))
out.push_back(labels[i]); 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) { for (Selection::iterator it = sel.begin(); it != sel.end(); ++it) {
c->audioController->SaveClip( c->audioController->SaveClip(
wxFileSelector(_("Save audio clip"), "", "", "wav", "", wxFD_SAVE|wxFD_OVERWRITE_PROMPT, c->parent), wxFileSelector(_("Save audio clip"), "", "", "wav", "", wxFD_SAVE|wxFD_OVERWRITE_PROMPT, c->parent),
SampleRange(c->audioController->SamplesFromMilliseconds((*it)->Start), TimeRange((*it)->Start, (*it)->End));
c->audioController->SamplesFromMilliseconds((*it)->End)));
} }
} }
}; };
@ -268,10 +267,8 @@ struct audio_play_before : public validate_audio_open {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->videoController->Stop(); c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange()); int begin = c->audioController->GetPrimaryPlaybackRange().begin();
c->audioController->PlayRange(SampleRange( c->audioController->PlayRange(TimeRange(begin - 500, begin));
times.begin() - c->audioController->SamplesFromMilliseconds(500),
times.begin()));
} }
}; };
@ -284,10 +281,8 @@ struct audio_play_after : public validate_audio_open {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->videoController->Stop(); c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange()); int end = c->audioController->GetPrimaryPlaybackRange().end();
c->audioController->PlayRange(SampleRange( c->audioController->PlayRange(TimeRange(end, end + 500));
times.end(),
times.end() + c->audioController->SamplesFromMilliseconds(500)));
} }
}; };
@ -300,11 +295,8 @@ struct audio_play_end : public validate_audio_open {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->videoController->Stop(); c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange()); TimeRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayToEndOfPrimary( c->audioController->PlayToEndOfPrimary(times.end() - std::min(500, times.length()));
times.end() - std::min(
c->audioController->SamplesFromMilliseconds(500),
times.length()));
} }
}; };
@ -317,12 +309,10 @@ struct audio_play_begin : public validate_audio_open {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->videoController->Stop(); c->videoController->Stop();
SampleRange times(c->audioController->GetPrimaryPlaybackRange()); TimeRange times(c->audioController->GetPrimaryPlaybackRange());
c->audioController->PlayRange(SampleRange( c->audioController->PlayRange(TimeRange(
times.begin(), times.begin(),
times.begin() + std::min( times.begin() + std::min(500, times.length())));
c->audioController->SamplesFromMilliseconds(500),
times.length())));
} }
}; };

View file

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