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:
parent
bba825ed0d
commit
01b92aa4e3
18 changed files with 413 additions and 514 deletions
|
@ -245,5 +245,5 @@ void AudioBox::ScrollAudioBy(int pixel_amount) {
|
|||
|
||||
void AudioBox::ScrollToActiveLine() {
|
||||
if (controller->GetTimingController())
|
||||
audioDisplay->ScrollSampleRangeInView(controller->GetTimingController()->GetIdealVisibleSampleRange());
|
||||
audioDisplay->ScrollTimeRangeInView(controller->GetTimingController()->GetIdealVisibleTimeRange());
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
/// @ingroup audio_ui
|
||||
///
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
|
@ -61,7 +60,7 @@
|
|||
|
||||
class VideoPositionMarker : public AudioMarker {
|
||||
Pen style;
|
||||
int64_t position;
|
||||
int position;
|
||||
|
||||
public:
|
||||
VideoPositionMarker()
|
||||
|
@ -70,20 +69,16 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void SetPosition(int64_t new_pos)
|
||||
{
|
||||
position = new_pos;
|
||||
}
|
||||
void SetPosition(int new_pos) { position = new_pos; }
|
||||
|
||||
int64_t GetPosition() const { return position; }
|
||||
int GetPosition() const { return position; }
|
||||
FeetStyle GetFeet() const { return Feet_None; }
|
||||
bool CanSnap() const { return true; }
|
||||
wxPen GetStyle() const { return style; }
|
||||
operator int64_t() const { return position; }
|
||||
operator int() const { return position; }
|
||||
};
|
||||
|
||||
class VideoPositionMarkerProvider : public AudioMarkerProvider {
|
||||
AudioController *ac;
|
||||
VideoContext *vc;
|
||||
|
||||
VideoPositionMarker marker;
|
||||
|
@ -99,7 +94,7 @@ class VideoPositionMarkerProvider : public AudioMarkerProvider {
|
|||
}
|
||||
else
|
||||
{
|
||||
marker.SetPosition(ac->SamplesFromMilliseconds(vc->TimeAtFrame(frame_number)));
|
||||
marker.SetPosition(vc->TimeAtFrame(frame_number));
|
||||
}
|
||||
AnnounceMarkerMoved();
|
||||
}
|
||||
|
@ -120,15 +115,14 @@ class VideoPositionMarkerProvider : public AudioMarkerProvider {
|
|||
|
||||
public:
|
||||
VideoPositionMarkerProvider(agi::Context *c)
|
||||
: ac(c->audioController)
|
||||
, vc(c->videoController)
|
||||
: vc(c->videoController)
|
||||
, video_seek_slot(vc->AddSeekListener(&VideoPositionMarkerProvider::Update, this))
|
||||
, enable_opt_changed_slot(OPT_SUB("Audio/Display/Draw/Video Position", &VideoPositionMarkerProvider::OptChanged, this))
|
||||
{
|
||||
OptChanged(*OPT_GET("Audio/Display/Draw/Video Position"));
|
||||
}
|
||||
|
||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const
|
||||
void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const
|
||||
{
|
||||
if (range.contains(marker))
|
||||
{
|
||||
|
@ -177,7 +171,7 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
|
|||
}
|
||||
else
|
||||
{
|
||||
AnnouncePlaybackPosition(pos);
|
||||
AnnouncePlaybackPosition(MillisecondsFromSamples(pos));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,11 +403,11 @@ void AudioController::OnSubtitlesSave()
|
|||
}
|
||||
}
|
||||
|
||||
void AudioController::PlayRange(const SampleRange &range)
|
||||
void AudioController::PlayRange(const TimeRange &range)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
player->Play(range.begin(), range.length());
|
||||
player->Play(SamplesFromMilliseconds(range.begin()), SamplesFromMilliseconds(range.length()));
|
||||
playback_mode = PM_Range;
|
||||
playback_timer.Start(20);
|
||||
|
||||
|
@ -428,26 +422,25 @@ void AudioController::PlayPrimaryRange()
|
|||
playback_mode = PM_PrimaryRange;
|
||||
}
|
||||
|
||||
void AudioController::PlayToEndOfPrimary(int64_t start_sample)
|
||||
void AudioController::PlayToEndOfPrimary(int start_ms)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
player->Play(start_sample, GetPrimaryPlaybackRange().end() - start_sample);
|
||||
playback_mode = PM_PrimaryRange;
|
||||
playback_timer.Start(20);
|
||||
|
||||
AnnouncePlaybackPosition(start_sample);
|
||||
PlayRange(TimeRange(start_ms, GetPrimaryPlaybackRange().end()));
|
||||
if (playback_mode == PM_Range)
|
||||
playback_mode = PM_PrimaryRange;
|
||||
}
|
||||
|
||||
void AudioController::PlayToEnd(int64_t start_sample)
|
||||
void AudioController::PlayToEnd(int start_ms)
|
||||
{
|
||||
if (!IsAudioOpen()) return;
|
||||
|
||||
int64_t start_sample = SamplesFromMilliseconds(start_ms);
|
||||
player->Play(start_sample, provider->GetNumSamples()-start_sample);
|
||||
playback_mode = PM_ToEnd;
|
||||
playback_timer.Start(20);
|
||||
|
||||
AnnouncePlaybackPosition(start_sample);
|
||||
AnnouncePlaybackPosition(start_ms);
|
||||
}
|
||||
|
||||
|
||||
|
@ -469,23 +462,30 @@ bool AudioController::IsPlaying()
|
|||
}
|
||||
|
||||
|
||||
int64_t AudioController::GetPlaybackPosition()
|
||||
int AudioController::GetPlaybackPosition()
|
||||
{
|
||||
if (!IsPlaying()) return 0;
|
||||
|
||||
return player->GetCurrentPosition();
|
||||
return MillisecondsFromSamples(player->GetCurrentPosition());
|
||||
}
|
||||
|
||||
int AudioController::GetDuration() const
|
||||
{
|
||||
if (!provider) return 0;
|
||||
|
||||
return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::ResyncPlaybackPosition(int64_t new_position)
|
||||
void AudioController::ResyncPlaybackPosition(int new_position)
|
||||
{
|
||||
if (!IsPlaying()) return;
|
||||
|
||||
player->SetCurrentPosition(new_position);
|
||||
player->SetCurrentPosition(SamplesFromMilliseconds(new_position));
|
||||
}
|
||||
|
||||
|
||||
SampleRange AudioController::GetPrimaryPlaybackRange() const
|
||||
TimeRange AudioController::GetPrimaryPlaybackRange() const
|
||||
{
|
||||
if (timing_controller)
|
||||
{
|
||||
|
@ -493,19 +493,19 @@ SampleRange AudioController::GetPrimaryPlaybackRange() const
|
|||
}
|
||||
else
|
||||
{
|
||||
return SampleRange(0, 0);
|
||||
return TimeRange(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const
|
||||
void AudioController::GetMarkers(const TimeRange &range, AudioMarkerVector &markers) const
|
||||
{
|
||||
/// @todo Find all sources of markers
|
||||
if (timing_controller) timing_controller->GetMarkers(range, markers);
|
||||
if (video_position_marker_provider.get()) video_position_marker_provider->GetMarkers(range, markers);
|
||||
}
|
||||
|
||||
void AudioController::GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const
|
||||
void AudioController::GetLabels(const TimeRange &range, std::vector<AudioLabel> &labels) const
|
||||
{
|
||||
if (timing_controller) timing_controller->GetLabels(range, labels);
|
||||
}
|
||||
|
@ -551,15 +551,17 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
|
|||
return millisamples / sr;
|
||||
}
|
||||
|
||||
void AudioController::SaveClip(wxString const& filename, SampleRange const& range) const
|
||||
void AudioController::SaveClip(wxString const& filename, TimeRange const& range) const
|
||||
{
|
||||
if (filename.empty() || range.begin() > provider->GetNumSamples() || range.length() == 0) return;
|
||||
int64_t start_sample = SamplesFromMilliseconds(range.begin());
|
||||
int64_t end_sample = SamplesFromMilliseconds(range.end());
|
||||
if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return;
|
||||
|
||||
agi::io::Save outfile(STD_STR(filename), true);
|
||||
std::ofstream& out(outfile.Get());
|
||||
|
||||
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
|
||||
size_t bufsize = range.length() * bytes_per_sample;
|
||||
size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
|
||||
|
||||
int intval;
|
||||
short shortval;
|
||||
|
@ -580,8 +582,8 @@ void AudioController::SaveClip(wxString const& filename, SampleRange const& rang
|
|||
//samples per read
|
||||
size_t spr = 65536 / bytes_per_sample;
|
||||
std::vector<char> buf(bufsize);
|
||||
for(int64_t i = range.begin(); i < range.end(); i += spr) {
|
||||
size_t len = std::min<size_t>(spr, range.end() - i);
|
||||
for(int64_t i = start_sample; i < end_sample; i += spr) {
|
||||
size_t len = std::min<size_t>(spr, end_sample - i);
|
||||
provider->GetAudio(&buf[0], i, len);
|
||||
out.write(&buf[0], len * bytes_per_sample);
|
||||
}
|
||||
|
|
|
@ -65,47 +65,44 @@ class AudioMarkerProvider;
|
|||
|
||||
typedef std::vector<const AudioMarker*> AudioMarkerVector;
|
||||
|
||||
|
||||
/// @class SampleRange
|
||||
/// @brief Represents an immutable range of audio samples
|
||||
class SampleRange {
|
||||
int64_t _begin;
|
||||
int64_t _end;
|
||||
/// @class TimeRange
|
||||
/// @brief Represents an immutable range of time
|
||||
class TimeRange {
|
||||
int _begin;
|
||||
int _end;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
/// @param begin Index of the first sample to include in the range
|
||||
/// @param end Index of one past the last sample to include in the range
|
||||
SampleRange(int64_t begin, int64_t end)
|
||||
: _begin(begin)
|
||||
, _end(end)
|
||||
/// @param begin Index of the first millisecond to include in the range
|
||||
/// @param end Index of one past the last millisecond to include in the range
|
||||
TimeRange(int begin, int end) : _begin(begin), _end(end)
|
||||
{
|
||||
assert(end >= begin);
|
||||
}
|
||||
|
||||
/// @brief Copy constructor, optionally adjusting the range
|
||||
/// @param src The range to duplicate
|
||||
/// @param begin_adjust Number of samples to add to the start of the range
|
||||
/// @param end_adjust Number of samples to add to the end of the range
|
||||
SampleRange(const SampleRange &src, int64_t begin_adjust = 0, int64_t end_adjust = 0)
|
||||
/// @param begin_adjust Number of milliseconds to add to the start of the range
|
||||
/// @param end_adjust Number of milliseconds to add to the end of the range
|
||||
TimeRange(const TimeRange &src, int begin_adjust = 0, int end_adjust = 0)
|
||||
{
|
||||
_begin = src._begin + begin_adjust;
|
||||
_end = src._end + end_adjust;
|
||||
assert(_end >= _begin);
|
||||
}
|
||||
|
||||
/// Get the number of samples in the range
|
||||
int64_t length() const { return _end - _begin; }
|
||||
/// Get the index of the first sample in the range
|
||||
int64_t begin() const { return _begin; }
|
||||
/// Get the index of one past the last sample in the range
|
||||
int64_t end() const { return _end; }
|
||||
/// Get the length of the range in milliseconds
|
||||
int length() const { return _end - _begin; }
|
||||
/// Get the start time of the range in milliseconds
|
||||
int begin() const { return _begin; }
|
||||
/// Get the exclusive end time of the range in milliseconds
|
||||
int end() const { return _end; }
|
||||
|
||||
/// Determine whether the range contains a given sample index
|
||||
bool contains(int64_t sample) const { return sample >= begin() && sample < end(); }
|
||||
/// Determine whether the range contains a given time in milliseconds
|
||||
bool contains(int ms) const { return ms >= begin() && ms < end(); }
|
||||
|
||||
/// Determine whether there is an overlap between two ranges
|
||||
bool overlaps(const SampleRange &other) const
|
||||
bool overlaps(const TimeRange &other) const
|
||||
{
|
||||
return other.contains(_begin) || contains(other._begin);
|
||||
}
|
||||
|
@ -121,8 +118,8 @@ public:
|
|||
/// Virtual destructor, does nothing
|
||||
virtual ~AudioMarkerProvider() { }
|
||||
|
||||
/// @brief Return markers in a sample range
|
||||
virtual void GetMarkers(const SampleRange &range, AudioMarkerVector &out) const = 0;
|
||||
/// @brief Return markers in a time range
|
||||
virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
||||
};
|
||||
|
@ -134,22 +131,22 @@ protected:
|
|||
/// One or more of the labels provided by this object have changed
|
||||
agi::signal::Signal<> AnnounceLabelChanged;
|
||||
public:
|
||||
/// A label for a range of samples on the audio display
|
||||
/// A label for a range of time on the audio display
|
||||
struct AudioLabel {
|
||||
/// Text of the label
|
||||
wxString text;
|
||||
/// Range which this label applies to
|
||||
SampleRange range;
|
||||
AudioLabel(wxString const& text, SampleRange const& range) : text(text), range(range) { }
|
||||
TimeRange range;
|
||||
AudioLabel(wxString const& text, TimeRange const& range) : text(text), range(range) { }
|
||||
};
|
||||
|
||||
/// Virtual destructor, does nothing
|
||||
virtual ~AudioLabelProvider() { }
|
||||
|
||||
/// @brief Get labels in a sample range
|
||||
/// @param range Range of samples to get labels for
|
||||
/// @brief Get labels in a time range
|
||||
/// @param range Range of times to get labels for
|
||||
/// @param[out] out Vector which should be filled with the labels
|
||||
virtual void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const = 0;
|
||||
virtual void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const = 0;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
|
||||
};
|
||||
|
@ -190,7 +187,7 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
|
|||
agi::signal::Signal<> AnnounceAudioClose;
|
||||
|
||||
/// Playback is in progress and the current position was updated
|
||||
agi::signal::Signal<int64_t> AnnouncePlaybackPosition;
|
||||
agi::signal::Signal<int> AnnouncePlaybackPosition;
|
||||
|
||||
/// Playback has stopped
|
||||
agi::signal::Signal<> AnnouncePlaybackStop;
|
||||
|
@ -258,8 +255,17 @@ class AudioController : public wxEvtHandler, public AudioMarkerProvider, public
|
|||
void OnComputerResuming(wxPowerEvent &event);
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// @brief Convert a count of audio samples to a time in milliseconds
|
||||
/// @param samples Sample count to convert
|
||||
/// @return The number of milliseconds equivalent to the sample-count, rounded down
|
||||
int64_t MillisecondsFromSamples(int64_t samples) const;
|
||||
|
||||
/// @brief Convert a time in milliseconds to a count of audio samples
|
||||
/// @param ms Time in milliseconds to convert
|
||||
/// @return The index of the first sample that is wholly inside the millisecond
|
||||
int64_t SamplesFromMilliseconds(int64_t ms) const;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
AudioController(agi::Context *context);
|
||||
|
||||
|
@ -294,7 +300,7 @@ public:
|
|||
///
|
||||
/// The end of the played back range may be requested changed, but is not
|
||||
/// changed automatically from any other operations.
|
||||
void PlayRange(const SampleRange &range);
|
||||
void PlayRange(const TimeRange &range);
|
||||
|
||||
/// @brief Start or restart audio playback, playing the primary playback range
|
||||
///
|
||||
|
@ -304,19 +310,19 @@ public:
|
|||
void PlayPrimaryRange();
|
||||
|
||||
/// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range
|
||||
/// @param start_sample Index of the sample to start playback at
|
||||
/// @param start_ms Time in milliseconds to start playback at
|
||||
///
|
||||
/// This behaves like PlayPrimaryRange, but the start point can differ from
|
||||
/// the beginning of the primary range.
|
||||
void PlayToEndOfPrimary(int64_t start_sample);
|
||||
void PlayToEndOfPrimary(int start_ms);
|
||||
|
||||
/// @brief Start or restart audio playback, playing from a point to the end of stream
|
||||
/// @param start_sample Index of the sample to start playback at
|
||||
/// @param start_ms Time in milliseconds to start playback at
|
||||
///
|
||||
/// Playback to end cannot be converted to a range playback like range
|
||||
/// playback can, it will continue until the end is reached, it is stopped,
|
||||
/// or restarted.
|
||||
void PlayToEnd(int64_t start_sample);
|
||||
void PlayToEnd(int start_ms);
|
||||
|
||||
/// @brief Stop all audio playback
|
||||
void Stop();
|
||||
|
@ -326,35 +332,39 @@ public:
|
|||
bool IsPlaying();
|
||||
|
||||
/// @brief Get the current playback position
|
||||
/// @return Approximate current sample index being heard by the user
|
||||
/// @return Approximate current time in milliseconds being heard by the user
|
||||
///
|
||||
/// Returns 0 if playback is stopped. The return value is only approximate.
|
||||
int64_t GetPlaybackPosition();
|
||||
int GetPlaybackPosition();
|
||||
|
||||
/// Get the duration of the currently open audio in milliseconds, or 0 if none
|
||||
/// @return Duration in milliseconds
|
||||
int GetDuration() const;
|
||||
|
||||
/// @brief If playing, restart playback from the specified position
|
||||
/// @param new_position Sample index to restart playback from
|
||||
/// @param new_position Time to restart playback from
|
||||
///
|
||||
/// This function can be used to re-synchronise audio playback to another
|
||||
/// source that might not be able to keep up with the full speed, such as
|
||||
/// video playback in high resolution or with complex subtitles.
|
||||
///
|
||||
/// This function only does something if audio is already playing.
|
||||
void ResyncPlaybackPosition(int64_t new_position);
|
||||
void ResyncPlaybackPosition(int new_position);
|
||||
|
||||
|
||||
/// @brief Get the primary playback range
|
||||
/// @return An immutable SampleRange object
|
||||
SampleRange GetPrimaryPlaybackRange() const;
|
||||
/// @return An immutable TimeRange object
|
||||
TimeRange GetPrimaryPlaybackRange() const;
|
||||
|
||||
/// @brief Get all markers inside a range
|
||||
/// @param range The sample range to retrieve markers for
|
||||
/// @param range The time range to retrieve markers for
|
||||
/// @param markers Vector to fill found markers into
|
||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
|
||||
void GetMarkers(const TimeRange &range, AudioMarkerVector &markers) const;
|
||||
|
||||
/// @brief Get all labels inside a range
|
||||
/// @param range The sample range to retrieve labels for
|
||||
/// @param range The time range to retrieve labels for
|
||||
/// @param labels Vector to fill found labels into
|
||||
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const;
|
||||
void GetLabels(const TimeRange &range, std::vector<AudioLabel> &labels) const;
|
||||
|
||||
|
||||
/// @brief Get the playback audio volume
|
||||
|
@ -376,20 +386,10 @@ public:
|
|||
/// dialogue line.
|
||||
void SetTimingController(AudioTimingController *new_controller);
|
||||
|
||||
/// @brief Convert a count of audio samples to a time in milliseconds
|
||||
/// @param samples Sample count to convert
|
||||
/// @return The number of milliseconds equivalent to the sample-count, rounded down
|
||||
int64_t MillisecondsFromSamples(int64_t samples) const;
|
||||
|
||||
/// @brief Convert a time in milliseconds to a count of audio samples
|
||||
/// @param ms Time in milliseconds to convert
|
||||
/// @return The index of the first sample that is wholly inside the millisecond
|
||||
int64_t SamplesFromMilliseconds(int64_t ms) const;
|
||||
|
||||
/// @brief Save a portion of the decoded loaded audio to a wav file
|
||||
/// @param filename File to save to
|
||||
/// @param range Range of samples to save
|
||||
void SaveClip(wxString const& filename, SampleRange const& range) const;
|
||||
/// @param range Time range to save
|
||||
void SaveClip(wxString const& filename, TimeRange const& range) const;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
|
||||
|
@ -414,8 +414,8 @@ public:
|
|||
};
|
||||
|
||||
/// @brief Get the marker's position
|
||||
/// @return The marker's position in samples
|
||||
virtual int64_t GetPosition() const = 0;
|
||||
/// @return The marker's position in milliseconds
|
||||
virtual int GetPosition() const = 0;
|
||||
|
||||
/// @brief Get the marker's drawing style
|
||||
/// @return A pen object describing the marker's drawing style
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
#include "audio_timing.h"
|
||||
#include "block_cache.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "include/aegisub/hotkey.h"
|
||||
#include "main.h"
|
||||
|
@ -250,10 +249,9 @@ const int AudioDisplayScrollbar::min_width;
|
|||
|
||||
|
||||
class AudioDisplayTimeline : public AudioDisplayInteractionObject {
|
||||
int64_t num_samples;
|
||||
int samplerate;
|
||||
int samples_per_pixel;
|
||||
int pixel_left;
|
||||
int duration; ///< Total duration in ms
|
||||
double ms_per_pixel; ///< Milliseconds per pixel
|
||||
int pixel_left; ///< Leftmost visible pixel (i.e. scroll position)
|
||||
|
||||
wxRect bounds;
|
||||
|
||||
|
@ -276,27 +274,21 @@ class AudioDisplayTimeline : public AudioDisplayInteractionObject {
|
|||
int scale_major_modulo; ///< If minor_scale_mark_index % scale_major_modulo == 0 the mark is a major mark
|
||||
double scale_minor_divisor; ///< Absolute scale-mark index multiplied by this number gives sample index for scale mark
|
||||
|
||||
AudioDisplay *display;
|
||||
AudioDisplay *display; ///< Containing audio display
|
||||
|
||||
UIColours colours; ///< Colour provider
|
||||
|
||||
public:
|
||||
|
||||
AudioDisplayTimeline(AudioDisplay *display)
|
||||
: num_samples(0)
|
||||
, samplerate(44100)
|
||||
, samples_per_pixel(1)
|
||||
: duration(0)
|
||||
, ms_per_pixel(1.0)
|
||||
, pixel_left(0)
|
||||
, dragging(false)
|
||||
, display(display)
|
||||
{
|
||||
}
|
||||
|
||||
int GetHeight() const
|
||||
{
|
||||
int width, height;
|
||||
display->GetTextExtent("0123456789:.", &width, &height);
|
||||
return height + 4;
|
||||
bounds.height = height + 4;
|
||||
}
|
||||
|
||||
void SetColourScheme(std::string const& name)
|
||||
|
@ -308,57 +300,55 @@ public:
|
|||
{
|
||||
// The size is without anything that goes below the timeline (like scrollbar)
|
||||
bounds.width = display_size.x;
|
||||
bounds.height = GetHeight();
|
||||
bounds.x = 0;
|
||||
bounds.y = 0;
|
||||
}
|
||||
|
||||
int GetHeight() const { return bounds.height; }
|
||||
const wxRect & GetBounds() const { return bounds; }
|
||||
|
||||
void ChangeAudio(int64_t new_length, int new_samplerate)
|
||||
void ChangeAudio(int new_duration)
|
||||
{
|
||||
num_samples = new_length;
|
||||
samplerate = new_samplerate;
|
||||
duration = new_duration;
|
||||
}
|
||||
|
||||
void ChangeZoom(int new_pixel_samples)
|
||||
void ChangeZoom(double new_ms_per_pixel)
|
||||
{
|
||||
samples_per_pixel = new_pixel_samples;
|
||||
ms_per_pixel = new_ms_per_pixel;
|
||||
|
||||
// Pixels per second
|
||||
double px_sec = (double)samplerate / (double)samples_per_pixel;
|
||||
double px_sec = 1000.0 / ms_per_pixel;
|
||||
|
||||
if (px_sec > 3000) {
|
||||
scale_minor = Sc_Millisecond;
|
||||
scale_minor_divisor = (double)samplerate / 1000;
|
||||
scale_minor_divisor = 1.0;
|
||||
scale_major_modulo = 10;
|
||||
} else if (px_sec > 300) {
|
||||
scale_minor = Sc_Centisecond;
|
||||
scale_minor_divisor = (double)samplerate / 100;
|
||||
scale_minor_divisor = 10.0;
|
||||
scale_major_modulo = 10;
|
||||
} else if (px_sec > 30) {
|
||||
scale_minor = Sc_Decisecond;
|
||||
scale_minor_divisor = (double)samplerate / 10;
|
||||
scale_minor_divisor = 100.0;
|
||||
scale_major_modulo = 10;
|
||||
} else if (px_sec > 3) {
|
||||
scale_minor = Sc_Second;
|
||||
scale_minor_divisor = (double)samplerate;
|
||||
scale_minor_divisor = 1000.0;
|
||||
scale_major_modulo = 10;
|
||||
} else if (px_sec > 1.0/3.0) {
|
||||
scale_minor = Sc_Decasecond;
|
||||
scale_minor_divisor = (double)samplerate * 10;
|
||||
scale_minor_divisor = 10000.0;
|
||||
scale_major_modulo = 6;
|
||||
} else if (px_sec > 1.0/9.0) {
|
||||
scale_minor = Sc_Minute;
|
||||
scale_minor_divisor = (double)samplerate * 60;
|
||||
scale_minor_divisor = 60000.0;
|
||||
scale_major_modulo = 10;
|
||||
} else if (px_sec > 1.0/90.0) {
|
||||
scale_minor = Sc_Decaminute;
|
||||
scale_minor_divisor = (double)samplerate * 600;
|
||||
scale_minor_divisor = 600000.0;
|
||||
scale_major_modulo = 6;
|
||||
} else {
|
||||
scale_minor = Sc_Hour;
|
||||
scale_minor_divisor = (double)samplerate * 3600;
|
||||
scale_minor_divisor = 3600000.0;
|
||||
scale_major_modulo = 10;
|
||||
}
|
||||
}
|
||||
|
@ -408,19 +398,19 @@ public:
|
|||
dc.SetTextForeground(colours.Light());
|
||||
|
||||
// Figure out the first scale mark to show
|
||||
int64_t sample_left = pixel_left * samples_per_pixel;
|
||||
int next_scale_mark = (int)(sample_left / scale_minor_divisor);
|
||||
if (next_scale_mark * scale_minor_divisor < sample_left)
|
||||
int ms_left = int(pixel_left * ms_per_pixel);
|
||||
int next_scale_mark = int(ms_left / scale_minor_divisor);
|
||||
if (next_scale_mark * scale_minor_divisor < ms_left)
|
||||
next_scale_mark += 1;
|
||||
assert(next_scale_mark * scale_minor_divisor >= sample_left);
|
||||
assert(next_scale_mark * scale_minor_divisor >= ms_left);
|
||||
|
||||
// Draw scale marks
|
||||
int next_scale_mark_pos;
|
||||
int last_text_right = -1;
|
||||
int last_hour = -1, last_minute = -1;
|
||||
if (num_samples / samplerate < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
|
||||
if (duration < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
|
||||
do {
|
||||
next_scale_mark_pos = (int)(next_scale_mark * scale_minor_divisor / samples_per_pixel) - pixel_left;
|
||||
next_scale_mark_pos = int(next_scale_mark * scale_minor_divisor / ms_per_pixel) - pixel_left;
|
||||
bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
|
||||
|
||||
if (mark_is_major)
|
||||
|
@ -431,7 +421,7 @@ public:
|
|||
// Print time labels on major scale marks
|
||||
if (mark_is_major && next_scale_mark_pos > last_text_right)
|
||||
{
|
||||
double mark_time = next_scale_mark * scale_minor_divisor / samplerate;
|
||||
double mark_time = next_scale_mark * scale_minor_divisor / 1000.0;
|
||||
int mark_hour = (int)(mark_time / 3600);
|
||||
int mark_minute = (int)(mark_time / 60) % 60;
|
||||
double mark_second = mark_time - mark_hour*3600 - mark_minute*60;
|
||||
|
@ -504,8 +494,8 @@ public:
|
|||
{
|
||||
timing_controller->OnMarkerDrag(
|
||||
marker,
|
||||
display->SamplesFromRelativeX(event.GetPosition().x),
|
||||
default_snap != event.ShiftDown() ? display->SamplesFromAbsoluteX(snap_range) : 0);
|
||||
display->TimeFromRelativeX(event.GetPosition().x),
|
||||
default_snap != event.ShiftDown() ? display->TimeFromAbsoluteX(snap_range) : 0);
|
||||
}
|
||||
|
||||
// We lose the marker drag if the button used to initiate it goes up
|
||||
|
@ -514,14 +504,14 @@ public:
|
|||
};
|
||||
|
||||
class AudioStyleRangeMerger : public AudioRenderingStyleRanges {
|
||||
typedef std::map<int64_t, AudioRenderingStyle> style_map;
|
||||
typedef std::map<int, AudioRenderingStyle> style_map;
|
||||
public:
|
||||
typedef style_map::iterator iterator;
|
||||
|
||||
private:
|
||||
style_map points;
|
||||
|
||||
void Split(int64_t point)
|
||||
void Split(int point)
|
||||
{
|
||||
iterator it = points.lower_bound(point);
|
||||
if (it == points.end() || it->first != point)
|
||||
|
@ -531,7 +521,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void Restyle(int64_t start, int64_t end, AudioRenderingStyle style)
|
||||
void Restyle(int start, int end, AudioRenderingStyle style)
|
||||
{
|
||||
assert(points.lower_bound(end) != points.end());
|
||||
for (iterator pt = points.lower_bound(start); pt->first < end; ++pt)
|
||||
|
@ -547,7 +537,7 @@ public:
|
|||
points[0] = AudioStyle_Normal;
|
||||
}
|
||||
|
||||
void AddRange(int64_t start, int64_t end, AudioRenderingStyle style)
|
||||
void AddRange(int start, int end, AudioRenderingStyle style)
|
||||
{
|
||||
|
||||
if (start < 0) start = 0;
|
||||
|
@ -567,7 +557,6 @@ AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::C
|
|||
, audio_open_connection(controller->AddAudioOpenListener(&AudioDisplay::OnAudioOpen, this))
|
||||
, context(context)
|
||||
, audio_renderer(new AudioRenderer)
|
||||
, provider(0)
|
||||
, controller(controller)
|
||||
, scrollbar(new AudioDisplayScrollbar(this))
|
||||
, timeline(new AudioDisplayTimeline(this))
|
||||
|
@ -627,29 +616,11 @@ void AudioDisplay::ScrollPixelToLeft(int pixel_position)
|
|||
}
|
||||
|
||||
|
||||
void AudioDisplay::ScrollPixelToCenter(int pixel_position)
|
||||
{
|
||||
ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2);
|
||||
}
|
||||
|
||||
|
||||
void AudioDisplay::ScrollSampleToLeft(int64_t sample_position)
|
||||
{
|
||||
ScrollPixelToLeft(AbsoluteXFromSamples(sample_position));
|
||||
}
|
||||
|
||||
|
||||
void AudioDisplay::ScrollSampleToCenter(int64_t sample_position)
|
||||
{
|
||||
ScrollPixelToCenter(AbsoluteXFromSamples(sample_position));
|
||||
}
|
||||
|
||||
|
||||
void AudioDisplay::ScrollSampleRangeInView(const SampleRange &range)
|
||||
void AudioDisplay::ScrollTimeRangeInView(const TimeRange &range)
|
||||
{
|
||||
int client_width = GetClientRect().GetWidth();
|
||||
int range_begin = AbsoluteXFromSamples(range.begin());
|
||||
int range_end = AbsoluteXFromSamples(range.end());
|
||||
int range_begin = AbsoluteXFromTime(range.begin());
|
||||
int range_end = AbsoluteXFromTime(range.end());
|
||||
int range_len = range_end - range_begin;
|
||||
|
||||
// Is everything already in view?
|
||||
|
@ -690,35 +661,24 @@ void AudioDisplay::SetZoomLevel(int new_zoom_level)
|
|||
{
|
||||
zoom_level = new_zoom_level;
|
||||
|
||||
if (!provider)
|
||||
{
|
||||
pixel_samples = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const int samples_per_second = provider ? provider->GetSampleRate() : 48000;
|
||||
const int base_pixels_per_second = 50; /// @todo Make this customisable
|
||||
const int base_samples_per_pixel = samples_per_second / base_pixels_per_second;
|
||||
|
||||
const int factor = GetZoomLevelFactor(zoom_level);
|
||||
const int base_pixels_per_second = 50; /// @todo Make this customisable
|
||||
const double base_ms_per_pixel = 1000.0 / base_pixels_per_second;
|
||||
const double new_ms_per_pixel = 100.0 * base_ms_per_pixel / factor;
|
||||
|
||||
const int new_samples_per_pixel = std::max(1, 100 * base_samples_per_pixel / factor);
|
||||
|
||||
if (pixel_samples != new_samples_per_pixel)
|
||||
if (ms_per_pixel != new_ms_per_pixel)
|
||||
{
|
||||
int client_width = GetClientSize().GetWidth();
|
||||
int64_t center_sample = int64_t(scroll_left + client_width / 2) * pixel_samples;
|
||||
double center_time = (scroll_left + client_width / 2) * ms_per_pixel;
|
||||
|
||||
pixel_samples = new_samples_per_pixel;
|
||||
audio_renderer->SetSamplesPerPixel(pixel_samples);
|
||||
|
||||
pixel_audio_width = provider->GetNumSamples() / pixel_samples + 1;
|
||||
ms_per_pixel = new_ms_per_pixel;
|
||||
pixel_audio_width = std::max(1, int(controller->GetDuration() / ms_per_pixel));
|
||||
|
||||
audio_renderer->SetMillisecondsPerPixel(ms_per_pixel);
|
||||
scrollbar->ChangeLengths(pixel_audio_width, client_width);
|
||||
timeline->ChangeZoom(pixel_samples);
|
||||
|
||||
ScrollSampleToCenter(center_sample);
|
||||
timeline->ChangeZoom(ms_per_pixel);
|
||||
|
||||
ScrollTimeToCenter(center_time);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
@ -770,13 +730,6 @@ void AudioDisplay::SetAmplitudeScale(float scale)
|
|||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
float AudioDisplay::GetAmplitudeScale() const
|
||||
{
|
||||
return audio_renderer->GetAmplitudeScale();
|
||||
}
|
||||
|
||||
|
||||
void AudioDisplay::ReloadRenderingSettings()
|
||||
{
|
||||
std::string colour_scheme_name;
|
||||
|
@ -831,13 +784,6 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
|
|||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
|
||||
if (!provider)
|
||||
{
|
||||
dc.SetBackground(*wxBLACK_BRUSH);
|
||||
dc.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height);
|
||||
bool redraw_scrollbar = false;
|
||||
bool redraw_timeline = false;
|
||||
|
@ -857,13 +803,13 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
|
|||
|
||||
if (audio_bounds.Intersects(updrect))
|
||||
{
|
||||
SampleRange updsamples(
|
||||
SamplesFromRelativeX(updrect.x - foot_size),
|
||||
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
||||
TimeRange updtime(
|
||||
std::max(0, TimeFromRelativeX(updrect.x - foot_size)),
|
||||
std::max(0, TimeFromRelativeX(updrect.x + updrect.width + foot_size)));
|
||||
|
||||
PaintAudio(dc, updsamples, updrect);
|
||||
PaintMarkers(dc, updsamples);
|
||||
PaintLabels(dc, updsamples);
|
||||
PaintAudio(dc, updtime, updrect);
|
||||
PaintMarkers(dc, updtime);
|
||||
PaintLabels(dc, updtime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,10 +824,10 @@ void AudioDisplay::OnPaint(wxPaintEvent&)
|
|||
timeline->Paint(dc);
|
||||
}
|
||||
|
||||
void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
|
||||
void AudioDisplay::PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect)
|
||||
{
|
||||
std::map<int64_t, int>::iterator pt = style_ranges.upper_bound(updsamples.begin());
|
||||
std::map<int64_t, int>::iterator pe = style_ranges.upper_bound(updsamples.end());
|
||||
std::map<int, int>::iterator pt = style_ranges.upper_bound(updtime.begin());
|
||||
std::map<int, int>::iterator pe = style_ranges.upper_bound(updtime.end());
|
||||
|
||||
if (pt != style_ranges.begin())
|
||||
--pt;
|
||||
|
@ -889,8 +835,8 @@ void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
|
|||
while (pt != pe)
|
||||
{
|
||||
AudioRenderingStyle range_style = static_cast<AudioRenderingStyle>(pt->second);
|
||||
int range_x1 = std::max(updrect.x, RelativeXFromSamples(pt->first));
|
||||
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromSamples(pt->first);
|
||||
int range_x1 = std::max(updrect.x, RelativeXFromTime(pt->first));
|
||||
int range_x2 = (++pt == pe) ? updrect.x + updrect.width : RelativeXFromTime(pt->first);
|
||||
|
||||
if (range_x2 > range_x1)
|
||||
{
|
||||
|
@ -899,10 +845,10 @@ void AudioDisplay::PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect)
|
|||
}
|
||||
}
|
||||
|
||||
void AudioDisplay::PaintMarkers(wxDC &dc, SampleRange updsamples)
|
||||
void AudioDisplay::PaintMarkers(wxDC &dc, TimeRange updtime)
|
||||
{
|
||||
AudioMarkerVector markers;
|
||||
controller->GetMarkers(updsamples, markers);
|
||||
controller->GetMarkers(updtime, markers);
|
||||
if (markers.empty()) return;
|
||||
|
||||
wxDCPenChanger pen_retainer(dc, wxPen());
|
||||
|
@ -910,7 +856,7 @@ void AudioDisplay::PaintMarkers(wxDC &dc, SampleRange updsamples)
|
|||
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
||||
{
|
||||
const AudioMarker *marker = *marker_i;
|
||||
int marker_x = RelativeXFromSamples(marker->GetPosition());
|
||||
int marker_x = RelativeXFromTime(marker->GetPosition());
|
||||
|
||||
dc.SetPen(marker->GetStyle());
|
||||
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
|
||||
|
@ -935,10 +881,10 @@ void AudioDisplay::PaintFoot(wxDC &dc, int marker_x, int dir)
|
|||
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
||||
}
|
||||
|
||||
void AudioDisplay::PaintLabels(wxDC &dc, SampleRange updsamples)
|
||||
void AudioDisplay::PaintLabels(wxDC &dc, TimeRange updtime)
|
||||
{
|
||||
std::vector<AudioLabelProvider::AudioLabel> labels;
|
||||
controller->GetLabels(updsamples, labels);
|
||||
controller->GetLabels(updtime, labels);
|
||||
if (labels.empty()) return;
|
||||
|
||||
wxDCFontChanger fc(dc);
|
||||
|
@ -949,8 +895,8 @@ void AudioDisplay::PaintLabels(wxDC &dc, SampleRange updsamples)
|
|||
for (size_t i = 0; i < labels.size(); ++i)
|
||||
{
|
||||
wxSize extent = dc.GetTextExtent(labels[i].text);
|
||||
int left = RelativeXFromSamples(labels[i].range.begin());
|
||||
int width = AbsoluteXFromSamples(labels[i].range.length());
|
||||
int left = RelativeXFromTime(labels[i].range.begin());
|
||||
int width = AbsoluteXFromTime(labels[i].range.length());
|
||||
|
||||
// If it doesn't fit, truncate
|
||||
if (width < extent.GetWidth())
|
||||
|
@ -1034,7 +980,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
|
|||
|
||||
if (show_time)
|
||||
{
|
||||
AssTime new_label_time = controller->MillisecondsFromSamples(SamplesFromAbsoluteX(track_cursor_pos));
|
||||
AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos);
|
||||
track_cursor_label = new_label_time.GetASSFormated();
|
||||
track_cursor_label_rect.x += new_pos - old_pos;
|
||||
RefreshRect(track_cursor_label_rect, false);
|
||||
|
@ -1118,9 +1064,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
|
|||
|
||||
if (event.MiddleIsDown())
|
||||
{
|
||||
context->videoController->JumpToTime(
|
||||
controller->MillisecondsFromSamples(SamplesFromRelativeX(mousepos.x)),
|
||||
agi::vfr::EXACT);
|
||||
context->videoController->JumpToTime(TimeFromRelativeX(mousepos.x), agi::vfr::EXACT);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1131,15 +1075,15 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
|
|||
|
||||
AudioTimingController *timing = controller->GetTimingController();
|
||||
if (!timing) return;
|
||||
int drag_sensitivity = pixel_samples * OPT_GET("Audio/Start Drag Sensitivity")->GetInt();
|
||||
int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? pixel_samples * OPT_GET("Audio/Snap/Distance")->GetInt() : 0;
|
||||
int drag_sensitivity = int(OPT_GET("Audio/Start Drag Sensitivity")->GetInt() * ms_per_pixel);
|
||||
int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? int(OPT_GET("Audio/Snap/Distance")->GetInt() * ms_per_pixel) : 0;
|
||||
|
||||
// Not scrollbar, not timeline, no button action
|
||||
if (event.Moving())
|
||||
{
|
||||
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
|
||||
int timepos = TimeFromRelativeX(mousepos.x);
|
||||
|
||||
if (timing->IsNearbyMarker(samplepos, drag_sensitivity))
|
||||
if (timing->IsNearbyMarker(timepos, drag_sensitivity))
|
||||
SetCursor(wxCursor(wxCURSOR_SIZEWE));
|
||||
else
|
||||
SetCursor(wxNullCursor);
|
||||
|
@ -1147,10 +1091,10 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
|
|||
|
||||
if (event.LeftDown() || event.RightDown())
|
||||
{
|
||||
int64_t samplepos = SamplesFromRelativeX(mousepos.x);
|
||||
int timepos = TimeFromRelativeX(mousepos.x);
|
||||
AudioMarker *marker = event.LeftDown() ?
|
||||
timing->OnLeftClick(samplepos, drag_sensitivity, snap_sensitivity) :
|
||||
timing->OnRightClick(samplepos, drag_sensitivity, snap_sensitivity);
|
||||
timing->OnLeftClick(timepos, drag_sensitivity, snap_sensitivity) :
|
||||
timing->OnRightClick(timepos, drag_sensitivity, snap_sensitivity);
|
||||
|
||||
if (marker)
|
||||
{
|
||||
|
@ -1176,8 +1120,11 @@ void AudioDisplay::OnSize(wxSizeEvent &)
|
|||
timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
|
||||
scrollbar->SetDisplaySize(size);
|
||||
|
||||
SampleRange sel(controller->GetPrimaryPlaybackRange());
|
||||
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
|
||||
if (controller->GetTimingController())
|
||||
{
|
||||
TimeRange sel(controller->GetTimingController()->GetPrimaryPlaybackRange());
|
||||
scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
|
||||
}
|
||||
|
||||
audio_height = size.GetHeight();
|
||||
audio_height -= scrollbar->GetBounds().GetHeight();
|
||||
|
@ -1199,17 +1146,15 @@ void AudioDisplay::OnFocus(wxFocusEvent &)
|
|||
|
||||
void AudioDisplay::OnAudioOpen(AudioProvider *provider)
|
||||
{
|
||||
this->provider = provider;
|
||||
|
||||
if (!audio_renderer_provider)
|
||||
ReloadRenderingSettings();
|
||||
|
||||
audio_renderer->SetAudioProvider(provider);
|
||||
audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
|
||||
|
||||
if (provider)
|
||||
timeline->ChangeAudio(provider->GetNumSamples(), provider->GetSampleRate());
|
||||
timeline->ChangeAudio(controller->GetDuration());
|
||||
|
||||
ms_per_pixel = 0;
|
||||
SetZoomLevel(zoom_level);
|
||||
|
||||
Refresh();
|
||||
|
@ -1238,9 +1183,9 @@ void AudioDisplay::OnAudioOpen(AudioProvider *provider)
|
|||
}
|
||||
}
|
||||
|
||||
void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
|
||||
void AudioDisplay::OnPlaybackPosition(int ms)
|
||||
{
|
||||
int pixel_position = AbsoluteXFromSamples(sample_position);
|
||||
int pixel_position = AbsoluteXFromTime(ms);
|
||||
SetTrackCursor(pixel_position, false);
|
||||
|
||||
if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool())
|
||||
|
@ -1260,12 +1205,12 @@ void AudioDisplay::OnPlaybackPosition(int64_t sample_position)
|
|||
|
||||
void AudioDisplay::OnSelectionChanged()
|
||||
{
|
||||
SampleRange sel(controller->GetPrimaryPlaybackRange());
|
||||
scrollbar->SetSelection(AbsoluteXFromSamples(sel.begin()), AbsoluteXFromSamples(sel.length()));
|
||||
TimeRange sel(controller->GetPrimaryPlaybackRange());
|
||||
scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
|
||||
|
||||
if (OPT_GET("Audio/Auto/Scroll")->GetBool())
|
||||
{
|
||||
ScrollSampleRangeInView(sel);
|
||||
ScrollTimeRangeInView(sel);
|
||||
}
|
||||
|
||||
RefreshRect(scrollbar->GetBounds(), false);
|
||||
|
@ -1278,16 +1223,16 @@ void AudioDisplay::OnStyleRangesChanged()
|
|||
AudioStyleRangeMerger asrm;
|
||||
controller->GetTimingController()->GetRenderingStyles(asrm);
|
||||
|
||||
std::map<int64_t, int> old_style_ranges;
|
||||
std::map<int, int> old_style_ranges;
|
||||
swap(old_style_ranges, style_ranges);
|
||||
style_ranges.insert(asrm.begin(), asrm.end());
|
||||
|
||||
std::map<int64_t, int>::iterator old_style_it = old_style_ranges.begin();
|
||||
std::map<int64_t, int>::iterator new_style_it = style_ranges.begin();
|
||||
std::map<int, int>::iterator old_style_it = old_style_ranges.begin();
|
||||
std::map<int, int>::iterator new_style_it = style_ranges.begin();
|
||||
|
||||
int old_style = old_style_it->second;
|
||||
int new_style = new_style_it->second;
|
||||
int64_t range_start = 0;
|
||||
int range_start = 0;
|
||||
|
||||
// Repaint each range which has changed
|
||||
while (old_style_it != old_style_ranges.end() || new_style_it != style_ranges.end())
|
||||
|
@ -1313,20 +1258,20 @@ void AudioDisplay::OnStyleRangesChanged()
|
|||
// Fill in the last style range
|
||||
if (old_style != new_style)
|
||||
{
|
||||
Redraw(range_start, SamplesFromRelativeX(GetClientSize().GetWidth()));
|
||||
Redraw(range_start, TimeFromRelativeX(GetClientSize().GetWidth()));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDisplay::Redraw(int64_t sample_start, int64_t sample_end)
|
||||
void AudioDisplay::Redraw(int time_start, int time_end)
|
||||
{
|
||||
if (sample_start == sample_end) return;
|
||||
if (time_start == time_end) return;
|
||||
|
||||
sample_start = RelativeXFromSamples(sample_start) - foot_size;
|
||||
sample_end = RelativeXFromSamples(sample_end) + foot_size;
|
||||
time_start = RelativeXFromTime(time_start) - foot_size;
|
||||
time_end = RelativeXFromTime(time_end) + foot_size;
|
||||
|
||||
if (sample_end >= 0 && sample_start <= GetClientSize().GetWidth())
|
||||
if (time_end >= 0 && time_start <= GetClientSize().GetWidth())
|
||||
{
|
||||
RefreshRect(wxRect(sample_start, audio_top, sample_end - sample_start, audio_height), false);
|
||||
RefreshRect(wxRect(time_start, audio_top, time_end - time_start, audio_height), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,9 +110,6 @@ class AudioDisplay: public wxWindow {
|
|||
/// The current audio renderer
|
||||
agi::scoped_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
|
||||
|
||||
/// Our current audio provider
|
||||
AudioProvider *provider;
|
||||
|
||||
/// The controller managing us
|
||||
AudioController *controller;
|
||||
|
||||
|
@ -139,8 +136,8 @@ class AudioDisplay: public wxWindow {
|
|||
/// Total width of the audio in pixels
|
||||
int pixel_audio_width;
|
||||
|
||||
/// Horizontal zoom measured in audio samples per pixel
|
||||
int pixel_samples;
|
||||
/// Horizontal zoom measured in millisecond per pixels
|
||||
double ms_per_pixel;
|
||||
|
||||
/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
|
||||
float scale_amplitude;
|
||||
|
@ -171,7 +168,7 @@ class AudioDisplay: public wxWindow {
|
|||
void RemoveTrackCursor();
|
||||
|
||||
/// Previous style ranges for optimizing redraw when ranges change
|
||||
std::map<int64_t, int> style_ranges;
|
||||
std::map<int, int> style_ranges;
|
||||
|
||||
/// @brief Reload all rendering settings from Options and reset caches
|
||||
///
|
||||
|
@ -179,21 +176,21 @@ class AudioDisplay: public wxWindow {
|
|||
/// in Options and need to be reloaded to take effect.
|
||||
void ReloadRenderingSettings();
|
||||
|
||||
/// @brief Repaint a range of samples
|
||||
/// @param sample_start First sample to repaint
|
||||
/// @param sample_end Last sample to repaint
|
||||
void Redraw(int64_t sample_start, int64_t sample_end);
|
||||
/// @brief Repaint a time range
|
||||
/// @param ms_start Beginning of range to repaint
|
||||
/// @param ms_end End of range to repaint
|
||||
void Redraw(int ms_start, int ms_end);
|
||||
|
||||
/// Paint the audio data for a range of samples
|
||||
/// Paint the audio data for a time range
|
||||
/// @param dc DC to paint to
|
||||
/// @param updsamples Sample range to repaint
|
||||
/// @param updtime Time range to repaint
|
||||
/// @param updrect Pixel range to repaint
|
||||
void PaintAudio(wxDC &dc, SampleRange updsamples, wxRect updrect);
|
||||
void PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect);
|
||||
|
||||
/// Paint the markers in a range of samples
|
||||
/// Paint the markers in a time range
|
||||
/// @param dc DC to paint to
|
||||
/// @param updsamples Sample range to repaint
|
||||
void PaintMarkers(wxDC &dc, SampleRange updsamples);
|
||||
/// @param updtime Time range to repaint
|
||||
void PaintMarkers(wxDC &dc, TimeRange updtime);
|
||||
|
||||
/// Draw a single foot for a marker
|
||||
/// @param dc DC to paint to
|
||||
|
@ -201,10 +198,10 @@ class AudioDisplay: public wxWindow {
|
|||
/// @param dir -1 for left, 1 for right
|
||||
void PaintFoot(wxDC &dc, int marker_x, int dir);
|
||||
|
||||
/// Paint the labels in a range of samples
|
||||
/// Paint the labels in a time range
|
||||
/// @param dc DC to paint to
|
||||
/// @param updsamples Sample range to repaint
|
||||
void PaintLabels(wxDC &dc, SampleRange updsamples);
|
||||
/// @param updtime Time range to repaint
|
||||
void PaintLabels(wxDC &dc, TimeRange updtime);
|
||||
|
||||
/// Paint the track cursor
|
||||
/// @param dc DC to paint to
|
||||
|
@ -223,7 +220,7 @@ class AudioDisplay: public wxWindow {
|
|||
|
||||
// AudioControllerAudioEventListener implementation
|
||||
void OnAudioOpen(AudioProvider *provider);
|
||||
void OnPlaybackPosition(int64_t sample_position);
|
||||
void OnPlaybackPosition(int ms_position);
|
||||
void OnSelectionChanged();
|
||||
void OnStyleRangesChanged();
|
||||
void OnMarkerMoved();
|
||||
|
@ -248,46 +245,49 @@ public:
|
|||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param pixel_position Absolute pixel to put in center of the audio display
|
||||
void ScrollPixelToCenter(int pixel_position);
|
||||
void ScrollPixelToCenter(int pixel_position) { ScrollPixelToLeft(pixel_position - GetClientRect().GetWidth()/2); }
|
||||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param sample_position Audio sample to put at left edge of the audio display
|
||||
void ScrollSampleToLeft(int64_t sample_position);
|
||||
/// @param ms Time in milliseconds to put at left edge of the audio display
|
||||
void ScrollTimeToLeft(int ms) { ScrollPixelToLeft(AbsoluteXFromTime(ms)); }
|
||||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param sample_position Audio sample to put in center of the audio display
|
||||
void ScrollSampleToCenter(int64_t sample_position);
|
||||
/// @param ms Time in milliseconds to put in center of the audio display
|
||||
void ScrollTimeToCenter(int ms) { ScrollPixelToCenter(AbsoluteXFromTime(ms)); }
|
||||
|
||||
/// @brief Scroll the audio display
|
||||
/// @param range Range of audio samples to ensure is in view
|
||||
/// @param range Time range to ensure is in view
|
||||
///
|
||||
/// If the entire range is already visible inside the display, nothing is scrolled. If
|
||||
/// just one of the two endpoints is visible, the display is scrolled such that the
|
||||
/// visible endpoint stays in view but more of the rest of the range becomes visible.
|
||||
/// If the entire range is already visible inside the display, nothing is
|
||||
/// scrolled. If just one of the two endpoints is visible, the display is
|
||||
/// scrolled such that the visible endpoint stays in view but more of the
|
||||
/// rest of the range becomes visible.
|
||||
///
|
||||
/// If the entire range fits inside the display, the display is centered over the range.
|
||||
/// For this calculation, the display is considered smaller by some margins, see below.
|
||||
/// If the entire range fits inside the display, the display is centered
|
||||
/// over the range. For this calculation, the display is considered
|
||||
/// smaller by some margins, see below.
|
||||
///
|
||||
/// If the range does not fit within the display with margins subtracted, the start of
|
||||
/// the range is ensured visible and as much of the rest of the range is brought into
|
||||
/// view.
|
||||
/// If the range does not fit within the display with margins subtracted,
|
||||
/// the start of the range is ensured visible and as much of the rest of
|
||||
/// the range is brought into view.
|
||||
///
|
||||
/// For the purpose of this function, a 5 percent margin is assumed at each end of the
|
||||
/// audio display such that a range endpoint that is ensured to be in view never gets
|
||||
/// closer to the edge of the display than the margin. The edge that is not ensured to
|
||||
/// be in view might be outside of view or might be closer to the display edge than the
|
||||
/// For the purpose of this function, a 5 percent margin is assumed at each
|
||||
/// end of the audio display such that a range endpoint that is ensured to
|
||||
/// be in view never gets closer to the edge of the display than the
|
||||
/// margin. The edge that is not ensured to be in view might be outside of
|
||||
/// view or might be closer to the display edge than the
|
||||
/// margin.
|
||||
void ScrollSampleRangeInView(const SampleRange &range);
|
||||
void ScrollTimeRangeInView(const TimeRange &range);
|
||||
|
||||
|
||||
/// @brief Change the zoom level
|
||||
/// @param new_zoom_level The new zoom level to use
|
||||
///
|
||||
/// A zoom level of 0 is the default zoom level, all other levels are based on this.
|
||||
/// Negative zoom levels zoom out, positive zoom in.
|
||||
/// A zoom level of 0 is the default zoom level, all other levels are based
|
||||
/// on this. Negative zoom levels zoom out, positive zoom in.
|
||||
///
|
||||
/// The zoom levels generally go from +30 to -30. It is possible to zoom in more than
|
||||
/// +30
|
||||
/// The zoom levels generally go from +30 to -30. It is possible to zoom in
|
||||
/// more than +30.
|
||||
void SetZoomLevel(int new_zoom_level);
|
||||
|
||||
/// @brief Get the zoom level
|
||||
|
@ -320,21 +320,15 @@ public:
|
|||
/// @param scale New amplitude scale factor, 1.0 is no scaling
|
||||
void SetAmplitudeScale(float scale);
|
||||
|
||||
/// @brief Get amplitude scale factor
|
||||
/// @return The amplitude scaling factor
|
||||
float GetAmplitudeScale() const;
|
||||
|
||||
|
||||
/// @brief Get a sample index from an X coordinate relative to current scroll
|
||||
int64_t SamplesFromRelativeX(int x) const { return (scroll_left + x) * pixel_samples; }
|
||||
/// @brief Get a sample index from an absolute X coordinate
|
||||
int64_t SamplesFromAbsoluteX(int x) const { return x * pixel_samples; }
|
||||
/// @brief Get an X coordinate relative to the current scroll from a sample index
|
||||
int RelativeXFromSamples(int64_t samples) const { return samples/pixel_samples - scroll_left; }
|
||||
/// @brief Get an absolute X coordinate from a sample index
|
||||
int AbsoluteXFromSamples(int64_t samples) const { return samples/pixel_samples; }
|
||||
/// Get a time in milliseconds from an X coordinate relative to current scroll
|
||||
int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
|
||||
/// Get a time in milliseconds from an absolute X coordinate
|
||||
int TimeFromAbsoluteX(int x) const { return int(x * ms_per_pixel); }
|
||||
/// Get an X coordinate relative to the current scroll from a time in milliseconds
|
||||
int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
|
||||
/// Get an absolute X coordinate from a time in milliseconds
|
||||
int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
|
||||
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
|
|
@ -36,21 +36,20 @@
|
|||
|
||||
class AudioMarkerKeyframe : public AudioMarker {
|
||||
Pen *style;
|
||||
int64_t position;
|
||||
int position;
|
||||
public:
|
||||
AudioMarkerKeyframe(Pen *style, int64_t position) : style(style), position(position) { }
|
||||
int64_t GetPosition() const { return position; }
|
||||
AudioMarkerKeyframe(Pen *style, int position) : style(style), position(position) { }
|
||||
int GetPosition() const { return position; }
|
||||
FeetStyle GetFeet() const { return Feet_None; }
|
||||
bool CanSnap() const { return true; }
|
||||
wxPen GetStyle() const { return *style; }
|
||||
operator int64_t() const { return position; }
|
||||
operator int() const { return position; }
|
||||
};
|
||||
|
||||
AudioMarkerProviderKeyframes::AudioMarkerProviderKeyframes(agi::Context *c, const char *opt_name)
|
||||
: controller(c->audioController)
|
||||
, vc(c->videoController)
|
||||
: vc(c->videoController)
|
||||
, keyframe_slot(vc->AddKeyframesListener(&AudioMarkerProviderKeyframes::Update, this))
|
||||
, audio_open_slot(controller->AddAudioOpenListener(&AudioMarkerProviderKeyframes::Update, this))
|
||||
, audio_open_slot(c->audioController->AddAudioOpenListener(&AudioMarkerProviderKeyframes::Update, this))
|
||||
, timecode_slot(vc->AddTimecodesListener(&AudioMarkerProviderKeyframes::Update, this))
|
||||
, enabled_slot(OPT_SUB(opt_name, &AudioMarkerProviderKeyframes::Update, this))
|
||||
, enabled_opt(OPT_GET(opt_name))
|
||||
|
@ -76,13 +75,12 @@ void AudioMarkerProviderKeyframes::Update() {
|
|||
markers.clear();
|
||||
markers.reserve(keyframes.size());
|
||||
for (size_t i = 0; i < keyframes.size(); ++i) {
|
||||
markers.push_back(AudioMarkerKeyframe(style.get(),
|
||||
controller->SamplesFromMilliseconds(timecodes.TimeAtFrame(keyframes[i]))));
|
||||
markers.push_back(AudioMarkerKeyframe(style.get(), timecodes.TimeAtFrame(keyframes[i])));
|
||||
}
|
||||
AnnounceMarkerMoved();
|
||||
}
|
||||
|
||||
void AudioMarkerProviderKeyframes::GetMarkers(SampleRange const& range, AudioMarkerVector &out) const {
|
||||
void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
|
||||
// Find first and last keyframes inside the range
|
||||
std::vector<AudioMarkerKeyframe>::const_iterator
|
||||
a = lower_bound(markers.begin(), markers.end(), range.begin()),
|
||||
|
|
|
@ -40,8 +40,6 @@ namespace agi {
|
|||
|
||||