> I think Sean commited most of the patches, though I still have some
> changes in my working copy, mostly involving metadata updating. I'll
> send those to you later today.
Here's another patch. This one includes:
* SConscript code to optionally compile with shoutcast support for
OGG and/or MP3 (needs at least one)
* Update metadata for shoutcast servers
* Improved metadata selection code
The only thing missing, I think, is to add a little leeway with the
crossfader metadata selection code, since some MIDI controllers, like
my M-Audio X-Session, don't have a slot or nitch at 0. This could
easily lead to false positives. It also might be useful to add a
little more inteligence to this code to detect if people are merely
scratching with a certain track.
--
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS d- s+:- a-- C+++(++++)$ UL+++$ P+ L+++>++++ E W++ N o?
K- w++(---) O? M-- V PS+++ PE-(--) Y+(++) PGP t(+) 5(+) X++
R b+>++ DI+ D+ G e h-(--) r y+
------END GEEK CODE BLOCK------
=== modified file 'mixxx/src/SConscript'
--- mixxx/src/SConscript 2009-05-15 03:08:59 +0000
+++ mixxx/src/SConscript 2009-05-22 15:53:18 +0000
@@ -211,6 +211,8 @@
opts.Add('ffmpeg', '(EXPERIMENTAL) Set to 1 to enable FFMPEG support', 0)
opts.Add('vinylcontrol', 'Set to 1 to enable vinyl control support', 1)
opts.Add('shoutcast', 'Set to 1 to enable shoutcast support', 0)
+opts.Add('shoutcastlame', 'Set to 1 to enable mp3 support in shoutcast using libmp3lame', 0)
+opts.Add('shoutcastvorbis', 'Set to 1 to enable ogg vorbis support in shoutcast using libvorbisenc', 0)
opts.Add('msvshacks', 'Set to 1 to build properly with MS Visual Studio 2005 (Express users should leave this off)', 0)
opts.Add('cmetrics', 'Set to 1 to enable crash reporting/usage statistics via Case Metrics (This should be disabled on development builds)', 0)
opts.Add('optimize', 'Set to 1 to enable -O3 compiler optimizations. Set to 2 to enable Pentium 4 optimizations. Set to 3 to enable Intel Core optimizations, and set to 4 to enable Intel Core 2 optimizations.', 1)
@@ -854,16 +856,40 @@
#Experimental Shoutcast
flags_shoutcast = getFlags(env, 'shoutcast', 0)
+flags_shoutcastlame = getFlags(env, 'shoutcastlame', 0)
+flags_shoutcastvorbis = getFlags(env, 'shoutcastvorbis', 0)
+
if int(flags_shoutcast):
#TODO: check for libshout
- env.Append(LIBS = 'shout');
- env.Append(LIBS = 'vorbisenc');
- env.Append(LIBS = 'mp3lame');
- env.Append(CPPDEFINES = '__SHOUTCAST__')
- sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp encodervorbis.cpp encodermp3.cpp""" )
- env.Uic4('dlgprefshoutcastdlg.ui')
- print "Shoutcast support... enabled"
- build_flags += 'shoutcast '
+ if int(flags_shoutcastlame) or int(flags_shoutcastvorbis):
+ env.Append(LIBS = 'shout');
+ env.Append(CPPDEFINES = '__SHOUTCAST__')
+ sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp """ )
+ build_flags += 'shoutcast '
+
+ if int(flags_shoutcastlame):
+ env.Append(LIBS = 'mp3lame');
+ env.Append(CPPDEFINES = '__SHOUTCAST_LAME__')
+ sources += Split(""" encodermp3.cpp """)
+ build_flags += 'shoutcastlame '
+
+ if int(flags_shoutcastvorbis):
+ env.Append(LIBS = 'vorbisenc');
+ env.Append(CPPDEFINES = '__SHOUTCAST_VORBIS__')
+ sources += Split(""" encodervorbis.cpp """)
+ build_flags += 'shoutcastvorbis '
+
+ if int(flags_shoutcastlame) and int(flags_shoutcastvorbis):
+ print "Shoutcast support (OGG/MP3)... enabled"
+ elif int(flags_shoutcastlame):
+ print "Shoutcast support (MP3)... enabled"
+ else:
+ print "Shoutcast support (OGG)... enabled"
+
+ env.Uic4('dlgprefshoutcastdlg.ui')
+ else:
+ print "Shoutcast support disabled... no supported encoders"
+
else:
print "Shoutcast support... disabled"
=== modified file 'mixxx/src/engine/engineshoutcast.cpp'
--- mixxx/src/engine/engineshoutcast.cpp 2009-02-18 06:27:27 +0000
+++ mixxx/src/engine/engineshoutcast.cpp 2009-05-22 15:20:36 +0000
@@ -19,8 +19,13 @@
#include "configobject.h"
#include "dlgprefshoutcast.h"
+#ifdef __SHOUTCAST_VORBIS__
#include "encodervorbis.h"
+#endif // __SHOUTCAST_VORBIS__
+#ifdef __SHOUTCAST_LAME__
#include "encodermp3.h"
+#endif // __SHOUTCAST_LAME__
+
#include "playerinfo.h"
#include "trackinfoobject.h"
@@ -58,6 +63,15 @@
return;
}
+ if (!(m_pShoutMetaData = shout_metadata_new())) {
+ qDebug() << "Cound not allocate shout_metadata_t";
+ return;
+ }
+
+ // set to a high number to automatically update the metadata
+ // on the first change
+ m_pMetaDataLife = 31337;
+
//Initialize the m_pShout structure with the info from Mixxx's shoutcast preferences.
updateFromPreferences();
@@ -75,10 +89,20 @@
// Initialize encoder
if ( ! qstrcmp(baFormat, "MP3")) {
+#ifdef __SHOUTCAST_LAME__
encoder = new EncoderMp3(m_pConfig, this);
+#else
+ qDebug() << "*** Missing MP3 Encoder Support";
+ return;
+#endif // __SHOUTCAST_LAME__
}
else if ( ! qstrcmp(baFormat, "Ogg Vorbis")) {
+#ifdef __SHOUTCAST_VORBIS__
encoder = new EncoderVorbis(m_pConfig, this);
+#else
+ qDebug() << "*** Missing OGG Vorbis Encoder Support";
+ return;
+#endif // __SHOUTCAST_VORBIS__
}
else {
qDebug() << "**** Unknown Encoder Format";
@@ -96,6 +120,8 @@
delete encoder;
delete m_pUpdateShoutcastFromPrefs;
+ if (m_pShoutMetaData)
+ shout_metadata_free(m_pShoutMetaData);
if (m_pShout)
shout_close(m_pShout);
shout_shutdown();
@@ -259,15 +285,130 @@
}
}
-/*void EngineShoutcast::wrapper2writePage(void *pObj, unsigned char *header, unsigned char *body,
- int headerLen, int bodyLen)
-{
- EngineShoutcast* mySelf = (EngineShoutcast*)pObj;
- pObj->writePage(header, body, headerLen, bodyLen);
-}*/
-
-void EngineShoutcast::process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize)
-{
-// encoder->encodeBuffer((void*) &objA, EngineShoutcast::wrapper2writePage, pOut, iBufferSize);
+void EngineShoutcast::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize)
+{
if (iBufferSize > 0) encoder->encodeBuffer(pOut, iBufferSize);
+
+ if ( metaDataHasChanged())
+ updateMetaData();
+}
+
+/* Algorithm which simply flips the lowest and/or second lowest bits,
+ * bits 1 and 2, to represent which track is active and returns the result.
+ */
+
+int EngineShoutcast::getActiveTracks()
+{
+ int tracks = 0;
+
+
+ if (ControlObject::getControl(ConfigKey("[Channel1]","play"))->get()==1.) tracks |= 1;
+ if (ControlObject::getControl(ConfigKey("[Channel2]","play"))->get()==1.) tracks |= 2;
+
+ if (tracks == 0)
+ return 0;
+
+ // Detect the dominant track by checking the crossfader and volume levels
+ if ((tracks & 1) && (tracks && 2)) {
+ ControlObjectThreadMain* pCrossfader = new ControlObjectThreadMain(
+ ControlObject::getControl(ConfigKey(
+ "[Master]","crossfader")));
+
+ // allow a bit of leeway with the crossfader
+ if ( pCrossfader->get() < 0.0001 ) {
+ tracks = 1;
+ }
+ else if ( pCrossfader->get() > 0.0001 ) {
+ tracks = 2;
+ }
+ else {
+ ControlObjectThreadMain* pVolume1 = new ControlObjectThreadMain(
+ ControlObject::getControl(ConfigKey(
+ "[Channel1]","volume")));
+
+ ControlObjectThreadMain* pVolume2 = new ControlObjectThreadMain(
+ ControlObject::getControl(ConfigKey(
+ "[Channel2]","volume")));
+
+ if (pVolume1->get() > pVolume2->get()) {
+ tracks = 1;
+ }
+ else if (pVolume1->get() < pVolume2->get()) {
+ tracks = 2;
+ }
+
+ delete pVolume1;
+ delete pVolume2;
+ }
+
+ delete pCrossfader;
+
+ }
+
+ return tracks;
+}
+
+bool EngineShoutcast::metaDataHasChanged()
+{
+ int tracks;
+ TrackInfoObject *newMetaData;
+ bool changed = false;
+
+
+ if ( m_pMetaDataLife < 32 ) {
+ m_pMetaDataLife++;
+ return false;
+ }
+
+ m_pMetaDataLife = 0;
+
+
+ tracks = getActiveTracks();
+
+
+ switch (tracks)
+ {
+ case 0:
+ // no tracks are playing
+ // we should set the metadata to nothing
+ break;
+ case 1:
+ // track 1 is active
+
+ newMetaData = PlayerInfo::Instance().getTrackInfo(1);
+ if (newMetaData != m_pMetaData)
+ {
+ m_pMetaData = newMetaData;
+ changed = true;
+ }
+ break;
+ case 2:
+ // track 2 is active
+ newMetaData = PlayerInfo::Instance().getTrackInfo(2);
+ if (newMetaData != m_pMetaData)
+ {
+ m_pMetaData = newMetaData;
+ changed = true;
+ }
+ break;
+ case 3:
+ // both tracks are active, just stick with it for now
+ break;
+ }
+
+ qDebug() << "tracks = " << tracks << " changed = " << changed;
+
+
+ return changed;
+}
+
+void EngineShoutcast::updateMetaData()
+{
+ // convert QStrings to char*s
+ QByteArray baArtist = m_pMetaData->getArtist().toLatin1();
+ QByteArray baTitle = m_pMetaData->getTitle().toLatin1();
+ QByteArray baSong = baArtist + " - " + baTitle;
+
+ shout_metadata_add(m_pShoutMetaData, "song", baSong.data());
+ shout_set_metadata(m_pShout, m_pShoutMetaData);
}
=== modified file 'mixxx/src/engine/engineshoutcast.h'
--- mixxx/src/engine/engineshoutcast.h 2009-02-18 04:28:21 +0000
+++ mixxx/src/engine/engineshoutcast.h 2009-04-08 11:59:16 +0000
@@ -52,7 +52,13 @@
// int headerLen, int bodyLen, int count);
private:
void serverConnect();
+ int getActiveTracks();
+ bool metaDataHasChanged();
+ void updateMetaData();
+ TrackInfoObject *m_pMetaData;
shout_t *m_pShout;
+ shout_metadata_t *m_pShoutMetaData;
+ int m_pMetaDataLife;
long m_iShoutStatus;
ConfigObject<ConfigValue> *m_pConfig;
ControlObject* recReady;
------------------------------------------------------------------------------
Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT
is a gathering of tech-side developers & brand creativity professionals. Meet
the minds behind Google Creative Lab, Visual Complexity, Processing, &
iPhoneDevCamp asthey present alongside digital heavyweights like Barbarian
Group, R/GA, & Big Spaceship. http://www.creativitycat.com
_______________________________________________
Mixxx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mixxx-devel