changeset 1:150ad38c1871 ofa-vamp-plugin

* First revision of MusicIP OFA fingerprinting and PUID lookup plugin
author cannam
date Mon, 24 Sep 2007 13:36:27 +0000
parents e70d9b1aa0a7
children 8056c637875b
files Makefile OfaVampPlugin.cpp OfaVampPlugin.h ofa-vamp-plugin.cat protocol.cpp protocol.h
diffstat 6 files changed, 750 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,11 @@
+
+CXXFLAGS	:= -I../vamp-plugin-sdk -O3 -Wall -march=pentium4 -msse -msse2 -fomit-frame-pointer -ffast-math
+
+ofa-vamp-plugin.so:	OfaVampPlugin.o protocol.o
+	g++ -shared $^ -o $@ -L../vamp-plugin-sdk/vamp-sdk -Wl,-Bstatic -lvamp-sdk -Wl,-Bdynamic -lofa -lcurl -lexpat
+
+clean:	
+	rm *.o
+
+OfaVampPlugin.o: OfaVampPlugin.h protocol.h
+protocol.o: protocol.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OfaVampPlugin.cpp	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,293 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "OfaVampPlugin.h"
+
+#include <vamp/vamp.h>
+#include <vamp-sdk/PluginAdapter.h>
+#include <vamp-sdk/RealTime.h>
+
+#include <cstdlib>
+#include <cmath>
+
+#include <ofa1/ofa.h>
+
+#include <endian.h>
+
+#include "protocol.h"
+
+using std::string;
+
+OfaVampPlugin::OfaVampPlugin(float inputSampleRate) :
+    Plugin(inputSampleRate),
+    m_buffer(0),
+    m_bufsiz(0),
+    m_bufidx(0),
+    m_channels(0),
+    m_blockSize(0),
+    m_totalCount(0),
+    m_enough(false)
+{
+}
+
+OfaVampPlugin::~OfaVampPlugin()
+{
+    if (m_buffer) free(m_buffer);
+}
+
+string
+OfaVampPlugin::getIdentifier() const
+{
+    return "ofa";
+}
+
+string
+OfaVampPlugin::getName() const
+{
+    return "MusicIP OFA Audio Fingerprinter";
+}
+
+string
+OfaVampPlugin::getDescription() const
+{
+    return "Calculates an audio fingerprint using the MusicIP fingerprinting library";
+}
+
+string
+OfaVampPlugin::getMaker() const
+{
+    return "Chris Cannam";
+}
+
+int
+OfaVampPlugin::getPluginVersion() const
+{
+    return 1;
+}
+
+string
+OfaVampPlugin::getCopyright() const
+{
+    return ""; //!!!
+}
+
+OfaVampPlugin::ParameterList
+OfaVampPlugin::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    return list;
+}
+
+float
+OfaVampPlugin::getParameter(std::string name) const
+{
+    return 0.0;
+}
+
+void
+OfaVampPlugin::setParameter(std::string name, float value)
+{
+}
+
+size_t
+OfaVampPlugin::getPreferredStepSize() const
+{
+    return 0;
+}
+
+size_t
+OfaVampPlugin::getPreferredBlockSize() const
+{
+    return 0;
+}
+
+bool
+OfaVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (stepSize != blockSize) {
+        std::cerr << "ERROR: OfaVampPlugin::initialise: stepSize must be equal to blockSize (" << stepSize << " != " << blockSize << ")" << std::endl;
+        return false;
+    }
+
+    m_bufsiz = 1048576;
+    m_buffer = (int16_t *)malloc(m_bufsiz * sizeof(int16_t));
+    m_bufidx = 0;
+    m_channels = channels;
+    m_blockSize = blockSize;
+    m_totalCount = 0;
+    m_enough = false;
+    return true;
+}
+
+void
+OfaVampPlugin::reset()
+{
+    m_bufidx = 0;
+    m_totalCount = 0;
+    m_print = "";
+    m_enough = false;
+}
+
+OfaVampPlugin::OutputList
+OfaVampPlugin::getOutputDescriptors() const
+{
+    OutputList list;
+
+    OutputDescriptor desc;
+    desc.identifier = "fingerprint";
+    desc.name = "Fingerprint";
+    desc.description = "Single result containing the audio fingerprint as its label";
+    desc.unit = "";
+    desc.hasFixedBinCount = true;
+    desc.binCount = 0;
+    desc.hasKnownExtents = false;
+    desc.isQuantized = false;
+    desc.sampleType = OutputDescriptor::VariableSampleRate;
+    desc.sampleRate = m_inputSampleRate;
+    list.push_back(desc);
+
+    desc.identifier = "puid";
+    desc.name = "PUID";
+    desc.description = "Single result containing the MusicIP online PUID for this audio track, if available";
+    desc.unit = "";
+    desc.hasFixedBinCount = true;
+    desc.binCount = 0;
+    desc.hasKnownExtents = false;
+    desc.isQuantized = false;
+    desc.sampleType = OutputDescriptor::VariableSampleRate;
+    desc.sampleRate = m_inputSampleRate;
+    list.push_back(desc);
+
+    return list;
+}
+
+// client id: a008bd70c9d3c681e2ff336e3ceaa6dc (limited)
+
+OfaVampPlugin::FeatureSet
+OfaVampPlugin::process(const float *const *inputBuffers,
+                       Vamp::RealTime timestamp)
+{
+    m_totalCount += m_blockSize;
+
+    if (m_enough) return FeatureSet();
+
+    // libofa requires the first 135 seconds of audio (where available)
+    size_t reqd = lrintf(ceil(135.f * m_inputSampleRate));
+
+    size_t effectiveChannels = m_channels;
+    if (effectiveChannels > 2) effectiveChannels = 2;
+
+    if (m_bufidx + m_blockSize * effectiveChannels > m_bufsiz) {
+        m_bufsiz *= 2;
+        m_buffer = (int16_t *)realloc(m_buffer, m_bufsiz * sizeof(int16_t));
+        std::cerr << "realloc " << m_bufsiz/2 << " -> " << m_bufsiz << std::endl;
+    }
+
+    for (size_t i = 0; i < m_blockSize; ++i) {
+        for (size_t c = 0; c < effectiveChannels; ++c) {
+            m_buffer[m_bufidx + i * effectiveChannels + c] = 
+                int16_t(inputBuffers[c][i] * 32767.0);
+        }
+    }
+    
+    m_bufidx += m_blockSize * effectiveChannels;
+
+    if (m_bufidx >= reqd * effectiveChannels) {
+
+        m_enough = true;
+
+        FeatureSet fset;
+
+        int endian = OFA_LITTLE_ENDIAN;
+
+#ifdef __BYTE_ORDER
+#if __BYTE_ORDER == _BIG_ENDIAN
+        endian = OFA_BIG_ENDIAN;
+#endif
+#endif
+
+        const char *print = ofa_create_print
+            ((unsigned char *)m_buffer,
+             endian,
+             m_bufidx,
+             lrintf(m_inputSampleRate),
+             m_channels > 1);
+        
+        if (!print) {
+            std::cerr << "OfaVampPlugin: ERROR from libofa" << std::endl;
+            return fset;
+        }
+
+        m_print = print;
+
+        Feature feature;
+        feature.hasTimestamp = true;
+        feature.timestamp = Vamp::RealTime::zeroTime;
+        feature.label = m_print;
+        fset[0].push_back(feature);
+        return fset;
+    }
+
+    return FeatureSet();
+}
+
+OfaVampPlugin::FeatureSet
+OfaVampPlugin::getRemainingFeatures()
+{
+    FeatureSet fset;
+
+    if (m_print == "") return fset;
+
+    Feature feature;
+    feature.hasTimestamp = true;
+    feature.timestamp = Vamp::RealTime::zeroTime;
+
+    int endian = OFA_LITTLE_ENDIAN;
+
+#ifdef __BYTE_ORDER
+#if __BYTE_ORDER == _BIG_ENDIAN
+        endian = OFA_BIG_ENDIAN;
+#endif
+#endif
+
+    AudioData data;
+    data.setData
+        (0, 
+         endian,
+         m_bufidx,
+         lrintf(m_inputSampleRate),
+         m_channels > 1,
+         lrintf((m_totalCount * 1000.f) / m_inputSampleRate), //!!! approx
+         "wav"); //!!!
+
+    data.info.setPrint(m_print);
+
+    TrackInformation *info = data.getMetadata
+        ("a008bd70c9d3c681e2ff336e3ceaa6dc",
+         "1",
+         false);
+    
+    if (!info) {
+        std::cerr << "OfaVampPlugin: ERROR from ofa/protocol, unable to retrieve PUID for fingerprint" << std::endl;
+        std::cerr << "Fingerprint was: " << m_print << std::endl;
+    } else {
+        feature.label = info->getPUID();
+        fset[1].push_back(feature);
+    }
+
+    return fset;
+}
+
+static Vamp::PluginAdapter<OfaVampPlugin> ofaAdapter;
+
+const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
+                                                    unsigned int index)
+{
+    if (version < 1) return 0;
+
+    switch (index) {
+    case  0: return ofaAdapter.getDescriptor();
+    default: return 0;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OfaVampPlugin.h	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,57 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef _OFA_VAMP_PLUGIN_H_
+#define _OFA_VAMP_PLUGIN_H_
+
+#include <vamp-sdk/Plugin.h>
+
+#include <pthread.h>
+
+class OfaVampPlugin : public Vamp::Plugin
+{
+public:
+    OfaVampPlugin(float inputSampleRate);
+    virtual ~OfaVampPlugin();
+
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+    void reset();
+
+    InputDomain getInputDomain() const { return TimeDomain; }
+
+    size_t getPreferredStepSize() const;
+    size_t getPreferredBlockSize() const;
+
+    size_t getMinChannelCount() const { return 1; }
+    size_t getMaxChannelCount() const { return 2; }
+
+    std::string getIdentifier() const;
+    std::string getName() const;
+    std::string getDescription() const;
+    std::string getMaker() const;
+    int getPluginVersion() const;
+    std::string getCopyright() const;
+
+    ParameterList getParameterDescriptors() const;
+    float getParameter(std::string) const;
+    void setParameter(std::string, float);
+
+    OutputList getOutputDescriptors() const;
+
+    FeatureSet process(const float *const *inputBuffers,
+                       Vamp::RealTime timestamp);
+
+    FeatureSet getRemainingFeatures();
+
+protected:
+    int16_t *m_buffer;
+    size_t m_bufsiz;
+    size_t m_bufidx;
+    size_t m_channels;
+    size_t m_blockSize;
+    size_t m_totalCount;
+    std::string m_print;
+    bool m_enough;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ofa-vamp-plugin.cat	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,1 @@
+vamp:ofa-vamp-plugin:ofa::Fingerprinting
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/protocol.cpp	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,274 @@
+/* ------------------------------------------------------------------
+
+   libofa -- the Open Fingerprint Architecture library
+
+   Public Domain (PD) 2006 MusicIP Corporation
+   No rights reserved.
+
+-------------------------------------------------------------------*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <map>
+#include <expat.h>
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+using namespace std;
+
+#include "protocol.h"
+
+const char *url = "http://ofa.musicdns.org/ofa/1/track"; 
+const char *userAgent = "libofa_example";
+const char *unknown = "unknown";
+
+// Lookup by fingerprint
+const char *request_format = 
+    "cid=%s&"       // Client ID
+    "cvr=%s&"       // Client Version
+    "fpt=%s&"       // Fingerprint
+    "rmd=%d&"       // m = 1: return metadata; m = 0: only return id 
+    "brt=%d&"       // bitrate (kbps)
+    "fmt=%s&"       // File extension (e.g. mp3, ogg, flac)
+    "dur=%ld&"      // Length of track (milliseconds)
+    "art=%s&"       // Artist name. If there is none, send "unknown"
+    "ttl=%s&"       // Track title. If there is none, send "unknown"
+    "alb=%s&"       // Album name. If there is none, send "unknown"
+    "tnm=%d&"       // Track number in album. If there is none, send "0"
+    "gnr=%s&"       // Genre. If there is none, send "unknown"
+    "yrr=%s&"       // Year. If there is none, send "0"
+    "enc=%s&"       // Encoding. e = true: ISO-8859-15; e = false: UTF-8 (default). Optional.
+    "\r\n";
+
+// Lookup by PUID (Most fields drop out)
+const char *request_format2 =
+    "cid=%s&"       // Client ID
+    "cvr=%s&"       // Client Version
+    "pid=%s&"       // PUID 
+    "rmd=%d&"       // m = 1: return metadata; m = 0: only return id 
+    "brt=%d&"       // bitrate (kbps)
+    "fmt=%s&"       // File extension (e.g. mp3, ogg, flac)
+    "dur=%ld&"      // Length of track (milliseconds)
+    "art=%s&"       // Artist name. If there is none, send "unknown"
+    "ttl=%s&"       // Track title. If there is none, send "unknown"
+    "alb=%s&"       // Album name. If there is none, send "unknown"
+    "tnm=%d&"       // Track number in album. If there is none, send "0"
+    "gnr=%s&"       // Genre. If there is none, send "unknown"
+    "yrr=%s&"       // Year. If there is none, send "0"
+    "enc=%s&"       // Encoding. e = true: ISO-8859-15; e = false: UTF-8 (default). Optional.
+    "\r\n";
+
+
+// --------------------------------------------------------------------
+// HTTP POST support using standard curl calls
+// --------------------------------------------------------------------
+size_t data_callback(void *ptr, size_t size, size_t num, void *arg)
+{
+    string *str = (string *)arg;
+    (*str) += string((const char *)ptr, size * num);
+    return size * num;
+}
+
+long http_post(const string &url, const string &userAgent, const string &postData, string &doc)
+{
+  CURL              *curl;
+  long               ret = 0;
+  struct curl_slist *headerlist=NULL;
+
+  headerlist = curl_slist_append(headerlist, "Expect:"); 
+
+  curl_global_init(CURL_GLOBAL_ALL);
+  curl = curl_easy_init();
+  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&doc);
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, data_callback);
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.length());
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
+  curl_easy_setopt(curl, CURLOPT_POST, 1);
+  curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
+  curl_easy_perform(curl);
+  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
+  curl_easy_cleanup(curl);
+
+  curl_slist_free_all (headerlist);
+
+  return ret;
+}
+
+// --------------------------------------------------------------------
+// XML Parsing support 
+// --------------------------------------------------------------------
+
+struct ParseInfo
+{
+    string path;
+    string pcdata;
+    TrackInformation *info;
+};
+
+void begin_element(void *data, const XML_Char *el, const XML_Char **attr)
+{
+    map<string, string> attrs;
+
+    for(; *attr;) {
+        string key = string((char *)*(attr++));
+	string value = string((char *)*(attr++));
+        attrs[key] = value;
+    }
+
+    ((ParseInfo *)data)->path += string("/") + string(el);
+    if (((ParseInfo *)data)->path == "/metadata/track/puid-list/puid")
+         ((ParseInfo *)data)->info->setPUID(attrs["id"]);
+
+    ((ParseInfo *)data)->pcdata = "";
+}
+
+void end_element(void *data, const XML_Char *el)
+{
+    string::size_type pos;
+
+    if (((ParseInfo *)data)->path == "/metadata/track/title")
+         ((ParseInfo *)data)->info->setTrack(((ParseInfo *)data)->pcdata);
+    if (((ParseInfo *)data)->path == "/metadata/track/artist/name")
+         ((ParseInfo *)data)->info->setArtist(((ParseInfo *)data)->pcdata);
+
+    pos = ((ParseInfo *)data)->path.rfind("/");
+    if (pos != string::npos)
+       ((ParseInfo *)data)->path = ((ParseInfo *)data)->path.substr(0, pos);
+}
+
+void pc_data(void *data, const XML_Char *charData, int len)
+{
+    char *temp;
+
+    temp = new char[len + 1];
+    strncpy(temp, (char *)charData, len);
+    temp[len] = 0;
+    ((ParseInfo *)data)->pcdata += string(temp);
+    delete temp;
+}
+
+bool parse_xml(const string &doc, TrackInformation *info, string &err) 
+{
+    ParseInfo pinfo;
+
+    err = "";
+    pinfo.info = info;
+    XML_Parser parser = XML_ParserCreate(NULL);
+    XML_SetUserData(parser, (void *)&pinfo);
+    XML_SetElementHandler(parser, ::begin_element, ::end_element);
+    XML_SetCharacterDataHandler(parser, ::pc_data);
+    int ret = XML_Parse(parser, doc.c_str(), doc.length(), 1);
+
+    if (ret)
+    {
+        XML_ParserFree(parser);
+        return true;
+    }
+
+    err = string(XML_ErrorString(XML_GetErrorCode(parser)));
+    char num[10];
+    sprintf(num, "%d", XML_GetCurrentLineNumber(parser));
+    err += string(" on line ") + string(num);
+    XML_ParserFree(parser);
+
+    return false;
+}
+
+// --------------------------------------------------------------------
+// Retrieve metadata for fingerprint
+// --------------------------------------------------------------------
+
+// Returns true on success
+bool retrieve_metadata(string client_key, string client_version,
+	TrackInformation *info, bool getMetadata) 
+{
+    if (!info)
+	return false;
+
+    // All metadata fields must be provided before this call if the
+    // information is available, as part of the Terms of Service.
+    // This helps create a better database for all users of the system.
+    //
+    // If the fields are not available, you can use default values.
+    // Here we check for fields which have no default values.
+    if (client_key.length() == 0)
+	return false;
+    if (client_version.length() == 0)
+	return false;
+
+    bool lookupByPrint = false;
+    if (info->getPUID().length() == 0) {
+	// Lookup by fingerprint
+	if (info->getPrint().length() == 0)
+	    return false;
+	if (info->getFormat().length() == 0)
+	    return false;
+	if (info->getLengthInMS() == 0)
+	    return false;
+
+        lookupByPrint = true;
+    }
+
+    // Sloppily estimate the size of the resultant URL. Err on the side of making the string too big.
+    int bufSize = strlen(lookupByPrint ? request_format : request_format2) +
+            client_key.length() + client_version.length() +
+            (lookupByPrint ? info->getPrint().length() : info->getPUID().length()) + 
+            16 + // getMetadata ? 1 : 0, 
+            16 + // info->getBitrate(),
+            16 + //info->getFormat().c_str(), 
+            16 + //info->getLengthInMS(), 
+            ((info->getArtist().c_str() == 0) ? strlen(unknown) : info->getArtist().length()) +
+            ((info->getTrack().c_str() == 0) ?  strlen(unknown) : info->getTrack().length()) +
+            ((info->getAlbum().c_str() == 0) ?  strlen(unknown) : info->getAlbum().length()) +
+            16 + // info->getTrackNum() + 
+            ((info->getGenre().c_str() == 0) ?  strlen(unknown) : info->getGenre().length()) +
+            ((info->getYear().c_str() == 0) ? 1 : info->getYear().length()) +
+            info->getEncoding().length();
+        
+    char *buf = new char[bufSize];
+    sprintf(buf, lookupByPrint ? request_format : request_format2, 
+            client_key.c_str(), 
+            client_version.c_str(),
+            lookupByPrint ? info->getPrint().c_str() : info->getPUID().c_str(), 
+            getMetadata ? 1 : 0, 
+            info->getBitrate(),
+            info->getFormat().c_str(), 
+            info->getLengthInMS(), 
+            (info->getArtist().length() == 0) ? unknown : info->getArtist().c_str(),
+            (info->getTrack().length() == 0) ? unknown : info->getTrack().c_str(),
+            (info->getAlbum().length() == 0) ? unknown : info->getAlbum().c_str(),
+            info->getTrackNum(), 
+            (info->getGenre().length() == 0) ? unknown : info->getGenre().c_str(),
+            (info->getYear().length() == 0) ? "0" : info->getYear().c_str(),
+            info->getEncoding().c_str());
+
+    string response;
+//    printf("request: '%s'\n", buf);
+    long ret = http_post(url, userAgent, buf, response);
+    delete [] buf;
+
+    if (ret != 200)
+    {
+//	     printf("Error: %ld\n", ret);
+//	     printf("response: %s\n\n", response.c_str());
+        return false;
+    }
+//    printf("response: %s\n\n", response.c_str());
+
+    unsigned int q = response.find("<?xml");
+    if (q != string::npos) {
+        response = response.substr(q);
+    }
+    string err;
+    if (!parse_xml(response, info, err)) {
+        // Clears title if it wasn't returned
+        info->setTrack("");
+
+        // Clears artists if it wasn't returned
+        info->setArtist("");
+    }
+    return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/protocol.h	Mon Sep 24 13:36:27 2007 +0000
@@ -0,0 +1,114 @@
+/* ------------------------------------------------------------------
+
+   libofa -- the Open Fingerprint Architecture library
+
+   Public Domain (PD) 2006 MusicIP Corporation
+   No rights reserved.
+
+-------------------------------------------------------------------*/
+#ifndef __PROTOCOL_H__
+#define __PROTOCOL_H__
+
+#include <string>
+#include "ofa1/ofa.h"
+
+using namespace std;
+
+// This object must be filled out completely prior to making any
+// calls to the server.  On return, some fields will be filled out.
+class TrackInformation {
+private:
+    string puid;
+    string print;
+    string encoding;        // All other strings must honor this encoding
+    int    bitrate;         // i.e. "192kbps", use 0 for VBR or freeformat
+    string format;          // File extension
+    long   length_in_ms;    // In milliseconds
+    string artist;
+    string track;
+    string album;
+    int    trackNum;        // use 0 if not known
+    string genre;
+    string year;
+public:
+    TrackInformation() :
+	bitrate(0), length_in_ms(0), trackNum(0) {}
+    ~TrackInformation() {}
+    void setPrint(string p) { print = p; }
+    string getPrint() const { return print; }
+    // Only supported encodings are UTF-8 (default) and ISO-8859-15
+    void setEncoding(string e) { encoding = e; }
+    string getEncoding() const { return encoding; }
+    void setBitrate(int b) { bitrate = b; }
+    int getBitrate() const { return bitrate; }
+    void setFormat(string fmt) { format = fmt; }
+    string getFormat() const { return format; }
+    void setLengthInMS(long ms) { length_in_ms = ms; }
+    long getLengthInMS() const { return length_in_ms; }
+    void setArtist(string name) { artist = name; }
+    string getArtist() const { return artist; }
+    void setTrack(string name) { track = name; }
+    string getTrack() const { return track; }
+    void setAlbum(string name) { album = name; }
+    string getAlbum() const { return album; }
+    void setTrackNum(int t) { trackNum = t; }
+    int getTrackNum() const { return trackNum; }
+    void setGenre(string g) { genre = g; }
+    string getGenre() const { return genre; }
+    void setYear(string y) { year = y; }
+    string getYear() const { return year; }
+    void setPUID(string id)  { puid = id; }
+    string getPUID() const { return puid; }
+};
+
+// Get your unique key at http://www.musicdns.org
+bool retrieve_metadata(string client_key, string client_verstion,
+	TrackInformation *info, bool getMetadata);
+
+class AudioData {
+private:
+    unsigned char *samples;
+    int byteOrder;
+    long size;
+    int sRate;
+    bool stereo;
+public:
+    TrackInformation info;
+    AudioData() : samples(0), size(0), sRate(0), stereo(false) {}
+    ~AudioData() {
+	delete[] samples;
+    }
+    // size is number of samples (half the number of bytes)
+    void setData(unsigned char*_samples, int _byteOrder, long _size,
+	   	 int _sRate, bool _stereo, int _ms, string _fmt) {
+	samples = _samples;
+	byteOrder = _byteOrder;
+	size = _size;
+	sRate = _sRate;
+	stereo = _stereo;
+	// These two fields are used later for the protocol layer
+	info.setLengthInMS(_ms);
+	info.setFormat(_fmt);
+    }
+    int getByteOrder() const { return byteOrder; }
+    long getSize() const { return size; }
+    int getSRate() const { return sRate; }
+    bool getStereo() const { return stereo; }
+    bool createPrint() {
+	const char *print = ofa_create_print(samples, byteOrder, size, sRate, stereo);
+	if (!print)
+	    return false;
+	info.setPrint(print);
+	return true;
+    }
+    // Get your unique key at http://www.musicdns.org
+    TrackInformation *getMetadata(string client_key, string client_version, 
+	    bool metadataFlag)
+    {
+	if (!retrieve_metadata(client_key, client_version, &info, metadataFlag))
+	    return 0;
+	return &info;
+    }
+};
+
+#endif