make engine 'multiple-section songs' aware -> you should now be able to play your .cue related music
This commit is contained in:
parent
bbe07cef44
commit
bbe97b00a2
@ -124,7 +124,9 @@ void Player::HandleSpecialLoad(const PlaylistItem::SpecialLoadResult &result) {
|
||||
if (!item || item->Url() != result.original_url_)
|
||||
return;
|
||||
|
||||
engine_->Play(result.media_url_, stream_change_type_);
|
||||
engine_->Play(result.media_url_, stream_change_type_,
|
||||
item->Metadata().beginning() * 1000,
|
||||
item->Metadata().end() * 1000);
|
||||
|
||||
current_item_ = item;
|
||||
loading_async_ = QUrl();
|
||||
@ -292,7 +294,9 @@ void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) {
|
||||
}
|
||||
else {
|
||||
loading_async_ = QUrl();
|
||||
engine_->Play(current_item_->Url(), change);
|
||||
engine_->Play(current_item_->Url(), change,
|
||||
current_item_->Metadata().beginning() * 1000,
|
||||
current_item_->Metadata().end() * 1000);
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
if (lastfm_->IsScrobblingEnabled())
|
||||
|
@ -222,7 +222,7 @@ class Song {
|
||||
void set_comment(const QString& v) { d->comment_ = v; }
|
||||
void set_compilation(bool v) { d->compilation_ = v; }
|
||||
void set_sampler(bool v) { d->sampler_ = v; }
|
||||
void set_beginning(int v) { d->beginning_ = v; }
|
||||
void set_beginning(int v) { d->beginning_ = qMax(0, v); }
|
||||
void set_end(int v) { d->end_ = v; }
|
||||
void set_length(int v) { d->end_ = d->beginning_ + v; }
|
||||
void set_bitrate(int v) { d->bitrate_ = v; }
|
||||
@ -291,12 +291,14 @@ class Song {
|
||||
// The beginning of the song in seconds. In case of single-part media
|
||||
// streams, this will equal to 0. In case of multi-part streams on the
|
||||
// other hand, this will mark the beginning of a section represented by
|
||||
// this Song object.
|
||||
// this Song object. This is always greater than 0.
|
||||
int beginning_;
|
||||
// The end of the song in seconds. In case of single-part media
|
||||
// streams, this will equal to the song's length. In case of multi-part
|
||||
// streams on the other hand, this will mark the end of a section
|
||||
// represented by this Song object.
|
||||
// This may be negative indicating that the length of this song is
|
||||
// unknown.
|
||||
int end_;
|
||||
|
||||
int bitrate_;
|
||||
|
@ -29,6 +29,8 @@ const char* Engine::Base::kSettingsGroup = "Player";
|
||||
|
||||
Engine::Base::Base()
|
||||
: volume_(50),
|
||||
beginning_(0),
|
||||
end_(0),
|
||||
scope_(kScopeSize),
|
||||
fadeout_enabled_(true),
|
||||
fadeout_duration_(2000),
|
||||
@ -41,8 +43,12 @@ Engine::Base::Base()
|
||||
Engine::Base::~Base() {
|
||||
}
|
||||
|
||||
bool Engine::Base::Load(const QUrl &url, TrackChangeType) {
|
||||
bool Engine::Base::Load(const QUrl &url, TrackChangeType,
|
||||
uint beginning, int end) {
|
||||
url_ = url;
|
||||
beginning_ = beginning;
|
||||
end_ = end;
|
||||
|
||||
about_to_end_emitted_ = false;
|
||||
return true;
|
||||
}
|
||||
@ -80,8 +86,9 @@ int Engine::Base::AddBackgroundStream(const QUrl& url) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Engine::Base::Play(const QUrl& u, TrackChangeType c) {
|
||||
if (!Load(u, c))
|
||||
bool Engine::Base::Play(const QUrl& u, TrackChangeType c, uint beginning, int end) {
|
||||
if (!Load(u, c, beginning, end))
|
||||
return false;
|
||||
return Play();
|
||||
|
||||
return Play(0);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class Base : public QObject, boost::noncopyable {
|
||||
virtual bool CanDecode(const QUrl &url) = 0;
|
||||
|
||||
virtual void StartPreloading(const QUrl&) {}
|
||||
virtual bool Play(uint offset = 0) = 0;
|
||||
virtual bool Play(uint offset) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual void Unpause() = 0;
|
||||
@ -60,11 +60,19 @@ class Base : public QObject, boost::noncopyable {
|
||||
virtual uint position() const = 0;
|
||||
virtual uint length() const { return 0; }
|
||||
|
||||
// Helpers
|
||||
virtual bool Load(const QUrl &url, TrackChangeType change);
|
||||
bool Play(const QUrl &u, TrackChangeType c);
|
||||
void SetVolume( uint value );
|
||||
// Subclasses should respect given markers (beginning and end) which are
|
||||
// in miliseconds.
|
||||
virtual bool Load(const QUrl& url, TrackChangeType change,
|
||||
uint beginning, int end);
|
||||
|
||||
// Plays a media stream represented with the URL 'u' from the given 'beginning'
|
||||
// to the given 'end' (usually from 0 to a song's length). Both markers
|
||||
// should be passed in miliseconds. 'end' can be negative, indicating that the
|
||||
// real length of 'u' stream is unknown.
|
||||
bool Play(const QUrl& u, TrackChangeType c,
|
||||
uint beginning, int end);
|
||||
|
||||
void SetVolume(uint value);
|
||||
|
||||
// Simple accessors
|
||||
inline uint volume() const { return volume_; }
|
||||
@ -108,6 +116,8 @@ class Base : public QObject, boost::noncopyable {
|
||||
|
||||
protected:
|
||||
uint volume_;
|
||||
uint beginning_;
|
||||
int end_;
|
||||
QUrl url_;
|
||||
Scope scope_;
|
||||
|
||||
|
@ -64,6 +64,7 @@ const char* GstEngine::kHypnotoadPipeline =
|
||||
"band0=-24 band1=-3 band2=7.5 band3=12 band4=8 "
|
||||
"band5=6 band6=5 band7=6 band8=0 band9=-24";
|
||||
|
||||
// TODO: weird analyzer problems with .cues
|
||||
|
||||
GstEngine::GstEngine()
|
||||
: Engine::Base(),
|
||||
@ -266,17 +267,18 @@ uint GstEngine::position() const {
|
||||
if (!current_pipeline_)
|
||||
return 0;
|
||||
|
||||
return uint(current_pipeline_->position() / GST_MSECOND);
|
||||
int result = (current_pipeline_->position() / GST_MSECOND) - beginning_;
|
||||
return uint(qMax(0, result));
|
||||
}
|
||||
|
||||
uint GstEngine::length() const {
|
||||
if (!current_pipeline_)
|
||||
return 0;
|
||||
|
||||
return uint(current_pipeline_->length() / GST_MSECOND);
|
||||
int result = end_ - beginning_;
|
||||
return uint(qMax(0, result));
|
||||
}
|
||||
|
||||
|
||||
Engine::State GstEngine::state() const {
|
||||
if (!current_pipeline_)
|
||||
return url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
||||
@ -439,10 +441,11 @@ QUrl GstEngine::FixupUrl(const QUrl& url) {
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool GstEngine::Load(const QUrl& url, Engine::TrackChangeType change) {
|
||||
bool GstEngine::Load(const QUrl& url, Engine::TrackChangeType change,
|
||||
uint beginning, int end) {
|
||||
EnsureInitialised();
|
||||
|
||||
Engine::Base::Load(url, change);
|
||||
Engine::Base::Load(url, change, beginning, end);
|
||||
|
||||
// Clementine just crashes when asked to load a file that doesn't exist on
|
||||
// Windows, so check for that here. This is definitely the wrong place for
|
||||
@ -503,7 +506,7 @@ void GstEngine::StartFadeout() {
|
||||
}
|
||||
|
||||
|
||||
bool GstEngine::Play( uint offset ) {
|
||||
bool GstEngine::Play(uint offset) {
|
||||
EnsureInitialised();
|
||||
|
||||
if (!current_pipeline_)
|
||||
@ -522,7 +525,9 @@ void GstEngine::PlayDone() {
|
||||
BoundFutureWatcher<GstStateChangeReturn, uint>* watcher =
|
||||
static_cast<BoundFutureWatcher<GstStateChangeReturn, uint>*>(sender());
|
||||
watcher->deleteLater();
|
||||
|
||||
GstStateChangeReturn ret = watcher->result();
|
||||
uint offset = watcher->data();
|
||||
|
||||
if (!current_pipeline_)
|
||||
return;
|
||||
@ -533,7 +538,7 @@ void GstEngine::PlayDone() {
|
||||
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->url()) {
|
||||
qDebug() << "Redirecting to" << redirect_url;
|
||||
current_pipeline_ = CreatePipeline(redirect_url);
|
||||
Play(watcher->data());
|
||||
Play(offset);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -547,8 +552,9 @@ void GstEngine::PlayDone() {
|
||||
|
||||
current_sample_ = 0;
|
||||
|
||||
if (watcher->data()) {
|
||||
Seek(watcher->data());
|
||||
// initial offset
|
||||
if(offset != 0 || beginning_ != 0) {
|
||||
Seek(offset);
|
||||
}
|
||||
|
||||
emit StateChanged(Engine::Playing);
|
||||
@ -559,6 +565,7 @@ void GstEngine::Stop() {
|
||||
StopTimers();
|
||||
|
||||
url_ = QUrl(); // To ensure we return Empty from state()
|
||||
beginning_ = end_ = 0;
|
||||
|
||||
if (fadeout_enabled_ && current_pipeline_)
|
||||
StartFadeout();
|
||||
@ -599,7 +606,7 @@ void GstEngine::Seek(uint ms) {
|
||||
if (!current_pipeline_)
|
||||
return;
|
||||
|
||||
seek_pos_ = ms;
|
||||
seek_pos_ = beginning_ + ms;
|
||||
waiting_to_seek_ = true;
|
||||
|
||||
if (!seek_timer_->isActive()) {
|
||||
@ -664,16 +671,29 @@ void GstEngine::timerEvent(QTimerEvent* e) {
|
||||
// we are fading
|
||||
PruneScope();
|
||||
|
||||
// Emit TrackAboutToEnd when we're a few seconds away from finishing
|
||||
if (current_pipeline_) {
|
||||
const qint64 nanosec_position = current_pipeline_->position();
|
||||
const qint64 nanosec_length = current_pipeline_->length();
|
||||
const qint64 remaining = (nanosec_length - nanosec_position) / 1000000;
|
||||
const qint64 current_position = position();
|
||||
const qint64 current_length = length();
|
||||
|
||||
const qint64 remaining = current_length - current_position;
|
||||
|
||||
const qint64 fudge = kTimerInterval + 100; // Mmm fudge
|
||||
const qint64 gap = autocrossfade_enabled_ ? fadeout_duration_ : kPreloadGap;
|
||||
|
||||
if (nanosec_length > 0 && remaining < gap + fudge)
|
||||
EmitAboutToEnd();
|
||||
// only if we know the length of the current stream...
|
||||
if(current_length > 0) {
|
||||
// emit TrackAboutToEnd when we're a few seconds away from finishing
|
||||
if (remaining < gap + fudge) {
|
||||
EmitAboutToEnd();
|
||||
}
|
||||
|
||||
// when at the end, kill the track if it didn't stop yet (probably a
|
||||
// multisection media file)
|
||||
// TODO: EndOfStreamReached
|
||||
if(current_position > current_length) {
|
||||
emit TrackEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -786,7 +806,7 @@ qint64 GstEngine::PruneScope() {
|
||||
return 0;
|
||||
|
||||
// get the position playing in the audio device
|
||||
const qint64 pos = current_pipeline_->position();
|
||||
const qint64 pos = position() * 1e6;
|
||||
const qint64 segment_start = current_pipeline_->segment_start();
|
||||
|
||||
GstBuffer *buf = 0;
|
||||
|
@ -87,7 +87,8 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
|
||||
public slots:
|
||||
void StartPreloading(const QUrl &);
|
||||
bool Load(const QUrl&, Engine::TrackChangeType change);
|
||||
bool Load(const QUrl&, Engine::TrackChangeType change,
|
||||
uint beginning, int end);
|
||||
bool Play(uint offset);
|
||||
void Stop();
|
||||
void Pause();
|
||||
|
@ -74,7 +74,11 @@ class GstEnginePipeline : public QObject {
|
||||
// Get information about the music playback
|
||||
QUrl url() const { return url_; }
|
||||
bool is_valid() const { return valid_; }
|
||||
// Please note that this method (unlike GstEngine's.position()) is
|
||||
// multiple-section media unaware.
|
||||
qint64 position() const;
|
||||
// Please note that this method (unlike GstEngine's.length()) is
|
||||
// multiple-section media unaware.
|
||||
qint64 length() const;
|
||||
GstState state() const;
|
||||
qint64 segment_start() const { return segment_start_; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user