Compare commits
10 Commits
v0.16_alph
...
v0.16
Author | SHA1 | Date | |
---|---|---|---|
![]() |
da01c6ef5b | ||
![]() |
fcd2355f4f | ||
![]() |
748a8a6f42 | ||
![]() |
cb9965bab5 | ||
![]() |
429ed24c99 | ||
![]() |
1ab46472ab | ||
![]() |
f6bbe1332f | ||
![]() |
11613347be | ||
![]() |
8f46f1520c | ||
![]() |
f2893b0d0f |
@@ -34,6 +34,7 @@ mpd_headers = \
|
||||
src/check.h \
|
||||
src/notify.h \
|
||||
src/ack.h \
|
||||
src/ape.h \
|
||||
src/audio.h \
|
||||
src/audio_format.h \
|
||||
src/audio_check.h \
|
||||
@@ -186,6 +187,7 @@ mpd_headers = \
|
||||
src/refcount.h \
|
||||
src/replay_gain_config.h \
|
||||
src/replay_gain_info.h \
|
||||
src/replay_gain_ape.h \
|
||||
src/sig_handlers.h \
|
||||
src/song.h \
|
||||
src/song_print.h \
|
||||
@@ -412,6 +414,8 @@ TAG_LIBS = \
|
||||
$(ID3TAG_LIBS)
|
||||
|
||||
TAG_SRC = \
|
||||
src/ape.c \
|
||||
src/replay_gain_ape.c \
|
||||
src/tag_ape.c
|
||||
|
||||
if HAVE_ID3TAG
|
||||
|
5
NEWS
5
NEWS
@@ -1,4 +1,4 @@
|
||||
ver 0.16 (20??/??/??)
|
||||
ver 0.16 (2010/12/11)
|
||||
* protocol:
|
||||
- send song modification time to client
|
||||
- added "update" idle event
|
||||
@@ -20,7 +20,9 @@ ver 0.16 (20??/??/??)
|
||||
* tags:
|
||||
- added tags "ArtistSort", "AlbumArtistSort"
|
||||
- id3: revised "performer" tag support
|
||||
- id3: support multiple values
|
||||
- ape: MusicBrainz tags
|
||||
- ape: support multiple values
|
||||
* decoders:
|
||||
- don't try a plugin twice (MIME type & suffix)
|
||||
- don't fall back to "mad" unless no plugin matches
|
||||
@@ -88,6 +90,7 @@ ver 0.16 (20??/??/??)
|
||||
- fall back to track gain if album gain is unavailable
|
||||
- optionally use hardware mixer to apply replay gain
|
||||
- added mode "auto"
|
||||
- parse replay gain from APE tags
|
||||
* log unused/unknown block parameters
|
||||
* removed the deprecated "error_file" option
|
||||
* save state when stopped
|
||||
|
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.60)
|
||||
AC_INIT(mpd, 0.16~alpha4, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.16, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
113
src/ape.c
Normal file
113
src/ape.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ape.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct ape_footer {
|
||||
unsigned char id[8];
|
||||
uint32_t version;
|
||||
uint32_t length;
|
||||
uint32_t count;
|
||||
unsigned char flags[4];
|
||||
unsigned char reserved[8];
|
||||
};
|
||||
|
||||
static bool
|
||||
ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
|
||||
{
|
||||
/* determine if file has an apeV2 tag */
|
||||
struct ape_footer footer;
|
||||
if (fseek(fp, -(long)sizeof(footer), SEEK_END) ||
|
||||
fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) ||
|
||||
memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
|
||||
GUINT32_FROM_LE(footer.version) != 2000)
|
||||
return false;
|
||||
|
||||
/* find beginning of ape tag */
|
||||
size_t remaining = GUINT32_FROM_LE(footer.length);
|
||||
if (remaining <= sizeof(footer) + 10 ||
|
||||
/* refuse to load more than one megabyte of tag data */
|
||||
remaining > 1024 * 1024 ||
|
||||
fseek(fp, -(long)remaining, SEEK_END))
|
||||
return false;
|
||||
|
||||
/* read tag into buffer */
|
||||
remaining -= sizeof(footer);
|
||||
assert(remaining > 10);
|
||||
|
||||
char *buffer = g_malloc(remaining);
|
||||
if (fread(buffer, 1, remaining, fp) != remaining)
|
||||
return false;
|
||||
|
||||
/* read tags */
|
||||
unsigned n = GUINT32_FROM_LE(footer.count);
|
||||
const char *p = buffer;
|
||||
while (n-- && remaining > 10) {
|
||||
size_t size = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||
p += 4;
|
||||
remaining -= 4;
|
||||
unsigned long flags = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||
p += 4;
|
||||
remaining -= 4;
|
||||
|
||||
/* get the key */
|
||||
const char *key = p;
|
||||
while (remaining > size && *p != '\0') {
|
||||
p++;
|
||||
remaining--;
|
||||
}
|
||||
p++;
|
||||
remaining--;
|
||||
|
||||
/* get the value */
|
||||
if (remaining < size)
|
||||
break;
|
||||
|
||||
if (!callback(flags, key, p, size, ctx))
|
||||
break;
|
||||
|
||||
p += size;
|
||||
remaining -= size;
|
||||
}
|
||||
|
||||
g_free(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path_fs, "rb");
|
||||
if (fp == NULL)
|
||||
return false;
|
||||
|
||||
bool success = ape_scan_internal(fp, callback, ctx);
|
||||
fclose(fp);
|
||||
return success;
|
||||
}
|
42
src/ape.h
Normal file
42
src/ape.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_APE_H
|
||||
#define MPD_APE_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef bool (*tag_ape_callback_t)(unsigned long flags, const char *key,
|
||||
const char *value, size_t value_length,
|
||||
void *ctx);
|
||||
|
||||
/**
|
||||
* Scans the APE tag values from a file.
|
||||
*
|
||||
* @param path_fs the path of the file in filesystem encoding
|
||||
* @return false if the file could not be opened or if no APE tag is
|
||||
* present
|
||||
*/
|
||||
bool
|
||||
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx);
|
||||
|
||||
#endif
|
@@ -1715,15 +1715,11 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
|
||||
}
|
||||
|
||||
sticker = sticker_song_get(song);
|
||||
if (NULL == sticker) {
|
||||
command_error(client, ACK_ERROR_NO_EXIST,
|
||||
"no stickers found");
|
||||
return COMMAND_RETURN_ERROR;
|
||||
if (sticker) {
|
||||
sticker_print(client, sticker);
|
||||
sticker_free(sticker);
|
||||
}
|
||||
|
||||
sticker_print(client, sticker);
|
||||
sticker_free(sticker);
|
||||
|
||||
return COMMAND_RETURN_OK;
|
||||
/* set song song_id id key */
|
||||
} else if (argc == 6 && strcmp(argv[1], "set") == 0) {
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "decoder_list.h"
|
||||
#include "decoder_plugin.h"
|
||||
#include "decoder_api.h"
|
||||
#include "replay_gain_ape.h"
|
||||
#include "input_stream.h"
|
||||
#include "player_control.h"
|
||||
#include "pipe.h"
|
||||
@@ -297,6 +298,18 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load replay gain data, and pass it to
|
||||
* decoder_replay_gain().
|
||||
*/
|
||||
static void
|
||||
decoder_load_replay_gain(struct decoder *decoder, const char *path_fs)
|
||||
{
|
||||
struct replay_gain_info info;
|
||||
if (replay_gain_ape_read(path_fs, &info))
|
||||
decoder_replay_gain(decoder, &info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try decoding a file.
|
||||
*/
|
||||
@@ -312,6 +325,8 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
|
||||
|
||||
decoder_unlock(dc);
|
||||
|
||||
decoder_load_replay_gain(decoder, path_fs);
|
||||
|
||||
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
|
||||
if (plugin->file_decode != NULL) {
|
||||
decoder_lock(dc);
|
||||
|
78
src/replay_gain_ape.c
Normal file
78
src/replay_gain_ape.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "replay_gain_ape.h"
|
||||
#include "replay_gain_info.h"
|
||||
#include "ape.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct rg_ape_ctx {
|
||||
struct replay_gain_info *info;
|
||||
bool found;
|
||||
};
|
||||
|
||||
static bool
|
||||
replay_gain_ape_callback(unsigned long flags, const char *key,
|
||||
const char *_value, size_t value_length, void *_ctx)
|
||||
{
|
||||
struct rg_ape_ctx *ctx = _ctx;
|
||||
|
||||
/* we only care about utf-8 text tags */
|
||||
if ((flags & (0x3 << 1)) != 0)
|
||||
return true;
|
||||
|
||||
char value[16];
|
||||
if (value_length >= sizeof(value))
|
||||
return true;
|
||||
memcpy(value, _value, value_length);
|
||||
value[value_length] = 0;
|
||||
|
||||
if (g_ascii_strcasecmp(key, "replaygain_track_gain") == 0) {
|
||||
ctx->info->tuples[REPLAY_GAIN_TRACK].gain = atof(value);
|
||||
ctx->found = true;
|
||||
} else if (g_ascii_strcasecmp(key, "replaygain_album_gain") == 0) {
|
||||
ctx->info->tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
|
||||
ctx->found = true;
|
||||
} else if (g_ascii_strcasecmp(key, "replaygain_track_peak") == 0) {
|
||||
ctx->info->tuples[REPLAY_GAIN_TRACK].peak = atof(value);
|
||||
ctx->found = true;
|
||||
} else if (g_ascii_strcasecmp(key, "replaygain_album_peak") == 0) {
|
||||
ctx->info->tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
|
||||
ctx->found = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info)
|
||||
{
|
||||
struct rg_ape_ctx ctx = {
|
||||
.info = info,
|
||||
.found = false,
|
||||
};
|
||||
|
||||
return tag_ape_scan(path_fs, replay_gain_ape_callback, &ctx) &&
|
||||
ctx.found;
|
||||
}
|
32
src/replay_gain_ape.h
Normal file
32
src/replay_gain_ape.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_REPLAY_GAIN_APE_H
|
||||
#define MPD_REPLAY_GAIN_APE_H
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct replay_gain_info;
|
||||
|
||||
bool
|
||||
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info);
|
||||
|
||||
#endif
|
128
src/tag_ape.c
128
src/tag_ape.c
@@ -21,11 +21,7 @@
|
||||
#include "tag_ape.h"
|
||||
#include "tag.h"
|
||||
#include "tag_table.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "ape.h"
|
||||
|
||||
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
|
||||
[TAG_ALBUM_ARTIST] = "album artist",
|
||||
@@ -56,101 +52,45 @@ tag_ape_import_item(struct tag *tag, unsigned long flags,
|
||||
|
||||
if (tag == NULL)
|
||||
tag = tag_new();
|
||||
tag_add_item_n(tag, type, value, value_length);
|
||||
|
||||
const char *end = value + value_length;
|
||||
while (true) {
|
||||
/* multiple values are separated by null bytes */
|
||||
const char *n = memchr(value, 0, end - value);
|
||||
if (n != NULL) {
|
||||
if (n > value)
|
||||
tag_add_item_n(tag, type, value, n - value);
|
||||
value = n + 1;
|
||||
} else {
|
||||
if (end > value)
|
||||
tag_add_item_n(tag, type, value, end - value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag_ape_ctx {
|
||||
struct tag *tag;
|
||||
};
|
||||
|
||||
static bool
|
||||
tag_ape_callback(unsigned long flags, const char *key,
|
||||
const char *value, size_t value_length, void *_ctx)
|
||||
{
|
||||
struct tag_ape_ctx *ctx = _ctx;
|
||||
|
||||
ctx->tag = tag_ape_import_item(ctx->tag, flags, key,
|
||||
value, value_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
tag_ape_load(const char *file)
|
||||
{
|
||||
struct tag *ret = NULL;
|
||||
FILE *fp;
|
||||
int tagCount;
|
||||
char *buffer = NULL;
|
||||
char *p;
|
||||
size_t tagLen;
|
||||
size_t size;
|
||||
unsigned long flags;
|
||||
char *key;
|
||||
struct tag_ape_ctx ctx = { .tag = NULL };
|
||||
|
||||
struct {
|
||||
unsigned char id[8];
|
||||
uint32_t version;
|
||||
uint32_t length;
|
||||
uint32_t tagCount;
|
||||
unsigned char flags[4];
|
||||
unsigned char reserved[8];
|
||||
} footer;
|
||||
|
||||
fp = fopen(file, "rb");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
/* determine if file has an apeV2 tag */
|
||||
if (fseek(fp, 0, SEEK_END))
|
||||
goto fail;
|
||||
size = (size_t)ftell(fp);
|
||||
if (fseek(fp, size - sizeof(footer), SEEK_SET))
|
||||
goto fail;
|
||||
if (fread(&footer, 1, sizeof(footer), fp) != sizeof(footer))
|
||||
goto fail;
|
||||
if (memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0)
|
||||
goto fail;
|
||||
if (GUINT32_FROM_LE(footer.version) != 2000)
|
||||
goto fail;
|
||||
|
||||
/* find beginning of ape tag */
|
||||
tagLen = GUINT32_FROM_LE(footer.length);
|
||||
if (tagLen <= sizeof(footer) + 10)
|
||||
goto fail;
|
||||
if (tagLen > 1024 * 1024)
|
||||
/* refuse to load more than one megabyte of tag data */
|
||||
goto fail;
|
||||
if (fseek(fp, size - tagLen, SEEK_SET))
|
||||
goto fail;
|
||||
|
||||
/* read tag into buffer */
|
||||
tagLen -= sizeof(footer);
|
||||
assert(tagLen > 10);
|
||||
|
||||
buffer = g_malloc(tagLen);
|
||||
if (fread(buffer, 1, tagLen, fp) != tagLen)
|
||||
goto fail;
|
||||
|
||||
/* read tags */
|
||||
tagCount = GUINT32_FROM_LE(footer.tagCount);
|
||||
p = buffer;
|
||||
while (tagCount-- && tagLen > 10) {
|
||||
size = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||
p += 4;
|
||||
tagLen -= 4;
|
||||
flags = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||
p += 4;
|
||||
tagLen -= 4;
|
||||
|
||||
/* get the key */
|
||||
key = p;
|
||||
while (tagLen > size && *p != '\0') {
|
||||
p++;
|
||||
tagLen--;
|
||||
}
|
||||
p++;
|
||||
tagLen--;
|
||||
|
||||
/* get the value */
|
||||
if (tagLen < size)
|
||||
goto fail;
|
||||
|
||||
ret = tag_ape_import_item(ret, flags, key, p, size);
|
||||
|
||||
p += size;
|
||||
tagLen -= size;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
g_free(buffer);
|
||||
return ret;
|
||||
tag_ape_scan(file, tag_ape_callback, &ctx);
|
||||
return ctx.tag;
|
||||
}
|
||||
|
@@ -126,17 +126,16 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
|
||||
* - string list
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
enum tag_type type)
|
||||
tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
|
||||
const struct id3_frame *frame,
|
||||
enum tag_type type)
|
||||
{
|
||||
struct id3_frame const *frame;
|
||||
id3_ucs4_t const *ucs4;
|
||||
id3_utf8_t *utf8;
|
||||
union id3_field const *field;
|
||||
unsigned int nstrings, i;
|
||||
|
||||
frame = id3_tag_findframe(tag, id, 0);
|
||||
if (frame == NULL || frame->nfields != 2)
|
||||
if (frame->nfields != 2)
|
||||
return;
|
||||
|
||||
/* check the encoding field */
|
||||
@@ -170,6 +169,20 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all text frames with the specified id (ID3v2.4.0 section
|
||||
* 4.2). This is a wrapper for tag_id3_import_text_frame().
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
enum tag_type type)
|
||||
{
|
||||
const struct id3_frame *frame;
|
||||
for (unsigned i = 0;
|
||||
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
||||
tag_id3_import_text_frame(dest, tag, frame, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a "Comment frame" (ID3v2.4.0 section 4.10). It
|
||||
* contains 4 fields:
|
||||
@@ -180,16 +193,15 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
* - full string (we use this one)
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
enum tag_type type)
|
||||
tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
|
||||
const struct id3_frame *frame,
|
||||
enum tag_type type)
|
||||
{
|
||||
struct id3_frame const *frame;
|
||||
id3_ucs4_t const *ucs4;
|
||||
id3_utf8_t *utf8;
|
||||
union id3_field const *field;
|
||||
|
||||
frame = id3_tag_findframe(tag, id, 0);
|
||||
if (frame == NULL || frame->nfields != 4)
|
||||
if (frame->nfields != 4)
|
||||
return;
|
||||
|
||||
/* for now I only read the 4th field, with the fullstring */
|
||||
@@ -209,6 +221,20 @@ tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
g_free(utf8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all comment frames (ID3v2.4.0 section 4.10). This is a
|
||||
* wrapper for tag_id3_import_comment_frame().
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
|
||||
enum tag_type type)
|
||||
{
|
||||
const struct id3_frame *frame;
|
||||
for (unsigned i = 0;
|
||||
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
||||
tag_id3_import_comment_frame(dest, tag, frame, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a TXXX name, and convert it to a tag_type enum value.
|
||||
* Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood.
|
||||
|
@@ -74,7 +74,7 @@ void timer_add(Timer *timer, int size)
|
||||
unsigned
|
||||
timer_delay(const Timer *timer)
|
||||
{
|
||||
int64_t delay = (timer->time - now()) / 1000;
|
||||
int64_t delay = (int64_t)(timer->time - now()) / 1000;
|
||||
if (delay < 0)
|
||||
return 0;
|
||||
|
||||
|
Reference in New Issue
Block a user