Generate Hypnotoad rather than streaming it.

This commit is contained in:
John Maguire 2010-07-14 11:16:56 +00:00
parent d612e8468d
commit 41ab99e62e
9 changed files with 108 additions and 24 deletions

3
dist/macdeploy.py vendored
View File

@ -56,6 +56,8 @@ XINEPLUGIN_SEARCH_PATH=[
GSTREAMER_PLUGINS=[ GSTREAMER_PLUGINS=[
# Core plugins # Core plugins
'libgstaudioconvert.so', 'libgstaudioconvert.so',
'libgstaudiofx.so',
'libgstaudiotestsrc.so',
'libgstaudioresample.so', 'libgstaudioresample.so',
'libgstautodetect.so', 'libgstautodetect.so',
'libgstcoreelements.so', 'libgstcoreelements.so',
@ -103,6 +105,7 @@ GSTREAMER_SEARCH_PATH=[
QT_PLUGINS = [ QT_PLUGINS = [
'accessible/libqtaccessiblewidgets.dylib', 'accessible/libqtaccessiblewidgets.dylib',
'bearer/libqcorewlanbearer.dylib',
'codecs/libqcncodecs.dylib', 'codecs/libqcncodecs.dylib',
'codecs/libqjpcodecs.dylib', 'codecs/libqjpcodecs.dylib',
'codecs/libqkrcodecs.dylib', 'codecs/libqkrcodecs.dylib',

View File

@ -49,7 +49,6 @@
using boost::shared_ptr; using boost::shared_ptr;
const char* Player::kRainUrl = "http://data.clementine-player.org/rainymood"; const char* Player::kRainUrl = "http://data.clementine-player.org/rainymood";
const char* Player::kHypnotoadUrl = "http://data.clementine-player.org/hypnotoad";
#ifdef Q_WS_X11 #ifdef Q_WS_X11
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) { QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
@ -621,7 +620,7 @@ void Player::MakeItRain(bool rain) {
void Player::AllHail(bool hypnotoad) { void Player::AllHail(bool hypnotoad) {
const bool is_hailing = toad_stream_ != -1; const bool is_hailing = toad_stream_ != -1;
if (hypnotoad && !is_hailing) { if (hypnotoad && !is_hailing) {
toad_stream_ = engine_->AddBackgroundStream(QUrl(kHypnotoadUrl)); toad_stream_ = engine_->AllGloryToTheHypnotoad();
} }
if (!hypnotoad && is_hailing) { if (!hypnotoad && is_hailing) {
engine_->StopBackgroundStream(toad_stream_); engine_->StopBackgroundStream(toad_stream_);

View File

@ -188,7 +188,6 @@ class Player : public QObject {
int volume_before_mute_; int volume_before_mute_;
static const char* kRainUrl; static const char* kRainUrl;
static const char* kHypnotoadUrl;
}; };
#endif // PLAYER_H #endif // PLAYER_H

View File

@ -53,6 +53,7 @@ class Base : public QObject, boost::noncopyable {
virtual int AddBackgroundStream(const QUrl& url); virtual int AddBackgroundStream(const QUrl& url);
virtual void StopBackgroundStream(int id) {} virtual void StopBackgroundStream(int id) {}
virtual int AllGloryToTheHypnotoad() { return -1; }
virtual State state() const = 0; virtual State state() const = 0;
virtual uint position() const = 0; virtual uint position() const = 0;

View File

@ -48,6 +48,13 @@ using boost::shared_ptr;
const char* GstEngine::kSettingsGroup = "GstEngine"; const char* GstEngine::kSettingsGroup = "GstEngine";
const char* GstEngine::kAutoSink = "autoaudiosink"; const char* GstEngine::kAutoSink = "autoaudiosink";
const char* GstEngine::kHypnotoadPipeline =
"audiotestsrc wave=6 ! "
"audioecho intensity=1 delay=50000000 ! "
"audioecho intensity=1 delay=25000000 ! "
"equalizer-10bands "
"band0=-24 band1=-3 band2=7.5 band3=12 band4=8 "
"band5=6 band6=5 band7=6 band8=0 band9=-24";
GstEngine::GstEngine() GstEngine::GstEngine()
@ -690,7 +697,7 @@ GstEngine::PluginDetailsList
return ret; return ret;
} }
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) { shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this)); shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this));
ret->set_output_device(sink_, device_); ret->set_output_device(sink_, device_);
ret->set_replaygain(rg_enabled_, rg_mode_, rg_preamp_, rg_compression_); ret->set_replaygain(rg_enabled_, rg_mode_, rg_preamp_, rg_compression_);
@ -705,7 +712,12 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
SLOT(NewMetaData(Engine::SimpleMetaBundle))); SLOT(NewMetaData(Engine::SimpleMetaBundle)));
connect(ret.get(), SIGNAL(destroyed()), SLOT(ClearScopeBuffers())); connect(ret.get(), SIGNAL(destroyed()), SLOT(ClearScopeBuffers()));
if (!ret->Init(url)) return ret;
}
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
if (!ret->InitFromUrl(url))
ret.reset(); ret.reset();
return ret; return ret;
@ -798,3 +810,24 @@ void GstEngine::BackgroundStreamFinished() {
GstEnginePipeline* pipeline = qobject_cast<GstEnginePipeline*>(sender()); GstEnginePipeline* pipeline = qobject_cast<GstEnginePipeline*>(sender());
pipeline->SetNextUrl(pipeline->url()); pipeline->SetNextUrl(pipeline->url());
} }
int GstEngine::AllGloryToTheHypnotoad() {
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline();
pipeline->InitFromString(kHypnotoadPipeline);
if (!pipeline) {
return -1;
}
pipeline->SetVolume(5); // Hypnotoad is *loud*.
// We don't want to get metadata messages or end notifications.
disconnect(pipeline.get(), SIGNAL(MetadataFound(Engine::SimpleMetaBundle)), this, 0);
disconnect(pipeline.get(), SIGNAL(EndOfStreamReached(bool)), this, 0);
connect(pipeline.get(), SIGNAL(EndOfStreamReached(bool)), SLOT(BackgroundStreamFinished()));
if (!pipeline->SetState(GST_STATE_PLAYING)) {
qWarning() << "Could not set thread to PLAYING.";
pipeline.reset();
return -1;
}
int stream_id = next_background_stream_id_++;
background_streams_[stream_id] = pipeline;
return stream_id;
}

View File

@ -68,6 +68,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
int AddBackgroundStream(const QUrl& url); int AddBackgroundStream(const QUrl& url);
void StopBackgroundStream(int id); void StopBackgroundStream(int id);
int AllGloryToTheHypnotoad();
uint position() const; uint position() const;
uint length() const; uint length() const;
@ -133,6 +134,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
void StartTimers(); void StartTimers();
void StopTimers(); void StopTimers();
boost::shared_ptr<GstEnginePipeline> CreatePipeline();
boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url); boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url);
void UpdateScope(); void UpdateScope();
@ -143,6 +145,8 @@ class GstEngine : public Engine::Base, public BufferConsumer {
static const int kPreloadGap = 1000; // msec static const int kPreloadGap = 1000; // msec
static const int kSeekDelay = 100; // msec static const int kSeekDelay = 100; // msec
static const char* kHypnotoadPipeline;
QString sink_; QString sink_;
QString device_; QString device_;

View File

@ -77,8 +77,7 @@ bool GstEnginePipeline::StopUriDecodeBin(gpointer bin) {
return false; // So it doesn't get called again return false; // So it doesn't get called again
} }
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) { bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin) {
GstElement* new_bin = engine_->CreateElement("uridecodebin");
if (!new_bin) return false; if (!new_bin) return false;
// Destroy the old one, if any // Destroy the old one, if any
@ -94,17 +93,33 @@ bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
segment_start_received_ = false; segment_start_received_ = false;
gst_bin_add(GST_BIN(pipeline_), uridecodebin_); gst_bin_add(GST_BIN(pipeline_), uridecodebin_);
g_object_set(G_OBJECT(uridecodebin_), "uri", url.toEncoded().constData(), NULL);
g_signal_connect(G_OBJECT(uridecodebin_), "pad-added", G_CALLBACK(NewPadCallback), this);
g_signal_connect(G_OBJECT(uridecodebin_), "drained", G_CALLBACK(SourceDrainedCallback), this);
return true; return true;
} }
bool GstEnginePipeline::Init(const QUrl &url) { bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
pipeline_ = gst_pipeline_new("pipeline"); GstElement* new_bin = engine_->CreateElement("uridecodebin");
url_ = url; g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), NULL);
g_signal_connect(G_OBJECT(new_bin), "drained", G_CALLBACK(SourceDrainedCallback), this);
g_signal_connect(G_OBJECT(new_bin), "pad-added", G_CALLBACK(NewPadCallback), this);
return ReplaceDecodeBin(new_bin);
}
GstElement* GstEnginePipeline::CreateDecodeBinFromString(const char* pipeline) {
GError* error = NULL;
GstElement* bin = gst_parse_bin_from_description(pipeline, TRUE, &error);
if (error) {
QString message = QString::fromLocal8Bit(error->message);
g_error_free(error);
qWarning() << message;
emit Error(message);
return NULL;
}
return bin;
}
bool GstEnginePipeline::Init() {
// Here we create all the parts of the gstreamer pipeline - from the source // Here we create all the parts of the gstreamer pipeline - from the source
// to the sink. The parts of the pipeline are split up into bins: // to the sink. The parts of the pipeline are split up into bins:
// uri decode bin -> audio bin // uri decode bin -> audio bin
@ -114,9 +129,6 @@ bool GstEnginePipeline::Init(const QUrl &url) {
// audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> equalizer -> // audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> equalizer ->
// volume -> audioscale -> audioconvert -> audiosink // volume -> audioscale -> audioconvert -> audiosink
// Decode bin
if (!ReplaceDecodeBin(url)) return false;
// Audio bin // Audio bin
audiobin_ = gst_bin_new("audiobin"); audiobin_ = gst_bin_new("audiobin");
gst_bin_add(GST_BIN(pipeline_), audiobin_); gst_bin_add(GST_BIN(pipeline_), audiobin_);
@ -192,10 +204,34 @@ bool GstEnginePipeline::Init(const QUrl &url) {
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
return true; return true;
} }
bool GstEnginePipeline::InitFromString(const QString& pipeline) {
pipeline_ = gst_pipeline_new("pipeline");
GstElement* new_bin = CreateDecodeBinFromString(pipeline.toAscii().constData());
if (!new_bin) {
return false;
}
if (!ReplaceDecodeBin(new_bin)) return false;
if (!Init()) return false;
return gst_element_link(new_bin, audiobin_);
}
bool GstEnginePipeline::InitFromUrl(const QUrl &url) {
pipeline_ = gst_pipeline_new("pipeline");
url_ = url;
// Decode bin
if (!ReplaceDecodeBin(url)) return false;
return Init();
}
GstEnginePipeline::~GstEnginePipeline() { GstEnginePipeline::~GstEnginePipeline() {
if (pipeline_) { if (pipeline_) {
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);

View File

@ -45,7 +45,8 @@ class GstEnginePipeline : public QObject {
void set_replaygain(bool enabled, int mode, float preamp, bool compression); void set_replaygain(bool enabled, int mode, float preamp, bool compression);
// Creates the pipeline, returns false on error // Creates the pipeline, returns false on error
bool Init(const QUrl& url); bool InitFromUrl(const QUrl& url);
bool InitFromString(const QString& pipeline);
// BufferConsumers get fed audio data. Thread-safe. // BufferConsumers get fed audio data. Thread-safe.
void AddBufferConsumer(BufferConsumer* consumer); void AddBufferConsumer(BufferConsumer* consumer);
@ -103,8 +104,12 @@ class GstEnginePipeline : public QObject {
void ElementMessageReceived(GstMessage*); void ElementMessageReceived(GstMessage*);
QString ParseTag(GstTagList* list, const char* tag) const; QString ParseTag(GstTagList* list, const char* tag) const;
bool Init();
GstElement* CreateDecodeBinFromString(const char* pipeline);
void UpdateVolume(); void UpdateVolume();
void UpdateEqualizer(); void UpdateEqualizer();
bool ReplaceDecodeBin(GstElement* new_bin);
bool ReplaceDecodeBin(const QUrl& url); bool ReplaceDecodeBin(const QUrl& url);
private slots: private slots:

View File

@ -247,13 +247,12 @@ void NowPlayingWidget::paintEvent(QPaintEvent *e) {
} }
void NowPlayingWidget::DrawContents(QPainter *p) { void NowPlayingWidget::DrawContents(QPainter *p) {
const int total_size = qMin(kMaxCoverSize, width());
if (hypnotoad_) {
p->drawPixmap(0, 0, total_size, total_size, hypnotoad_->currentPixmap());
return;
}
switch (mode_) { switch (mode_) {
case SmallSongDetails: case SmallSongDetails:
if (hypnotoad_) {
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, hypnotoad_->currentPixmap());
return;
}
// Draw the cover // Draw the cover
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, cover_); p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, cover_);
@ -264,6 +263,11 @@ void NowPlayingWidget::DrawContents(QPainter *p) {
break; break;
case LargeSongDetails: case LargeSongDetails:
const int total_size = qMin(kMaxCoverSize, width());
if (hypnotoad_) {
p->drawPixmap(0, 0, total_size, total_size, hypnotoad_->currentPixmap());
return;
}
const int x_offset = (width() - cover_height_) / 2; const int x_offset = (width() - cover_height_) / 2;
// Draw the black background // Draw the black background