make engine 'multiple-section songs' aware -> you should now be able to play your .cue related music

This commit is contained in:
Paweł Bara 2011-01-02 18:53:45 +00:00
parent bbe07cef44
commit bbe97b00a2
7 changed files with 79 additions and 31 deletions

View File

@ -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())

View File

@ -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_;

View File

@ -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);
}

View File

@ -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_;

View File

@ -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;

View File

@ -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();

View File

@ -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_; }