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
1=== modified file 'mixxx/src/SConscript'
2--- mixxx/src/SConscript 2009-07-07 21:55:55 +0000
3+++ mixxx/src/SConscript 2009-07-08 03:00:57 +0000
4@@ -872,16 +872,42 @@
5
6 #Experimental Shoutcast
7 flags_shoutcast = getFlags(env, 'shoutcast', 0)
8+
9 if int(flags_shoutcast):
10-#TODO: check for libshout
11- env.Append(LIBS = 'shout');
12- env.Append(LIBS = 'vorbisenc');
13- env.Append(LIBS = 'mp3lame');
14+ shoutmp3 = 0
15+ shoutogg = 0
16+
17+ conf = Configure(env, custom_tests = { 'CheckForPKGConfig' : CheckForPKGConfig, 'CheckForPKG' : CheckForPKG })
18+ conf.CheckLib('shout')
19+
20 env.Append(CPPDEFINES = '__SHOUTCAST__')
21- sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp encodervorbis.cpp encodermp3.cpp""" )
22+
23+ sources += Split(""" dlgprefshoutcast.cpp engine/engineshoutcast.cpp encoder.cpp """ )
24+ build_flags += 'shoutcast '
25+
26+
27+ if conf.CheckLib('mp3lame'):
28+ env.Append(CPPDEFINES = '__SHOUTCAST_LAME__')
29+ sources += Split(""" encodermp3.cpp """)
30+ shoutmp3 = 1
31+
32+ if conf.CheckLib('vorbisenc'):
33+ env.Append(CPPDEFINES = '__SHOUTCAST_VORBIS__')
34+ sources += Split(""" encodervorbis.cpp """)
35+ shoutogg = 1
36+
37+
38+ if shoutmp3 and shoutogg:
39+ print "Shoutcast support (OGG/MP3)... enabled"
40+ elif shoutmp3:
41+ print "Shoutcast support (MP3)... enabled"
42+ elif shoutogg:
43+ print "Shoutcast support (OGG)... enabled"
44+ else:
45+ print "Shoutcast support... enabled"
46+
47 env.Uic4('dlgprefshoutcastdlg.ui')
48- print "Shoutcast support... enabled"
49- build_flags += 'shoutcast '
50+
51 else:
52 print "Shoutcast support... disabled"
53
54
55=== modified file 'mixxx/src/engine/engineshoutcast.cpp'
56--- mixxx/src/engine/engineshoutcast.cpp 2009-02-18 06:27:27 +0000
57+++ mixxx/src/engine/engineshoutcast.cpp 2009-07-08 17:47:11 +0000
58@@ -19,8 +19,13 @@
59 #include "configobject.h"
60 #include "dlgprefshoutcast.h"
61
62+#ifdef __SHOUTCAST_VORBIS__
63 #include "encodervorbis.h"
64+#endif // __SHOUTCAST_VORBIS__
65+#ifdef __SHOUTCAST_LAME__
66 #include "encodermp3.h"
67+#endif // __SHOUTCAST_LAME__
68+
69 #include "playerinfo.h"
70 #include "trackinfoobject.h"
71
72@@ -45,6 +50,10 @@
73 m_pConfig = _config;
74 m_pUpdateShoutcastFromPrefs = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(SHOUTCAST_PREF_KEY, "update_from_prefs")));
75
76+ m_pCrossfader = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Master]","crossfader")));
77+ m_pVolume1 = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Channel1]","volume")));
78+ m_pVolume2 = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Channel2]","volume")));
79+
80 QByteArray baBitrate = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"bitrate")).toLatin1();
81 QByteArray baFormat = m_pConfig->getValueString(ConfigKey(SHOUTCAST_PREF_KEY,"format")).toLatin1();
82 int len;
83@@ -58,6 +67,15 @@
84 return;
85 }
86
87+ if (!(m_pShoutMetaData = shout_metadata_new())) {
88+ qDebug() << "Cound not allocate shout_metadata_t";
89+ return;
90+ }
91+
92+ // set to a high number to automatically update the metadata
93+ // on the first change
94+ m_pMetaDataLife = 31337;
95+
96 //Initialize the m_pShout structure with the info from Mixxx's shoutcast preferences.
97 updateFromPreferences();
98
99@@ -67,7 +85,8 @@
100 }
101
102 qDebug("********START SERVERCONNECT*******");
103- serverConnect();
104+ if ( !serverConnect())
105+ return;
106
107 if (( len = baBitrate.indexOf(' ')) != -1) {
108 baBitrate.resize(len);
109@@ -75,10 +94,20 @@
110
111 // Initialize encoder
112 if ( ! qstrcmp(baFormat, "MP3")) {
113+#ifdef __SHOUTCAST_LAME__
114 encoder = new EncoderMp3(m_pConfig, this);
115+#else
116+ qDebug() << "*** Missing MP3 Encoder Support";
117+ return;
118+#endif // __SHOUTCAST_LAME__
119 }
120 else if ( ! qstrcmp(baFormat, "Ogg Vorbis")) {
121+#ifdef __SHOUTCAST_VORBIS__
122 encoder = new EncoderVorbis(m_pConfig, this);
123+#else
124+ qDebug() << "*** Missing OGG Vorbis Encoder Support";
125+ return;
126+#endif // __SHOUTCAST_VORBIS__
127 }
128 else {
129 qDebug() << "**** Unknown Encoder Format";
130@@ -95,7 +124,12 @@
131 {
132 delete encoder;
133 delete m_pUpdateShoutcastFromPrefs;
134+ delete m_pCrossfader;
135+ delete m_pVolume1;
136+ delete m_pVolume2;
137
138+ if (m_pShoutMetaData)
139+ shout_metadata_free(m_pShoutMetaData);
140 if (m_pShout)
141 shout_close(m_pShout);
142 shout_shutdown();
143@@ -205,7 +239,7 @@
144
145 }
146
147-void EngineShoutcast::serverConnect()
148+bool EngineShoutcast::serverConnect()
149 {
150 qDebug("in serverConnect();");
151 if (m_pShout)
152@@ -223,7 +257,10 @@
153 }
154 if (m_iShoutStatus == SHOUTERR_CONNECTED) {
155 qDebug() << "***********Connected to Shoutcast server...";
156+ return true;
157 }
158+
159+ return false;
160 }
161
162 void EngineShoutcast::writePage(unsigned char *header, unsigned char *body,
163@@ -259,15 +296,128 @@
164 }
165 }
166
167-/*void EngineShoutcast::wrapper2writePage(void *pObj, unsigned char *header, unsigned char *body,
168- int headerLen, int bodyLen)
169-{
170- EngineShoutcast* mySelf = (EngineShoutcast*)pObj;
171- pObj->writePage(header, body, headerLen, bodyLen);
172-}*/
173-
174-void EngineShoutcast::process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize)
175-{
176-// encoder->encodeBuffer((void*) &objA, EngineShoutcast::wrapper2writePage, pOut, iBufferSize);
177+void EngineShoutcast::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize)
178+{
179+ if (m_iShoutStatus != SHOUTERR_CONNECTED)
180+ return;
181+
182 if (iBufferSize > 0) encoder->encodeBuffer(pOut, iBufferSize);
183+
184+ if ( metaDataHasChanged())
185+ updateMetaData();
186+}
187+
188+/* Algorithm which simply flips the lowest and/or second lowest bits,
189+ * bits 1 and 2, to represent which track is active and returns the result.
190+ */
191+
192+int EngineShoutcast::getActiveTracks()
193+{
194+ int tracks = 0;
195+
196+
197+ if (ControlObject::getControl(ConfigKey("[Channel1]","play"))->get()==1.) tracks |= 1;
198+ if (ControlObject::getControl(ConfigKey("[Channel2]","play"))->get()==1.) tracks |= 2;
199+
200+ if (tracks == 0)
201+ return 0;
202+
203+ // Detect the dominant track by checking the crossfader and volume levels
204+ if ((tracks & 1) && (tracks & 2)) {
205+
206+ if ((m_pVolume1->get() == 0) && (m_pVolume2->get() == 0))
207+ return 0;
208+
209+ if (m_pVolume2->get() == 0) {
210+ tracks = 1;
211+ }
212+ else if ( m_pVolume1->get() == 0) {
213+ tracks = 2;
214+ }
215+ // allow a bit of leeway with the crossfader
216+ else if ((m_pCrossfader->get() < 0.05) && (m_pCrossfader->get() > -0.05)) {
217+
218+ if (m_pVolume1->get() > m_pVolume2->get()) {
219+ tracks = 1;
220+ }
221+ else if (m_pVolume1->get() < m_pVolume2->get()) {
222+ tracks = 2;
223+ }
224+
225+ }
226+ else if ( m_pCrossfader->get() < -0.05 ) {
227+ tracks = 1;
228+ }
229+ else if ( m_pCrossfader->get() > 0.05 ) {
230+ tracks = 2;
231+ }
232+
233+ }
234+
235+ return tracks;
236+}
237+
238+bool EngineShoutcast::metaDataHasChanged()
239+{
240+ int tracks;
241+ TrackInfoObject *newMetaData;
242+ bool changed = false;
243+
244+
245+ if ( m_pMetaDataLife < 1 ) {
246+ m_pMetaDataLife++;
247+ return false;
248+ }
249+
250+ m_pMetaDataLife = 0;
251+
252+
253+ tracks = getActiveTracks();
254+
255+
256+ switch (tracks)
257+ {
258+ case 0:
259+ // no tracks are playing
260+ // we should set the metadata to nothing
261+ break;
262+ case 1:
263+ // track 1 is active
264+
265+ newMetaData = PlayerInfo::Instance().getTrackInfo(1);
266+ if (newMetaData != m_pMetaData)
267+ {
268+ m_pMetaData = newMetaData;
269+ changed = true;
270+ }
271+ break;
272+ case 2:
273+ // track 2 is active
274+ newMetaData = PlayerInfo::Instance().getTrackInfo(2);
275+ if (newMetaData != m_pMetaData)
276+ {
277+ m_pMetaData = newMetaData;
278+ changed = true;
279+ }
280+ break;
281+ case 3:
282+ // both tracks are active, just stick with it for now
283+ break;
284+ }
285+
286+ qDebug() << "tracks = " << tracks << " changed = " << changed;
287+
288+
289+ return changed;
290+}
291+
292+void EngineShoutcast::updateMetaData()
293+{
294+ // convert QStrings to char*s
295+ QByteArray baArtist = m_pMetaData->getArtist().toLatin1();
296+ QByteArray baTitle = m_pMetaData->getTitle().toLatin1();
297+ QByteArray baSong = baArtist + " - " + baTitle;
298+
299+ shout_metadata_add(m_pShoutMetaData, "song", baSong.data());
300+ shout_set_metadata(m_pShout, m_pShoutMetaData);
301 }
302
303=== modified file 'mixxx/src/engine/engineshoutcast.h'
304--- mixxx/src/engine/engineshoutcast.h 2009-02-18 04:28:21 +0000
305+++ mixxx/src/engine/engineshoutcast.h 2009-07-08 03:01:35 +0000
306@@ -51,14 +51,23 @@
307 // void writePage(unsigned char *header, unsigned char *body,
308 // int headerLen, int bodyLen, int count);
309 private:
310- void serverConnect();
311+ bool serverConnect();
312+ int getActiveTracks();
313+ bool metaDataHasChanged();
314+ void updateMetaData();
315+ TrackInfoObject *m_pMetaData;
316 shout_t *m_pShout;
317+ shout_metadata_t *m_pShoutMetaData;
318+ int m_pMetaDataLife;
319 long m_iShoutStatus;
320 ConfigObject<ConfigValue> *m_pConfig;
321 ControlObject* recReady;
322 Encoder *encoder;
323 ControlObjectThreadMain* m_pUpdateShoutcastFromPrefs;
324 // void (*writeFn)(unsigned char *, unsigned char *, int, int);
325+ ControlObjectThread* m_pCrossfader;
326+ ControlObjectThread* m_pVolume1;
327+ ControlObjectThread* m_pVolume2;
328 };
329
330 #endif

Subscribers

People subscribed via source and target branches