--- a/sc2src/sc2mpd.cpp
+++ b/sc2src/sc2mpd.cpp
@@ -46,6 +46,7 @@
 #include "conftree.h"
 #include "chrono.h"
 #include "watcher.h"
+#include "songcastreceiver.h"
 
 #ifdef WITH_WAVSC2
 #include "openaudio.h"
@@ -113,192 +114,30 @@
     }
 }
 
-using namespace OpenHome;
-using namespace OpenHome::Net;
-using namespace OpenHome::TestFramework;
-using namespace OpenHome::Av;
-
-class OhmReceiverDriver : public IOhmReceiverDriver, public IOhmMsgProcessor {
-public:
-    OhmReceiverDriver(AudioEater* eater, AudioEater::Context *ctxt);
-
-    virtual TBool IsConnected();
-private:
-    // IOhmReceiverDriver
-    virtual void Add(OhmMsg& aMsg);
-    virtual void Timestamp(OhmMsg& aMsg);
-    virtual void Started();
-    virtual void Connected();
-    virtual void Playing();
-    virtual void Disconnected();
-    virtual void Stopped();
-
-    // IOhmMsgProcessor
-    virtual void Process(OhmMsgAudio& aMsg);
-    virtual void Process(OhmMsgTrack& aMsg);
-    virtual void Process(OhmMsgMetatext& aMsg);
-
-private:
-    // Debug, stats, etc while we get to understand the Songcast streams
-    class Observer {
-    public:
-        TBool iReset;
-        TUint iCount;
-        TUint iFrame;
-        int dumpfd;
-        Chrono chron;
-        Observer() : iReset(true), iCount(0), iFrame(0), dumpfd(-1) {
-#if 0
-            dumpfd = 
-                open("/y/av/tmp/sc2dump", O_WRONLY|O_CREAT|O_TRUNC, 0666);
-            if (dumpfd < 0) {
-                LOGERR("OhmReceiverDriver::Open dump file failed\n");
-            }
-#endif
-        }
-
-        void reset() {
-            iReset = true;
-        }
-
-        void process(OhmMsgAudio& aMsg);
-    };
-    Observer m_obs;
-    AudioEater *m_eater;
-    TBool iConnected;
-};
-
-OhmReceiverDriver::OhmReceiverDriver(AudioEater *eater, 
-                                     AudioEater::Context *ctxt)
-    : m_eater(eater), iConnected(false)
-{
-    audioqueue.start(1, m_eater->worker, ctxt);
-}
-
-void OhmReceiverDriver::Add(OhmMsg& aMsg)
-{
-    aMsg.Process(*this);
-    aMsg.RemoveRef();
-}
-
-void OhmReceiverDriver::Timestamp(OhmMsg& /*aMsg*/)
-{
-}
-
-void OhmReceiverDriver::Started()
-{
-    LOGDEB("=== STARTED ====\n");
-}
-
-void OhmReceiverDriver::Connected()
-{
-    m_obs.reset();
-    printf("CONNECTED\n");
-    fflush(stdout);
-    LOGDEB("=== CONNECTED ====\n");
-    iConnected = true;
-}
-
-void OhmReceiverDriver::Playing()
-{
-    LOGDEB("=== PLAYING ====\n");
-    iConnected = true;
-    if (m_eater->playing)
-        m_eater->playing();
-}
-
-void OhmReceiverDriver::Disconnected()
-{
-    LOGDEB("=== DISCONNECTED ====\n");
-    iConnected = false;
-}
-
-void OhmReceiverDriver::Stopped()
-{
-    LOGDEB("=== STOPPED ====\n");
-    if (m_eater->stopped)
-        m_eater->stopped();
-}
-
-TBool OhmReceiverDriver::IsConnected()
-{
-    return iConnected;
-}
-
-// Debug and stats only, not needed for main function
-void OhmReceiverDriver::Observer::process(OhmMsgAudio& aMsg)
-{
-    if (++iCount == 400 || aMsg.Halt()) {
-        LOGDEB("OhmRcvDrv::Process:audio: samplerate " << aMsg.SampleRate() <<
-               " bitdepth " << aMsg.BitDepth() << " channels " <<
-               aMsg.Channels() << " samples " << aMsg.Samples() << 
-               " Halted ? " << aMsg.Halt() << endl);
-
-#if 0
-        static unsigned long long last_timestamp;
-        unsigned long long timestamp = aMsg.MediaTimestamp();
-        if (last_timestamp) {
-            long long intervalus = 
-                ((timestamp - last_timestamp) * 1000000) / (256*48000);
-            long long atsus = 
-                ((timestamp) * 1000000) / (256*48000);
-
-            // Not too sure what this did. amicros not in chrono.cpp any more
-            long long absus = chron.amicros() - 1430477861905884LL;
-            LOGDEB("Computed-uS: " << intervalus  << 
-                   " Elapsed-uS: " << chron.urestart() << 
-                   " Timestamp-uS: " << atsus <<
-                   " Abs-uS: " << absus << 
-                   " Diff-mS " << (absus - atsus) / 1000 <<
-                   endl);
-        }
-        last_timestamp = timestamp;
-#endif
-
-        if (!aMsg.Halt()) {
-            unsigned int bytes = 
-                aMsg.Samples() * (aMsg.BitDepth() / 8) * aMsg.Channels();
-
-            if (bytes != aMsg.Audio().Bytes()) {
-                LOGERR("OhmRcvDrv::Process:audio: computed bytes " << bytes << 
-                       " !=  buffer's " << aMsg.Audio().Bytes() << endl);
-                bytes = aMsg.Audio().Bytes();
-            }
-            const unsigned char *icp = 
-                (const unsigned char *)aMsg.Audio().Ptr();
-            bool silence = true;
-            for (unsigned int i = 0; i < bytes; i++) {
-                if (icp[i]) {
-                    silence = false;
-                    break;
-                }
-            }
-            if (silence) {
-                LOGDEB("OhmRcvDrv::Process:audio: silence buffer" << endl);
-            }
-            if (dumpfd >= 0) {
-                if (write(dumpfd, icp, bytes) != int(bytes)) {
-                    ;
-                }
-            }
-        }
-
-        iCount = 0;
-    }
-
-    if (iReset) {
-        iFrame = aMsg.Frame();
-        iReset = false;
-    } else {
-        if (aMsg.Frame() != iFrame + 1) {
-            LOGINF("Missed frames between " << iFrame << " and " << 
-                   aMsg.Frame() << endl);
-        }
-        iFrame = aMsg.Frame();
-    }
-}
-
-void copyswap(unsigned char *dest, const unsigned char *src, 
+bool needswap(AudioEater::BOrder order)
+{
+    // Songcast data is always msb-first.  Convert to desired order:
+    // depends on what downstream wants, and just as well we do it
+    // here because we copy the buf anyway.
+    bool needswap = false;
+    switch (order) {
+    case AudioEater::BO_MSB:
+        break;
+    case AudioEater::BO_LSB:
+        needswap = true;
+        break;
+    case AudioEater::BO_HOST:
+#ifdef WORDS_BIGENDIAN
+        needswap = false;
+#else
+        needswap = true;
+#endif
+        break;
+    }
+    return needswap;
+}
+
+void copyswap(unsigned char *dest, const unsigned char *src,
               unsigned int bytes, unsigned int bits)
 {
     unsigned char *ocp = dest;
@@ -324,91 +163,10 @@
     }
 }
 
-void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
-{
-    unsigned int bytes = 0;
-    unsigned int allocbytes = 0;
-    char *buf = NULL;
-    bool needswap = false;
-
-    if (aMsg.Audio().Bytes() == 0) {
-        if (aMsg.Halt()) {
-            LOGDEB("OhmReceiverDriver::Process: empty message with halt flag "
-                   "set\n");
-            goto put_audio_message;
-        } else {
-            LOGDEB("OhmReceiverDriver::Process: ignoring empty message\n");
-            return;
-        }
-    }
-
-    m_obs.process(aMsg);
-    if (aMsg.Halt()) {
-        LOGDEB("OhmReceiverDriver::Process: halt flag set in message\n");
-    }
-
-    bytes = aMsg.Audio().Bytes();
-    // We allocate a bit more space to avoir reallocations in the resampler
-    allocbytes = bytes + 100;
-    buf = (char *)malloc(allocbytes);
-    if (buf == 0) {
-        LOGERR("OhmReceiverDriver::Process: can't allocate " << 
-               bytes << " bytes\n");
-        return;
-    }
-
-    // Songcast data is always msb-first.  Convert to desired order:
-    // depends on what downstream wants, and just as well we do it
-    // here because we copy the buf anyway.
-    switch (m_eater->input_border) {
-    case AudioEater::BO_MSB: 
-        break;
-    case AudioEater::BO_LSB: 
-        needswap = true; 
-        break;
-    case AudioEater::BO_HOST:
-#ifdef WORDS_BIGENDIAN
-        needswap = false;
-#else
-        needswap = true;
-#endif
-        break;
-    }
-
-    if (needswap) {
-        copyswap((unsigned char *)buf, aMsg.Audio().Ptr(), bytes, aMsg.BitDepth());
-    } else {
-        memcpy(buf, aMsg.Audio().Ptr(), bytes);
-    }
-
-put_audio_message:
-    AudioMessage *ap = new 
-        AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
-                     aMsg.SampleRate(), aMsg.Halt(), buf, allocbytes);
-
-    // There is nothing special we can do if put fails: no way to
-    // return status. Should we just exit ?
-    if (!audioqueue.put(ap, false)) {
-        LOGERR("sc2mpd: queue dead: exiting\n");
-        exit(1);
-    }
-}
-
-void OhmReceiverDriver::Process(OhmMsgTrack& aMsg)
-{
-    Brhz uri(aMsg.Uri());
-    Brhz metadata(aMsg.Metadata());
-    LOGDEB("OhmRcvDrv::Process:trk: TRACK SEQ " << aMsg.Sequence() <<
-           " URI " << uri.CString() <<
-           " METADATA " << metadata.CString() << endl);
-}
-
-void OhmReceiverDriver::Process(OhmMsgMetatext& aMsg)
-{
-    Brhz metatext(aMsg.Metatext());
-    LOGDEB("OhmRcvDrv::Process:meta: METATEXT SEQUENCE " <<  aMsg.Sequence() <<
-           " METATEXT " << metatext.CString() << endl);
-}
+using namespace OpenHome;
+using namespace OpenHome::Net;
+using namespace OpenHome::TestFramework;
+using namespace OpenHome::Av;
 
 #ifdef WITH_WAVSC2
 static int playWav(const string& wavfile, AudioEater *eater,
@@ -489,7 +247,7 @@
 
 #define SOCK_PATH "/tmp/sc2mpd.sock"
 #define BUF_SIZE 16
-void HandleUserCmd(OhmReceiver* receiver, OhmReceiverDriver* driver,
+void HandleUserCmd(OhmReceiver* receiver, SongcastReceiver* driver,
                    AudioEater* audio, const Brx& aUri)
 {
     struct sockaddr_un server_addr, client_addr;
@@ -680,7 +438,7 @@
     AudioEater* eater = optionDevice.Value() ?
                           &alsaAudioEater : &httpAudioEater;
 
-    OhmReceiverDriver* driver = new OhmReceiverDriver(eater, ctxt);
+    SongcastReceiver* driver = new SongcastReceiver(eater, ctxt);
 
     OhmReceiver* receiver = new OhmReceiver(lib->Env(), adapter, ttl, *driver);
 
@@ -724,7 +482,7 @@
     }
 
     delete(receiver);
-
+    delete driver;
     delete lib;
 
     if (optionInteract.Value())