Attempt to add opus support.

This commit is contained in:
Storm Dragon
2025-04-05 02:02:50 -04:00
parent 1a9b32ee7a
commit 326b491fde
8 changed files with 792 additions and 524 deletions

59
tagutils/opus.c Normal file
View File

@ -0,0 +1,59 @@
//=========================================================================
// FILENAME : tagutils-opus.c
// DESCRIPTION : Opus metadata reader
//=========================================================================
static int
_get_opusfileinfo(char *filename, struct song_metadata *psong)
{
OggOpusFile *opusfile;
const OpusTags *tags;
char **comment;
int *commentlen;
int j, e;
opusfile = op_open_file (filename, &e);
if(!opusfile)
{
DPRINTF(E_WARN, L_SCANNER,
"Error opening input file \"%s\": %s\n", filename, opus_strerror(e));
return -1;
}
DPRINTF(E_MAXDEBUG, L_SCANNER, "Processing file \"%s\"...\n", filename);
psong->song_length = op_pcm_total (opusfile, -1);
if (psong->song_length < 0)
{
DPRINTF(E_WARN, L_SCANNER,
"Unable to obtain length of %s\n", filename);
psong->song_length = 0;
} else
/* Sample rate is always 48k, so length in ms is just samples/48 */
psong->song_length /= 48;
/* Note that this gives only the first link's channel count. */
psong->channels = op_channel_count (opusfile, -1);
psong->samplerate = 48000;
psong->bitrate = op_bitrate (opusfile, -1);
tags = op_tags (opusfile, -1);
if (!tags)
{
DPRINTF(E_WARN, L_SCANNER, "Unable to obtain tags from %s\n",
filename);
return -1;
}
comment = tags->user_comments;
commentlen = tags->comment_lengths;
for (j = 0; j < tags->comments; j++)
vc_scan (psong, *(comment++), *(commentlen++));
op_free (opusfile);
return 0;
}

6
tagutils/opus.h Normal file
View File

@ -0,0 +1,6 @@
//=========================================================================
// FILENAME : tagutils-opus.h
// DESCRIPTION : Opus metadata reader
//=========================================================================
static int _get_opusfileinfo(char *filename, struct song_metadata *psong);

View File

@ -1,6 +1,6 @@
//=========================================================================
// FILENAME : tagutils.c
// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader
// FILENAME : tagutils.c
// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader
//=========================================================================
// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
//=========================================================================
@ -42,6 +42,10 @@
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
/* ADD THIS INCLUDE FOR OPUS */
#ifdef HAVE_OPUS
#include <opusfile.h>
#endif
#include <sqlite3.h>
#include "tagutils.h"
#include "../metadata.h"
@ -49,51 +53,51 @@
#include "../log.h"
struct id3header {
unsigned char id[3];
unsigned char version[2];
unsigned char flags;
unsigned char size[4];
unsigned char id[3];
unsigned char version[2];
unsigned char flags;
unsigned char size[4];
} __attribute((packed));
char *winamp_genre[] = {
/*00*/ "Blues", "Classic Rock", "Country", "Dance",
"Disco", "Funk", "Grunge", "Hip-Hop",
/*08*/ "Jazz", "Metal", "New Age", "Oldies",
"Other", "Pop", "R&B", "Rap",
/*10*/ "Reggae", "Rock", "Techno", "Industrial",
"Alternative", "Ska", "Death Metal", "Pranks",
/*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
"Vocal", "Jazz+Funk", "Fusion", "Trance",
/*20*/ "Classical", "Instrumental", "Acid", "House",
"Game", "Sound Clip", "Gospel", "Noise",
/*28*/ "AlternRock", "Bass", "Soul", "Punk",
"Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
/*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
"Electronic", "Pop-Folk", "Eurodance", "Dream",
/*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta",
"Top 40", "Christian Rap", "Pop/Funk", "Jungle",
/*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic",
"Rave", "Showtunes", "Trailer", "Lo-Fi",
/*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka",
"Retro", "Musical", "Rock & Roll", "Hard Rock",
/*50*/ "Folk", "Folk/Rock", "National folk", "Swing",
"Fast-fusion", "Bebob", "Latin", "Revival",
/*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
"Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
/*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic",
"Humour", "Speech", "Chanson", "Opera",
/*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass",
"Primus", "Porn Groove", "Satire", "Slow Jam",
/*70*/ "Club", "Tango", "Samba", "Folklore",
"Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle",
/*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella",
"Euro-House", "Dance Hall", "Goa", "Drum & Bass",
/*80*/ "Club House", "Hardcore", "Terror", "Indie",
"BritPop", "NegerPunk", "Polsk Punk", "Beat",
/*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
"Contemporary C", "Christian Rock", "Merengue", "Salsa",
/*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop",
"Unknown"
/*00*/ "Blues", "Classic Rock", "Country", "Dance",
"Disco", "Funk", "Grunge", "Hip-Hop",
/*08*/ "Jazz", "Metal", "New Age", "Oldies",
"Other", "Pop", "R&B", "Rap",
/*10*/ "Reggae", "Rock", "Techno", "Industrial",
"Alternative", "Ska", "Death Metal", "Pranks",
/*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
"Vocal", "Jazz+Funk", "Fusion", "Trance",
/*20*/ "Classical", "Instrumental", "Acid", "House",
"Game", "Sound Clip", "Gospel", "Noise",
/*28*/ "AlternRock", "Bass", "Soul", "Punk",
"Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
/*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
"Electronic", "Pop-Folk", "Eurodance", "Dream",
/*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta",
"Top 40", "Christian Rap", "Pop/Funk", "Jungle",
/*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic",
"Rave", "Showtunes", "Trailer", "Lo-Fi",
/*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka",
"Retro", "Musical", "Rock & Roll", "Hard Rock",
/*50*/ "Folk", "Folk/Rock", "National folk", "Swing",
"Fast-fusion", "Bebob", "Latin", "Revival",
/*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
"Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
/*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic",
"Humour", "Speech", "Chanson", "Opera",
/*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass",
"Primus", "Porn Groove", "Satire", "Slow Jam",
/*70*/ "Club", "Tango", "Samba", "Folklore",
"Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle",
/*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella",
"Euro-House", "Dance Hall", "Goa", "Drum & Bass",
/*80*/ "Club House", "Hardcore", "Terror", "Indie",
"BritPop", "NegerPunk", "Polsk Punk", "Beat",
/*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
"Contemporary C", "Christian Rock", "Merengue", "Salsa",
/*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop",
"Unknown"
};
#define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1)
@ -104,8 +108,9 @@ char *winamp_genre[] = {
*/
#include "tagutils-mp3.h"
#include "tagutils-aac.h"
#ifdef HAVE_VORBISFILE
#include "tagutils-ogg.h"
#ifdef HAVE_OPUS
#include "tagutils-opus.h"
#endif
#include "tagutils-flc.h"
#include "tagutils-asf.h"
@ -123,24 +128,25 @@ static int _get_fileinfo(char *file, struct song_metadata *psong);
*/
typedef struct {
char* type;
int (*get_tags)(char* file, struct song_metadata* psong);
int (*get_fileinfo)(char* file, struct song_metadata* psong);
char* type;
int (*get_tags)(char* file, struct song_metadata* psong);
int (*get_fileinfo)(char* file, struct song_metadata* psong);
} taghandler;
static taghandler taghandlers[] = {
{ "aac", _get_aactags, _get_aacfileinfo },
{ "mp3", _get_mp3tags, _get_mp3fileinfo },
{ "flc", _get_flctags, _get_flcfileinfo },
#ifdef HAVE_VORBISFILE
{ "ogg", NULL, _get_oggfileinfo },
{ "aac", _get_aactags, _get_aacfileinfo },
{ "mp3", _get_mp3tags, _get_mp3fileinfo },
{ "flc", _get_flctags, _get_flcfileinfo },
{ "ogg", 0, _get_oggfileinfo },
#ifdef HAVE_OPUS
{ "ops", 0, _get_opusfileinfo },
#endif
{ "asf", NULL, _get_asffileinfo },
{ "wav", _get_wavtags, _get_wavfileinfo },
{ "pcm", NULL, _get_pcmfileinfo },
{ "dsf", _get_dsftags, _get_dsffileinfo },
{ "dff", NULL, _get_dfffileinfo },
{ NULL, NULL, NULL }
{ "asf", 0, _get_asffileinfo },
{ "wav", _get_wavtags, _get_wavfileinfo },
{ "pcm", 0, _get_pcmfileinfo },
{ "dsf", _get_dsftags, _get_dsffileinfo },
{ "dff", 0, _get_dfffileinfo },
{ NULL, NULL, NULL }
};
@ -149,8 +155,9 @@ static taghandler taghandlers[] = {
#include "tagutils-misc.c"
#include "tagutils-mp3.c"
#include "tagutils-aac.c"
#ifdef HAVE_VORBISFILE
#include "tagutils-ogg.c"
#ifdef HAVE_OPUS
#include "tagutils-opus.c"
#endif
#include "tagutils-flc.c"
#include "tagutils-asf.c"
@ -166,86 +173,86 @@ static taghandler taghandlers[] = {
void
freetags(struct song_metadata *psong)
{
int role;
int role;
MAYBEFREE(psong->path);
MAYBEFREE(psong->image);
MAYBEFREE(psong->title);
MAYBEFREE(psong->album);
MAYBEFREE(psong->genre);
MAYBEFREE(psong->comment);
for(role = ROLE_START; role <= ROLE_LAST; role++)
{
MAYBEFREE(psong->contributor[role]);
MAYBEFREE(psong->contributor_sort[role]);
}
MAYBEFREE(psong->grouping);
MAYBEFREE(psong->mime);
MAYBEFREE(psong->dlna_pn);
MAYBEFREE(psong->tagversion);
MAYBEFREE(psong->musicbrainz_albumid);
MAYBEFREE(psong->musicbrainz_trackid);
MAYBEFREE(psong->musicbrainz_artistid);
MAYBEFREE(psong->musicbrainz_albumartistid);
MAYBEFREE(psong->path);
MAYBEFREE(psong->image);
MAYBEFREE(psong->title);
MAYBEFREE(psong->album);
MAYBEFREE(psong->genre);
MAYBEFREE(psong->comment);
for(role = ROLE_START; role <= ROLE_LAST; role++)
{
MAYBEFREE(psong->contributor[role]);
MAYBEFREE(psong->contributor_sort[role]);
}
MAYBEFREE(psong->grouping);
MAYBEFREE(psong->mime);
MAYBEFREE(psong->dlna_pn);
MAYBEFREE(psong->tagversion);
MAYBEFREE(psong->musicbrainz_albumid);
MAYBEFREE(psong->musicbrainz_trackid);
MAYBEFREE(psong->musicbrainz_artistid);
MAYBEFREE(psong->musicbrainz_albumartistid);
}
// _get_fileinfo
static int
_get_fileinfo(char *file, struct song_metadata *psong)
{
taghandler *hdl;
taghandler *hdl;
// dispatch to appropriate tag handler
for(hdl = taghandlers; hdl->type; ++hdl)
if(!strcmp(hdl->type, psong->type))
break;
// dispatch to appropriate tag handler
for(hdl = taghandlers; hdl->type; ++hdl)
if(!strcmp(hdl->type, psong->type))
break;
if(hdl->get_fileinfo)
return hdl->get_fileinfo(file, psong);
if(hdl->get_fileinfo)
return hdl->get_fileinfo(file, psong);
return 0;
return 0;
}
static void
_make_composite_tags(struct song_metadata *psong)
{
int len;
int len;
len = 1;
len = 1;
if(!psong->contributor[ROLE_ARTIST] &&
(psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR]))
{
if(psong->contributor[ROLE_BAND])
len += strlen(psong->contributor[ROLE_BAND]);
if(psong->contributor[ROLE_CONDUCTOR])
len += strlen(psong->contributor[ROLE_CONDUCTOR]);
if(!psong->contributor[ROLE_ARTIST] &&
(psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR]))
{
if(psong->contributor[ROLE_BAND])
len += strlen(psong->contributor[ROLE_BAND]);
if(psong->contributor[ROLE_CONDUCTOR])
len += strlen(psong->contributor[ROLE_CONDUCTOR]);
len += 3;
len += 3;
psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1);
if(psong->contributor[ROLE_ARTIST])
{
if(psong->contributor[ROLE_BAND])
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]);
psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1);
if(psong->contributor[ROLE_ARTIST])
{
if(psong->contributor[ROLE_BAND])
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]);
if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR])
strcat(psong->contributor[ROLE_ARTIST], " - ");
if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR])
strcat(psong->contributor[ROLE_ARTIST], " - ");
if(psong->contributor[ROLE_CONDUCTOR])
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]);
}
}
if(psong->contributor[ROLE_CONDUCTOR])
strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]);
}
}
#if 0 // already taken care of by scanner.c
if(!psong->title)
{
char *suffix;
psong->title = strdup(psong->basename);
suffix = strrchr(psong->title, '.');
if(suffix) *suffix = '\0';
}
if(!psong->title)
{
char *suffix;
psong->title = strdup(psong->basename);
suffix = strrchr(psong->title, '.');
if(suffix) *suffix = '\0';
}
#endif
}
@ -255,19 +262,19 @@ _make_composite_tags(struct song_metadata *psong)
static int
_get_tags(char *file, struct song_metadata *psong)
{
taghandler *hdl;
taghandler *hdl;
// dispatch
for(hdl = taghandlers ; hdl->type ; ++hdl)
if(!strcasecmp(hdl->type, psong->type))
break;
// dispatch
for(hdl = taghandlers ; hdl->type ; ++hdl)
if(!strcasecmp(hdl->type, psong->type))
break;
if(hdl->get_tags)
{
return hdl->get_tags(file, psong);
}
if(hdl->get_tags)
{
return hdl->get_tags(file, psong);
}
return 0;
return 0;
}
/*****************************************************************************/
@ -275,31 +282,31 @@ _get_tags(char *file, struct song_metadata *psong)
int
readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type)
{
char *fname;
char *fname;
if(lang_index == -1)
lang_index = _lang2cp(lang);
if(lang_index == -1)
lang_index = _lang2cp(lang);
memset((void*)psong, 0, sizeof(struct song_metadata));
psong->path = strdup(path);
psong->type = type;
memset((void*)psong, 0, sizeof(struct song_metadata));
psong->path = strdup(path);
psong->type = type;
fname = strrchr(psong->path, '/');
psong->basename = fname ? fname + 1 : psong->path;
fname = strrchr(psong->path, '/');
psong->basename = fname ? fname + 1 : psong->path;
if(stat)
{
if(!psong->time_modified)
psong->time_modified = stat->st_mtime;
psong->file_size = stat->st_size;
}
if(stat)
{
if(!psong->time_modified)
psong->time_modified = stat->st_mtime;
psong->file_size = stat->st_size;
}
// get tag
if( _get_tags(path, psong) == 0 )
{
_make_composite_tags(psong);
}
// get fileinfo
return _get_fileinfo(path, psong);
// get tag
if( _get_tags(path, psong) == 0 )
{
_make_composite_tags(psong);
}
// get fileinfo
return _get_fileinfo(path, psong);
}