Merge lp:~pwhelan/mixxx/shoutcast into lp:mixxx/1.7

Proposed by Phillip Whelan
Status: Merged
Approved by: Albert Santoni
Approved revision: 2453
Merge reported by: Albert Santoni
Merged at revision: not available
Proposed branch: lp:~pwhelan/mixxx/shoutcast
Merge into: lp:mixxx/1.7
Diff against target: None lines
To merge this branch: bzr merge lp:~pwhelan/mixxx/shoutcast
Reviewer Review Type Date Requested Status
Albert Santoni Approve
Review via email: mp+8518@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Phillip Whelan (pwhelan) wrote :

Several changes and bugfixes to Shoutcast, including:

  * Check in EngineShoutcast::writePage for the connection status
  * Better Tracking of the Active track for Metadata
  * Metadata Updates for Shoutcast support

I know 1.7 is feature frozen, but my previous changes were also accepted, as well, none of the binaries are rolled out with shoutcast support.

If there was a trunk branch I'd be proposing it to be merged there... but oh well...

lp:~pwhelan/mixxx/shoutcast updated
2452. By Phillip Whelan <madjester@voidwalker>

More stability changes for Shoutcast, as well as reseting the metadata lifetime
back to a sane value.

  * Check metadata every 32 invocations, as before.
  * Reconnect on server connect error
  * Reconnect on shout_send error

2453. By Phillip Whelan <madjester@voidwalker>

Comments for EngineShoutcast.

Revision history for this message
Albert Santoni (gamegod) wrote :

This looks like it includes your recent shoutcast-metadata-update.patch and shoutcast-scons-autodeps.patch patches, am I correct?

On lines 295-297 of the merge proposal, you convert QStrings to latin1-encoded char* strings. Does libshout support utf8? If so, use toUtf8() instead of toLatin1(). If not, leave it the way it is.

Just a heads up - In my sqlite branch, I have a real "Player" class which should probably deprecate the PlayerInfo class. It'll probably be my job to modify your code to use that since your code will be in trunk before mine. :)

Anyways, looks good otherwise. For sanity, I also think this should go in the 1.7 branch A) because that's where the latest shoutcast code is otherwise (it might save us some merging headaches later) and B) all of your changes only apply when Mixxx is built with shoutcast=1.

Thanks!

review: Needs Information
Revision history for this message
Phillip Whelan (pwhelan) wrote :

> Review: Needs Information
> This looks like it includes your recent shoutcast-metadata-update.patch
and
> shoutcast-scons-autodeps.patch patches, am I correct?

More for the most part, yes you are correct. I did extensive testing on the
metadata update code and fixed it to actually work. I was doing the
crossfader conditional totally wrong. I also changed the code to use
properly instantiated ControlObjectThread objects which are instantiated
once, instead of for each pass of the code which detects which is the
active track.

>
> On lines 295-297 of the merge proposal, you convert QStrings to
> latin1-encoded char* strings. Does libshout support utf8? If so, use
> toUtf8() instead of toLatin1(). If not, leave it the way it is.

I'll check into that, but I seriously doubt that libshout supports UTF-8,
and if so, Shoutcast might not support metadata in UTF-8.

> Just a heads up - In my sqlite branch, I have a real "Player" class which
> should probably deprecate the PlayerInfo class. It'll probably be my job
to
> modify your code to use that since your code will be in trunk before
mine.
> :)

That'd be great, on both accounts.

> Anyways, looks good otherwise. For sanity, I also think this should go in
> the 1.7 branch A) because that's where the latest shoutcast code is
> otherwise (it might save us some merging headaches later) and B) all of
> your changes only apply when Mixxx is built with shoutcast=1.

I also thought the same.

> Thanks!
Your welcome!

Revision history for this message
Albert Santoni (gamegod) :
review: Approve
Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

> For sanity, I also think this should go in the
> 1.7 branch A) because that's where the latest shoutcast code is otherwise (it
> might save us some merging headaches later) and B) all of your changes only
> apply when Mixxx is built with shoutcast=1.

Does that mean we should offer it as a new feature in 1.7.1? Or are we going to reserve point releases strictly for bug fixes?

Revision history for this message
Albert Santoni (gamegod) wrote :

For the time being, point releases are going to be strictly bug fix
releases. If you're doing feature development, it should stay in a
branch until it's ready to be merged into the 1.8 branch. The case of
madjesta's shoutcast code is special because we're transitioning to
this new development cycle still.

On Mon, Jul 13, 2009 at 7:59 PM, Pegasus<email address hidden> wrote:
>> For sanity, I also think this should go in the
>> 1.7 branch A) because that's where the latest shoutcast code is otherwise (it
>> might save us some merging headaches later) and B) all of your changes only
>> apply when Mixxx is built with shoutcast=1.
>
> Does that mean we should offer it as a new feature in 1.7.1? Or are we going to reserve point releases strictly for bug fixes?
> --
> https://code.launchpad.net/~pwhelan/mixxx/shoutcast/+merge/8518
> You are reviewing the proposed merge of lp:~pwhelan/mixxx/shoutcast into lp:mixxx/1.7.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/src/SConscript'
--- mixxx/src/SConscript 2009-07-07 21:55:55 +0000
+++ mixxx/src/SConscript 2009-07-08 03:00:57 +0000
@@ -872,16 +872,42 @@
872872
873#Experimental Shoutcast873#Experimental Shoutcast
874flags_shoutcast = getFlags(env, 'shoutcast', 0)874flags_shoutcast = getFlags(env, 'shoutcast', 0)
875
875if int(flags_shoutcast):876if int(flags_shoutcast):
876#TODO: check for libshout877 shoutmp3 = 0
877 env.Append(LIBS = 'shout');878 shoutogg = 0
878 env.Append(LIBS = 'vorbisenc');879
879 env.Append(LIBS = 'mp3lame');880 conf = Configure(env, custom_tests = { 'CheckForPKGConfig' : CheckForPKGConfig, 'CheckForPKG' : CheckForPKG })
881 conf.CheckLib('shout')
882
880 env.Append(CPPDEFINES = '__SHOUTCAST__')883 env.Append(CPPDEFINES = '__SHOUTCAST__')
881 sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp encodervorbis.cpp encodermp3.cpp""" )884
885 sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp """ )
886 build_flags += 'shoutcast '
887
888
889 if conf.CheckLib('mp3lame'):
890 env.Append(CPPDEFINES = '__SHOUTCAST_LAME__')
891 sources += Split(""" encodermp3.cpp """)
892 shoutmp3 = 1
893
894 if conf.CheckLib('vorbisenc'):
895 env.Append(CPPDEFINES = '__SHOUTCAST_VORBIS__')
896 sources += Split(""" encodervorbis.cpp """)
897 shoutogg = 1
898
899
900 if shoutmp3 and shoutogg:
901 print "Shoutcast support (OGG/MP3)... enabled"
902 elif shoutmp3:
903 print "Shoutcast support (MP3)... enabled"
904 elif shoutogg:
905 print "Shoutcast support (OGG)... enabled"
906 else:
907 print "Shoutcast support... enabled"
908
882 env.Uic4('dlgprefshoutcastdlg.ui')909 env.Uic4('dlgprefshoutcastdlg.ui')
883 print "Shoutcast support... enabled"910
884 build_flags += 'shoutcast '
885else:911else:
886 print "Shoutcast support... disabled"912 print "Shoutcast support... disabled"
887913
888914
=== 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-07-08 17:47:11 +0000
@@ -19,8 +19,13 @@
19#include "configobject.h"19#include "configobject.h"
20#include "dlgprefshoutcast.h"20#include "dlgprefshoutcast.h"
2121
22#ifdef __SHOUTCAST_VORBIS__
22#include "encodervorbis.h"23#include "encodervorbis.h"
24#endif // __SHOUTCAST_VORBIS__
25#ifdef __SHOUTCAST_LAME__
23#include "encodermp3.h"26#include "encodermp3.h"
27#endif // __SHOUTCAST_LAME__
28
24#include "playerinfo.h"29#include "playerinfo.h"
25#include "trackinfoobject.h"30#include "trackinfoobject.h"
2631
@@ -45,6 +50,10 @@
45 m_pConfig = _config;50 m_pConfig = _config;
46 m_pUpdateShoutcastFromPrefs = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(SHOUTCAST_PREF_KEY, "update_from_prefs")));51 m_pUpdateShoutcastFromPrefs = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(SHOUTCAST_PREF_KEY, "update_from_prefs")));
47 52
53 m_pCrossfader = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Master]","crossfader")));
54 m_pVolume1 = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Channel1]","volume")));
55 m_pVolume2 = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Channel2]","volume")));
56
48 QByteArray baBitrate = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"bitrate")).toLatin1();57 QByteArray baBitrate = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"bitrate")).toLatin1();
49 QByteArray baFormat = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"format")).toLatin1();58 QByteArray baFormat = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"format")).toLatin1();
50 int len;59 int len;
@@ -58,6 +67,15 @@
58 return;67 return;
59 }68 }
60 69
70 if (!(m_pShoutMetaData = shout_metadata_new())) {
71 qDebug() << "Cound not allocate shout_metadata_t";
72 return;
73 }
74
75 // set to a high number to automatically update the metadata
76 // on the first change
77 m_pMetaDataLife = 31337;
78
61 //Initialize the m_pShout structure with the info from Mixxx's shoutcast preferences.79 //Initialize the m_pShout structure with the info from Mixxx's shoutcast preferences.
62 updateFromPreferences();80 updateFromPreferences();
6381
@@ -67,7 +85,8 @@
67 }85 }
68 86
69 qDebug("********START SERVERCONNECT*******");87 qDebug("********START SERVERCONNECT*******");
70 serverConnect();88 if ( !serverConnect())
89 return;
71 90
72 if (( len = baBitrate.indexOf(' ')) != -1) {91 if (( len = baBitrate.indexOf(' ')) != -1) {
73 baBitrate.resize(len);92 baBitrate.resize(len);
@@ -75,10 +94,20 @@
75 94
76 // Initialize encoder95 // Initialize encoder
77 if ( ! qstrcmp(baFormat, "MP3")) {96 if ( ! qstrcmp(baFormat, "MP3")) {
97#ifdef __SHOUTCAST_LAME__
78 encoder = new EncoderMp3(m_pConfig, this);98 encoder = new EncoderMp3(m_pConfig, this);
99#else
100 qDebug() << "*** Missing MP3 Encoder Support";
101 return;
102#endif // __SHOUTCAST_LAME__
79 }103 }
80 else if ( ! qstrcmp(baFormat, "Ogg Vorbis")) {104 else if ( ! qstrcmp(baFormat, "Ogg Vorbis")) {
105#ifdef __SHOUTCAST_VORBIS__
81 encoder = new EncoderVorbis(m_pConfig, this);106 encoder = new EncoderVorbis(m_pConfig, this);
107#else
108 qDebug() << "*** Missing OGG Vorbis Encoder Support";
109 return;
110#endif // __SHOUTCAST_VORBIS__
82 }111 }
83 else {112 else {
84 qDebug() << "**** Unknown Encoder Format";113 qDebug() << "**** Unknown Encoder Format";
@@ -95,7 +124,12 @@
95{124{
96 delete encoder;125 delete encoder;
97 delete m_pUpdateShoutcastFromPrefs;126 delete m_pUpdateShoutcastFromPrefs;
127 delete m_pCrossfader;
128 delete m_pVolume1;
129 delete m_pVolume2;
98 130
131 if (m_pShoutMetaData)
132 shout_metadata_free(m_pShoutMetaData);
99 if (m_pShout)133 if (m_pShout)
100 shout_close(m_pShout);134 shout_close(m_pShout);
101 shout_shutdown();135 shout_shutdown();
@@ -205,7 +239,7 @@
205 239
206}240}
207241
208void EngineShoutcast::serverConnect()242bool EngineShoutcast::serverConnect()
209{243{
210 qDebug("in serverConnect();");244 qDebug("in serverConnect();");
211 if (m_pShout)245 if (m_pShout)
@@ -223,7 +257,10 @@
223 }257 }
224 if (m_iShoutStatus == SHOUTERR_CONNECTED) {258 if (m_iShoutStatus == SHOUTERR_CONNECTED) {
225 qDebug() << "***********Connected to Shoutcast server...";259 qDebug() << "***********Connected to Shoutcast server...";
260 return true;
226 }261 }
262
263 return false;
227}264}
228265
229void EngineShoutcast::writePage(unsigned char *header, unsigned char *body,266void EngineShoutcast::writePage(unsigned char *header, unsigned char *body,
@@ -259,15 +296,128 @@
259 }296 }
260}297}
261298
262/*void EngineShoutcast::wrapper2writePage(void *pObj, unsigned char *header, unsigned char *body,299void EngineShoutcast::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize)
263 int headerLen, int bodyLen)300{
264{301 if (m_iShoutStatus != SHOUTERR_CONNECTED)
265 EngineShoutcast* mySelf = (EngineShoutcast*)pObj;302 return;
266 pObj->writePage(header, body, headerLen, bodyLen);303
267}*/
268
269void EngineShoutcast::process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize)
270{
271// encoder->encodeBuffer((void*) &objA, EngineShoutcast::wrapper2writePage, pOut, iBufferSize);
272 if (iBufferSize > 0) encoder->encodeBuffer(pOut, iBufferSize);304 if (iBufferSize > 0) encoder->encodeBuffer(pOut, iBufferSize);
305
306 if ( metaDataHasChanged())
307 updateMetaData();
308}
309
310/* Algorithm which simply flips the lowest and/or second lowest bits,
311 * bits 1 and 2, to represent which track is active and returns the result.
312 */
313
314int EngineShoutcast::getActiveTracks()
315{
316 int tracks = 0;
317
318
319 if (ControlObject::getControl(ConfigKey("[Channel1]","play"))->get()==1.) tracks |= 1;
320 if (ControlObject::getControl(ConfigKey("[Channel2]","play"))->get()==1.) tracks |= 2;
321
322 if (tracks == 0)
323 return 0;
324
325 // Detect the dominant track by checking the crossfader and volume levels
326 if ((tracks & 1) && (tracks & 2)) {
327
328 if ((m_pVolume1->get() == 0) && (m_pVolume2->get() == 0))
329 return 0;
330
331 if (m_pVolume2->get() == 0) {
332 tracks = 1;
333 }
334 else if ( m_pVolume1->get() == 0) {
335 tracks = 2;
336 }
337 // allow a bit of leeway with the crossfader
338 else if ((m_pCrossfader->get() < 0.05) && (m_pCrossfader->get() > -0.05)) {
339
340 if (m_pVolume1->get() > m_pVolume2->get()) {
341 tracks = 1;
342 }
343 else if (m_pVolume1->get() < m_pVolume2->get()) {
344 tracks = 2;
345 }
346
347 }
348 else if ( m_pCrossfader->get() < -0.05 ) {
349 tracks = 1;
350 }
351 else if ( m_pCrossfader->get() > 0.05 ) {
352 tracks = 2;
353 }
354
355 }
356
357 return tracks;
358}
359
360bool EngineShoutcast::metaDataHasChanged()
361{
362 int tracks;
363 TrackInfoObject *newMetaData;
364 bool changed = false;
365
366
367 if ( m_pMetaDataLife < 1 ) {
368 m_pMetaDataLife++;
369 return false;
370 }
371
372 m_pMetaDataLife = 0;
373
374
375 tracks = getActiveTracks();
376
377
378 switch (tracks)
379 {
380 case 0:
381 // no tracks are playing
382 // we should set the metadata to nothing
383 break;
384 case 1:
385 // track 1 is active
386
387 newMetaData = PlayerInfo::Instance().getTrackInfo(1);
388 if (newMetaData != m_pMetaData)
389 {
390 m_pMetaData = newMetaData;
391 changed = true;
392 }
393 break;
394 case 2:
395 // track 2 is active
396 newMetaData = PlayerInfo::Instance().getTrackInfo(2);
397 if (newMetaData != m_pMetaData)
398 {
399 m_pMetaData = newMetaData;
400 changed = true;
401 }
402 break;
403 case 3:
404 // both tracks are active, just stick with it for now
405 break;
406 }
407
408 qDebug() << "tracks = " << tracks << " changed = " << changed;
409
410
411 return changed;
412}
413
414void EngineShoutcast::updateMetaData()
415{
416 // convert QStrings to char*s
417 QByteArray baArtist = m_pMetaData->getArtist().toLatin1();
418 QByteArray baTitle = m_pMetaData->getTitle().toLatin1();
419 QByteArray baSong = baArtist + " - " + baTitle;
420
421 shout_metadata_add(m_pShoutMetaData, "song", baSong.data());
422 shout_set_metadata(m_pShout, m_pShoutMetaData);
273}423}
274424
=== 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-07-08 03:01:35 +0000
@@ -51,14 +51,23 @@
51// void writePage(unsigned char *header, unsigned char *body,51// void writePage(unsigned char *header, unsigned char *body,
52// int headerLen, int bodyLen, int count);52// int headerLen, int bodyLen, int count);
53private:53private:
54 void serverConnect();54 bool serverConnect();
55 int getActiveTracks();
56 bool metaDataHasChanged();
57 void updateMetaData();
58 TrackInfoObject *m_pMetaData;
55 shout_t *m_pShout;59 shout_t *m_pShout;
60 shout_metadata_t *m_pShoutMetaData;
61 int m_pMetaDataLife;
56 long m_iShoutStatus;62 long m_iShoutStatus;
57 ConfigObject<ConfigValue> *m_pConfig;63 ConfigObject<ConfigValue> *m_pConfig;
58 ControlObject* recReady;64 ControlObject* recReady;
59 Encoder *encoder;65 Encoder *encoder;
60 ControlObjectThreadMain* m_pUpdateShoutcastFromPrefs;66 ControlObjectThreadMain* m_pUpdateShoutcastFromPrefs;
61// void (*writeFn)(unsigned char *, unsigned char *, int, int);67// void (*writeFn)(unsigned char *, unsigned char *, int, int);
68 ControlObjectThread* m_pCrossfader;
69 ControlObjectThread* m_pVolume1;
70 ControlObjectThread* m_pVolume2;
62};71};
6372
64#endif73#endif

Subscribers

People subscribed via source and target branches