-/*
- * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file afh_common.c Common audio format handler functions. */
#include "string.h"
#include "afh.h"
-typedef void afh_init_func(struct audio_format_handler *);
-/* It does not hurt to declare init functions which are not available. */
-extern afh_init_func mp3_init, ogg_init, aac_afh_init, wma_afh_init,
- spx_afh_init, flac_afh_init, opus_afh_init;
-
/** The list of all status items */
-const char *status_item_list[] = {STATUS_ITEM_ARRAY};
+const char *status_item_list[] = {STATUS_ITEMS};
+
+/**
+ * For each audio file the number of its audio format is stored in the
+ * database. Therefore this list, in particular its order, is part of the ABI.
+ * So it's only OK to append new audio formats. All audio formats are listed
+ * here, regardless of whether the audio format handler is compiled in.
+ */
+#define ALL_AUDIO_FORMATS \
+ AUDIO_FORMAT(mp3) \
+ AUDIO_FORMAT(ogg) \
+ AUDIO_FORMAT(aac) \
+ AUDIO_FORMAT(wma) \
+ AUDIO_FORMAT(spx) \
+ AUDIO_FORMAT(flac) \
+ AUDIO_FORMAT(opus) \
+
+/** \cond audio_format_handler */
+#define AUDIO_FORMAT(_fmt) #_fmt,
+static const char * const audio_format_names[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+/* Weak declarations must be public. */
+#define AUDIO_FORMAT(_fmt) \
+ struct audio_format_handler _fmt ## _afh __attribute__ ((weak)) \
+ = {.get_file_info = NULL};
+ALL_AUDIO_FORMATS
+#undef AUDIO_FORMAT
+#define AUDIO_FORMAT(_fmt) & _fmt ## _afh,
+static struct audio_format_handler *afl[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+#define NUM_AUDIO_FORMATS (ARRAY_SIZE(afl))
+/** \endcond audio_format_handler */
/**
- * The list of supported audio formats.
+ * Get the name of the given audio format.
*
- * We always define the full array of audio formats even if some audio formats
- * were not compiled in. This is because for each audio file the number of its
- * audio format is stored in the database. We don't want that numbers to become
- * stale just because the user installed a new version of paraslash that
- * supports a different set of audio formats.
+ * \param i The audio format number.
*
- * It can still be easily detected whether an audio format is compiled in by
- * checking if the init function pointer is not \p NULL.
+ * \return This returns a pointer to statically allocated memory so it
+ * must not be freed by the caller.
*/
-static struct audio_format_handler afl[] = {
- {
- .name = "mp3",
- .init = mp3_init,
- },
- {
- .name = "ogg",
-#if defined(HAVE_OGG) && defined(HAVE_VORBIS)
- .init = ogg_init,
-#endif
- },
- {
- .name = "aac",
-#if defined(HAVE_FAAD)
- .init = aac_afh_init,
-#endif
- },
- {
- .name = "wma",
- .init = wma_afh_init,
- },
- {
- .name = "spx",
-#if defined(HAVE_OGG) && defined(HAVE_SPEEX)
- .init = spx_afh_init,
-#endif
- },
- {
- .name = "flac",
-#if defined(HAVE_OGG) && defined(HAVE_FLAC)
- .init = flac_afh_init,
-#endif
- },
- {
- .name = "opus",
-#if defined(HAVE_OGG) && defined(HAVE_OPUS)
- .init = opus_afh_init,
-#endif
- },
- {
- .name = NULL,
- }
-};
+const char *audio_format_name(int i)
+{
+ if (i < 0 || i >= NUM_AUDIO_FORMATS)
+ return "???";
+ return audio_format_names[i];
+}
static inline int next_audio_format(int format)
{
for (;;) {
- if (!afl[format].name)
- return format;
format++;
- if (afl[format].init)
+ if (format >= NUM_AUDIO_FORMATS)
+ return format;
+ if (afl[format]->get_file_info)
return format;
}
}
/** Iterate over each supported audio format. */
-#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i = next_audio_format(i))
-
-/**
- * Call the init function of each supported audio format handler.
- */
-void afh_init(void)
-{
- int i;
-
- PARA_NOTICE_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
- FOR_EACH_AUDIO_FORMAT(i) {
- PARA_INFO_LOG("initializing %s handler\n",
- audio_format_name(i));
- afl[i].init(&afl[i]);
- }
-}
+#define FOR_EACH_AUDIO_FORMAT(i) \
+ for (i = 0; i < NUM_AUDIO_FORMATS; i = next_audio_format(i))
/**
* Tell whether an audio format handler provides chunk tables.
*/
bool afh_supports_dynamic_chunks(int audio_format_id)
{
- return afl[audio_format_id].get_chunk;
+ return afl[audio_format_id]->get_chunk;
}
/**
int i,j, len = strlen(name);
FOR_EACH_AUDIO_FORMAT(i) {
- for (j = 0; afl[i].suffixes[j]; j++) {
- const char *p = afl[i].suffixes[j];
+ for (j = 0; afl[i]->suffixes[j]; j++) {
+ const char *p = afl[i]->suffixes[j];
int plen = strlen(p);
if (len < plen + 1)
continue;
return -E_AUDIO_FORMAT;
}
-/**
- * Get the name of the given audio format.
- *
- * \param i The audio format number.
- *
- * \return This returns a pointer to statically allocated memory so it
- * must not be freed by the caller.
- */
-const char *audio_format_name(int i)
-{
- if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
- return "???";
- return afl[i].name;
-}
-
static int get_file_info(int format, const char *path, char *data,
size_t size, int fd, struct afh_info *afhi)
{
const char *fmt = audio_format_name(format);
memset(afhi, 0, sizeof(*afhi));
- ret = afl[format].get_file_info(data, size, fd, afhi);
+ ret = afl[format]->get_file_info(data, size, fd, afhi);
if (ret < 0) {
PARA_WARNING_LOG("%s: %s format not detected: %s\n",
path, fmt, para_strerror(-ret));
uint8_t audio_format_id, const void *map, size_t mapsize,
const char **buf, size_t *len, void **afh_context)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (afh_supports_dynamic_chunks(audio_format_id)) {
int ret;
if (ret < 0)
return ret;
}
- ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context,
+ ret = afh->get_chunk(chunk_num, *afh_context,
buf, len);
if (ret < 0) {
afh->close(*afh_context);
*/
void afh_close(void *afh_context, uint8_t audio_format_id)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!afh_supports_dynamic_chunks(audio_format_id))
return;
void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
void *map, size_t mapsize, char **buf, size_t *len)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!map || !afhi || !afhi->header_len) {
*buf = NULL;
*/
void afh_free_header(char *header_buf, uint8_t audio_format_id)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (afh->get_header)
free(header_buf);
"%s: %s\n" /* year */
"%s: %s\n" /* album */
"%s: %s\n", /* comment */
- status_item_list[SI_BITRATE], afhi->bitrate,
- status_item_list[SI_FORMAT], audio_format_name(audio_format_num),
- status_item_list[SI_FREQUENCY], afhi->frequency,
- status_item_list[SI_CHANNELS], afhi->channels,
- status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total,
- status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
+ status_item_list[SI_bitrate], afhi->bitrate,
+ status_item_list[SI_format], audio_format_name(audio_format_num),
+ status_item_list[SI_frequency], afhi->frequency,
+ status_item_list[SI_channels], afhi->channels,
+ status_item_list[SI_seconds_total], afhi->seconds_total,
+ status_item_list[SI_chunk_time], (long unsigned)afhi->chunk_tv.tv_sec,
(long unsigned)afhi->chunk_tv.tv_usec,
- status_item_list[SI_NUM_CHUNKS], afhi->chunks_total,
- status_item_list[SI_MAX_CHUNK_SIZE], afhi->max_chunk_size,
- status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "",
- status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "",
- status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "",
- status_item_list[SI_YEAR], afhi->tags.year? afhi->tags.year : "",
- status_item_list[SI_ALBUM], afhi->tags.album? afhi->tags.album : "",
- status_item_list[SI_COMMENT], afhi->tags.comment? afhi->tags.comment : ""
+ status_item_list[SI_num_chunks], afhi->chunks_total,
+ status_item_list[SI_max_chunk_size], afhi->max_chunk_size,
+ status_item_list[SI_techinfo], afhi->techinfo? afhi->techinfo : "",
+ status_item_list[SI_artist], afhi->tags.artist? afhi->tags.artist : "",
+ status_item_list[SI_title], afhi->tags.title? afhi->tags.title : "",
+ status_item_list[SI_year], afhi->tags.year? afhi->tags.year : "",
+ status_item_list[SI_album], afhi->tags.album? afhi->tags.album : "",
+ status_item_list[SI_comment], afhi->tags.comment? afhi->tags.comment : ""
);
}
int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
struct taginfo *tags, int output_fd, const char *filename)
{
- struct audio_format_handler *afh = afl + audio_format_id;
+ struct audio_format_handler *afh = afl[audio_format_id];
if (!afh->rewrite_tags)
return -ERRNO_TO_PARA_ERROR(ENOTSUP);