Logo Search packages:      
Sourcecode: zinf version File versions  Download package

vorbis.cpp

/*____________________________________________________________________________
    
    Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)

    Portions Copyright (C) 1999 EMusic.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    $Id: vorbis.cpp,v 1.19 2004/02/09 09:55:37 enxrah Exp $
____________________________________________________________________________*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <errno.h>
#include <locale.h>
#include <ctype.h>
#include <map>
#include <algorithm>

#ifdef __QNX__
#include <strings.h>
#endif

    using namespace std;

#include "config.h"
#include "path_max.h"
#include "errors.h"
#include "utility.h"

#include "vorbis.h"
#include "vorbis/vorbisfile.h"

#include "vcedit.h"
#include "i18n.h"


#define CHUNK 4096

typedef multimap<string,string> tagmap_t;

extern    "C"
{
    MetaDataFormat *Initialize(FAContext * context)
    {
        return new Vorbis(context);
    }
}

static bool 
add_comment(tagmap_t & map, 
            const string& tag, const string&val, bool singleton = true)
{
    if (val.size() == 0) return false;
    // case 1: when a singleton, erase all existing values.
    if (singleton) 
        map.erase (tag);

    map.insert (pair<string,string>(tag,val));
    return true;
}

/** Find a value from in the tag table returning true when it exists 
 *  
 */
static bool
get_comment(tagmap_t& map, const string& tag, string&val)
{
    tagmap_t::iterator it;
    it = map.find (tag);
    if (it != map.end()) 
    {
        val = (*it).second;
        return true;
    }
    return false;
}

/** Load all tags from the comment structure into the given map. */
static void 
load_tags (vorbis_comment *vc, tagmap_t& map) 
{
    string entry;
    string key;
    string val;
    for (int i=0; i<vc->comments; i++) 
    {
        entry = vc->user_comments[i];

        string::size_type sep = entry.find ('=');
        key = entry.substr(0, sep);

        transform(key.begin(),key.end(),key.begin(),(int(*)(int))&tolower);
        map.insert (pair<string,string>(key, entry.substr(sep+1)));
    }
}

/** Save the tags in the map to the given comment structure.
 *  NOTE:  This will not enforce vorbis tag recommenation singleton
 * or values, so use with care 
 */
static void 
save_tags (vorbis_comment *vc, tagmap_t& map) 
{

    string comment;
    string key;
    for (tagmap_t::iterator it = map.begin(); it != map.end(); it++) 
    {
        key = (*it).first;
        transform(key.begin(),key.end(),key.begin(),(int(*)(int))&toupper);
        comment = key;
        comment += '=';
        comment += (*it).second;
        vorbis_comment_add (vc, (char*)comment.c_str());
    }
}

Vorbis::Vorbis(FAContext * context):MetaDataFormat(context)
{}

Vorbis::~Vorbis()
{}

/** Write out vorbis metadata.    */
bool Vorbis::WriteMetaData(const char *url, const MetaData & metadata)
{
    char      dummy[20];
    char     *ptr;
    bool     writetags;
    // We will support only id3-like tags.  For a more complete list see 
    //  http://reactor-core.org/ogg-tag-standard.html

    ptr = strrchr(url, '.');
    if (ptr == NULL)
        return false;

    if (strcasecmp(ptr, ".ogg"))
        return false;  

    m_context->prefs->GetPrefBoolean(kWriteVorbisTagsPref, &writetags);

    if (writetags == false)
    {
        fprintf (stderr, "Bailing : don't write vorbis tags \n");
        return true;
    }
    
    // Rewrite the file with the updated tags.

    vcedit_state *state;
    vorbis_comment *vc;
    
    string path;

    URLToFilePath(url, path);
    state = vcedit_new_state();

    FILE *in = fopen (path.c_str(), "rb");

    if (in == NULL) 
    {
//        fprintf (stderr, _("Failed to open %s"), path.c_str());
        vcedit_clear(state);
        return false;
    }
    if(vcedit_open(state, in) < 0)
    {
//         fprintf(stderr, _("Failed to open %s as vorbis: %s\n"), 
//                 path.c_str(), vcedit_error(state));
        fclose (in);
        vcedit_clear(state);
        return false;
    }
    tagmap_t tagmap;

    vc = vcedit_comments(state);
    load_tags (vc, tagmap);
    
    add_comment(tagmap, "title", metadata.Title());
    add_comment(tagmap, "artist", metadata.Artist());
    add_comment(tagmap, "album", metadata.Album());
    add_comment(tagmap, "comment", metadata.Comment());
    add_comment(tagmap, "genre", metadata.Genre());
    if (metadata.Year() > 0)
    {
        sprintf(dummy, "%d", metadata.Year());
        add_comment(tagmap, "date", dummy);
    }
    if (metadata.Track() > 0) {
        sprintf (dummy, "%d", metadata.Track());
        add_comment(tagmap, "tracknumber", dummy);
    }
    
    vorbis_comment_clear (vc);
    save_tags (vc, tagmap);
    
    char *newpath = new char[PATH_MAX];
    strncpy(newpath,path.c_str(),path.length());
    FILE *out = NULL;

    ptr = strrchr(newpath, '.');
    if (ptr) 
    {
        strcpy(ptr, "XXXXXX");
        int fd = mkstemp(newpath);
        out = fdopen (fd, "wb");

        if(out == NULL || vcedit_write(state, out) < 0){
//             fprintf(stderr,_("Failed to write comments to output file: %s\n"), 
//                     vcedit_error(state));
            writetags = false;
        }
    }        
    if (in) fclose (in);
    if (out) fclose (out);
    vcedit_clear(state);
    
    if (writetags && rename (newpath, path.c_str()) < 0)
    {
//         fprintf(stderr,_("Failed rename output file: %s\n"), newpath);
        delete [] newpath;
        return false;
    }
    delete [] newpath;
    return true;
}

bool Vorbis::ReadMetaData(const char *url, MetaData * metadata)
{
    // We will support only id3-like tags.  For a more complete list see 
    //  http://reactor-core.org/ogg-tag-standard.html

    tagmap_t tagmap;
    string val;

    if (readMetadata(string(url), tagmap) == false)
        return false;
    

    if (get_comment(tagmap, "title", val))
        metadata->SetTitle (val);
    if (get_comment(tagmap, "artist", val))
        metadata->SetArtist (val);
    if (get_comment(tagmap, "album", val))
        metadata->SetAlbum (val);
    if (get_comment(tagmap, "tracknumber", val))
        metadata->SetTrack (atoi(val.c_str()));
    if (get_comment(tagmap, "comment", val))
        metadata->SetComment (val);
    if (get_comment(tagmap, "genre", val))
        metadata->SetGenre (val);
    if (get_comment(tagmap, "date", val)){
        //  First 2 or 4 digits should be year
        if (val.size () >=4)
            metadata->SetYear (atoi(val.substr (0,4).c_str()));
        else if (val.size () >= 2)
            metadata->SetYear (atoi(val.substr (0,2).c_str()));
    }

    return true;
}




bool 
Vorbis::readMetadata(const string&url, 
                     multimap<string,string>& pairs)
{
    string::size_type pos = url.rfind('.');
    if (pos == string::npos) return false;
    if (url.substr(pos) != ".ogg") return false;

    vcedit_state *state;
    vorbis_comment *vc;
    state = vcedit_new_state();

    string path;
    //    fprintf(stderr, "vorbis: url %s\n", url.c_str());
    
    URLToFilePath(url, path);
    FILE *in = fopen (path.c_str(), "rb");
    if (in == NULL) {
        fprintf(stderr, "vorbis: open failed %s\n", path.c_str());
        
        vcedit_clear(state);
        return false;
    }
    if(vcedit_open(state, in) < 0) {
        fprintf(stderr, "vorbis: vcedit failed %s\n", path.c_str());
        fclose (in);
        vcedit_clear(state);
        return false;
    }
    vc = vcedit_comments(state);
    load_tags(vc, pairs);
    vcedit_clear(state);
    //fclose(in);
    //in = fopen (path.c_str(), "rb");
    fseek(in, 0,0);
    OggVorbis_File  vf;
    memset(&vf, 0, sizeof(vf));
    int err = ov_open(in, &vf, NULL, 0);
    if (err == 0) {
      //        fprintf(stderr, "vorbis: getting time from vorbis");
        char buffer[17];
        sprintf(buffer, "%d", int(ov_time_total(&vf, -1)));
        pairs.insert(pairs.end(), make_pair(string("time"), string(buffer)));
        ov_clear(&vf);
    } else {
        fprintf(stderr, "vorbis:  time failed in vorbis(%d)", err);
        fclose(in);
    }
    //    fprintf(stderr, "vorbis: read %d entries\n", pairs.size());

    return true;
}

bool 
Vorbis::writeMetadata(const std::string&url, 
                      multimap<std::string,std::string>& pairs)
{
    // Rewrite the file with the updated tags.
    bool     writetags = true;

    string::size_type pos = url.rfind('.');
    if (pos == string::npos) return false;
    if (url.substr(pos) != ".ogg") return false;


    m_context->prefs->GetPrefBoolean(kWriteVorbisTagsPref, &writetags);

    if (writetags == false) {
        return true;
    }

    vcedit_state *state;
    vorbis_comment *vc;
    
    string path;

    URLToFilePath(url, path);
    state = vcedit_new_state();

    FILE *in = fopen (path.c_str(), "rb");

    if (in == NULL) {
        vcedit_clear(state);
        return false;
    }
    if(vcedit_open(state, in) < 0) {
        fclose (in);
        vcedit_clear(state);
        return false;
    }
    tagmap_t tagmap;

    vc = vcedit_comments(state);
    load_tags (vc, tagmap);

    // Load the new tags
    copy(pairs.begin(), pairs.end(), inserter(tagmap, tagmap.end()));

    vorbis_comment_clear (vc);
    save_tags (vc, tagmap);
    
    char* newpath = new char[path.length()+6];
    strcpy(newpath, path.c_str());
    FILE *out = NULL;

    char *spot = strrchr(newpath,'.');
    if (spot) {
        strcpy(spot, "XXXXXX");
        int fd = mkstemp(newpath);
        out = fdopen (fd, "wb");

        if(out == NULL || vcedit_write(state, out) < 0){
            writetags = false;
        }
    }        
    if (in) fclose (in);
    if (out) fclose (out);
    vcedit_clear(state);
    
    if (writetags && rename (newpath, path.c_str()) < 0) {
        delete [] newpath;
        return false;
    }
    delete [] newpath;
    return true;

}
/* arch-tag: f9494afe-4c54-49ff-bd6f-1b214d6f2fed
   (do not change this comment) */

Generated by  Doxygen 1.6.0   Back to index