[SCM] libgroove/upstream: add acoustid fingerprinter sink. closes #19

andrewrk-guest at users.alioth.debian.org andrewrk-guest at users.alioth.debian.org
Sat Apr 19 22:37:15 UTC 2014


The following commit has been merged in the upstream branch:
commit ac4c373bfefe680fb13a7ce56ff3f62cf6d63e96
Author: Andrew Kelley <superjoe30 at gmail.com>
Date:   Sat Apr 19 01:49:12 2014 -0700

    add acoustid fingerprinter sink. closes #19

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4dd375a..616f15f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,15 @@ else(DISABLE_LOUDNESS)
   file(GLOB_RECURSE LIBGROOVE_LOUDNESS_HEADERS ${CMAKE_SOURCE_DIR}/grooveloudness/*.h)
 endif(DISABLE_LOUDNESS)
 
+if(DISABLE_FINGERPRINTER)
+  set(LIBGROOVE_FINGERPRINTER_STATUS "no")
+else(DISABLE_FINGERPRINTER)
+  set(LIBGROOVE_FINGERPRINTER_STATUS "yes")
+  message("Configuring libgroovefingerprinter version ${LIBGROOVE_VERSION}")
+  file(GLOB_RECURSE LIBGROOVE_FINGERPRINTER_SOURCES ${CMAKE_SOURCE_DIR}/groovefingerprinter/*.c)
+  file(GLOB_RECURSE LIBGROOVE_FINGERPRINTER_HEADERS ${CMAKE_SOURCE_DIR}/groovefingerprinter/*.h)
+endif(DISABLE_FINGERPRINTER)
+
 # check for C99
 find_package(C99)
 if(C99_FLAG_DETECTED)
@@ -63,6 +72,18 @@ else(DISABLE_LOUDNESS)
   endif(EBUR128_FOUND)
 endif(DISABLE_LOUDNESS)
 
+# check for chromaprint
+if(DISABLE_FINGERPRINTER)
+  set(STATUS_CHROMAPRINT, "not needed")
+else(DISABLE_FINGERPRINTER)
+  find_package(Chromaprint)
+  if(CHROMAPRINT_FOUND)
+    set(STATUS_CHROMAPRINT "OK")
+  else(CHROMAPRINT_FOUND)
+    set(STATUS_CHROMAPRINT "not found")
+  endif(CHROMAPRINT_FOUND)
+endif(DISABLE_FINGERPRINTER)
+
 # check for SDL2
 if(DISABLE_PLAYER)
   set(STATUS_SDL2 "not needed")
@@ -379,6 +400,42 @@ else()
   add_dependencies(replaygain groove grooveloudness)
 endif()
 
+if(DISABLE_FINGERPRINTER)
+else()
+  add_library(groovefingerprinter SHARED
+    ${LIBGROOVE_FINGERPRINTER_SOURCES}
+    ${LIBGROOVE_FINGERPRINTER_HEADERS})
+  set_target_properties(groovefingerprinter PROPERTIES
+    SOVERSION ${LIBGROOVE_VERSION_MAJOR}
+    VERSION ${LIBGROOVE_VERSION}
+    COMPILE_FLAGS ${LIB_CFLAGS}
+    )
+  target_link_libraries(groovefingerprinter LINK_PUBLIC groove ${CMAKE_THREAD_LIBS_INIT})
+  add_dependencies(groovefingerprinter groove)
+  target_link_libraries(groovefingerprinter LINK_PRIVATE ${CHROMAPRINT_LIBRARIES})
+  include_directories(${CHROMAPRINT_INCLUDE_DIR})
+
+  install(FILES "groovefingerprinter/fingerprinter.h" DESTINATION "include/groovefingerprinter")
+  install(TARGETS groovefingerprinter DESTINATION lib)
+
+
+  add_library(groovefingerprinter_static STATIC
+    ${LIBGROOVE_FINGERPRINTER_SOURCES}
+    ${LIBGROOVE_FINGERPRINTER_HEADERS})
+  set_target_properties(groovefingerprinter_static PROPERTIES
+    OUTPUT_NAME groovefingerprinter
+    COMPILE_FLAGS "${LIB_CFLAGS} -fPIC")
+  install(TARGETS groovefingerprinter_static DESTINATION lib)
+
+
+  add_executable(fingerprint example/fingerprint.c)
+  set_target_properties(fingerprint PROPERTIES
+    COMPILE_FLAGS ${EXAMPLE_CFLAGS})
+  include_directories(${EXAMPLE_INCLUDES})
+  target_link_libraries(fingerprint groove groovefingerprinter)
+  add_dependencies(fingerprint groove groovefingerprinter)
+endif()
+
 message("\n"
 "Installation Summary\n"
 "--------------------\n"
@@ -386,6 +443,7 @@ message("\n"
 "* Build libgroove              : ${LIBGROOVE_STATUS}\n"
 "* Build libgrooveplayer        : ${LIBGROOVE_PLAYER_STATUS}\n"
 "* Build libgrooveloudness      : ${LIBGROOVE_LOUDNESS_STATUS}\n"
+"* Build libgroovefingerprinter : ${LIBGROOVE_FINGERPRINTER_STATUS}\n"
 )
 
 message(
@@ -403,6 +461,7 @@ message(
 "* threads                      : ${STATUS_THREADS}\n"
 "* SDL2                         : ${STATUS_SDL2}\n"
 "* ebur128                      : ${STATUS_EBUR128}\n"
+"* chromaprint                  : ${STATUS_CHROMAPRINT}\n"
 "* libavformat                  : ${STATUS_LIBAVFORMAT}\n"
 "* libavcodec                   : ${STATUS_LIBAVCODEC}\n"
 "* libavfilter                  : ${STATUS_LIBAVFILTER}\n"
diff --git a/README.md b/README.md
index e19d446..fc3682b 100644
--- a/README.md
+++ b/README.md
@@ -24,14 +24,15 @@ music player.
    * **loudness scanner sink** - uses the [EBU R 128](http://tech.ebu.ch/loudness)
      standard to detect loudness. The values it produces are compatible with
      [ReplayGain](http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification).
-   * ([on the roadmap](https://github.com/andrewrk/libgroove/issues/19)) accoustid fingerprint
+   * **fingerprint sink** - uses [chromaprint](acoustid.org/chromaprint) to
+     generate unique song IDs that can be used with the acoustid service.
  * Thread-safe.
  * Example programs included:
    * `playlist` - play a series of songs with gapless playback
    * `metadata` - read or update song metadata
    * `replaygain` - report the suggested replaygain for a set of files
    * `transcode` - transcode one or more files into one output file
- * Cross-platform.
+   * `fingerprint` - generate acoustid fingerprints for one or more files
 
 ## Dependencies
 
@@ -45,6 +46,7 @@ so that you can still compile if they cannot be found on your system.
    - [libmp3lame-dev](http://lame.sourceforge.net/)
  * [libebur128](https://github.com/jiixyj/libebur128) (bundled)
  * [libsdl2-dev](http://www.libsdl.org/) (bundled)
+ * [libchromaprint-dev](http://acoustid.org/chromaprint)
 
 ## Installation
 
@@ -55,7 +57,7 @@ so that you can still compile if they cannot be found on your system.
    ```
    sudo apt-add-repository ppa:andrewrk/libgroove
    sudo apt-get update
-   sudo apt-get install libgroove-dev libgrooveplayer-dev libgrooveloudness-dev
+   sudo apt-get install libgroove-dev libgrooveplayer-dev libgrooveloudness-dev libgroovefingerprinter-dev
    ```
  * [Debian experimental](http://serverfault.com/questions/22414)
 
@@ -69,9 +71,9 @@ so that you can still compile if they cannot be found on your system.
 ## Documentation
 
  * Check out the example programs in the example folder.
- * Read some header files for the relevant APIs:
+ * Read header files for the relevant APIs:
    * groove/groove.h
-     - global stuff
+     - globals
      - GrooveFile
      - GroovePlaylist
      - GrooveBuffer
@@ -82,6 +84,8 @@ so that you can still compile if they cannot be found on your system.
      - GroovePlayer
    * grooveloudness/loudness.h
      - GrooveLoudnessDetector
+   * groovefingerprinter/fingerprinter.h
+     - GrooveFingerprinter
  * Join #libgroove on irc.freenode.org and ask questions.
 
 ## Projects Using libgroove
diff --git a/cmake/FindChromaprint.cmake b/cmake/FindChromaprint.cmake
new file mode 100644
index 0000000..5b093de
--- /dev/null
+++ b/cmake/FindChromaprint.cmake
@@ -0,0 +1,22 @@
+# copied from https://bitbucket.org/acoustid/acoustid-fingerprinter
+# GPLv2 license
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE(PkgConfig)
+PKG_CHECK_MODULES(PKG_LIBCHROMAPRINT libchromaprint)
+
+FIND_PATH(CHROMAPRINT_INCLUDE_DIR chromaprint.h
+    ${PKG_LIBCHROMAPRINT_INCLUDE_DIRS}
+    /usr/include
+    /usr/local/include
+)
+
+FIND_LIBRARY(CHROMAPRINT_LIBRARIES
+    NAMES
+    chromaprint chromaprint.dll
+    PATHS
+    ${PKG_LIBCHROMAPRINT_LIBRARY_DIRS}
+    /usr/lib
+    /usr/local/lib
+)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Chromaprint DEFAULT_MSG CHROMAPRINT_LIBRARIES CHROMAPRINT_INCLUDE_DIR)
diff --git a/example/fingerprint.c b/example/fingerprint.c
new file mode 100644
index 0000000..4172004
--- /dev/null
+++ b/example/fingerprint.c
@@ -0,0 +1,59 @@
+/* compute the acoustid of a list of songs */
+
+#include <groovefingerprinter/fingerprinter.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char * argv[]) {
+    if (argc < 2) {
+        fprintf(stderr, "Usage: %s file1 file2 ...\n", argv[0]);
+        return 1;
+    }
+
+    groove_init();
+    atexit(groove_finish);
+    groove_set_logging(GROOVE_LOG_INFO);
+
+    struct GroovePlaylist *playlist = groove_playlist_create();
+
+    for (int i = 1; i < argc; i += 1) {
+        char * filename = argv[i];
+        struct GrooveFile * file = groove_file_open(filename);
+        if (!file) {
+            fprintf(stderr, "Unable to open %s\n", filename);
+            continue;
+        }
+        groove_playlist_insert(playlist, file, 1.0, NULL);
+    }
+
+    struct GrooveFingerprinter *printer = groove_fingerprinter_create();
+    groove_fingerprinter_attach(printer, playlist);
+
+    struct GrooveFingerprinterInfo info;
+    while (groove_fingerprinter_info_get(printer, &info, 1) == 1) {
+        if (info.item) {
+            fprintf(stdout, "\nduration: %f: %s\n",
+                    info.duration,
+                    info.item->file->filename);
+            fprintf(stdout, "%s\n", info.fingerprint);
+            groove_fingerprinter_free_info(&info);
+        } else {
+            break;
+        }
+    }
+
+    struct GroovePlaylistItem *item = playlist->head;
+    while (item) {
+        struct GrooveFile *file = item->file;
+        struct GroovePlaylistItem *next = item->next;
+        groove_playlist_remove(playlist, item);
+        groove_file_close(file);
+        item = next;
+    }
+
+    groove_fingerprinter_detach(printer);
+    groove_fingerprinter_destroy(printer);
+    groove_playlist_destroy(playlist);
+
+    return 0;
+}
diff --git a/groovefingerprinter/fingerprinter.c b/groovefingerprinter/fingerprinter.c
new file mode 100644
index 0000000..3907f5c
--- /dev/null
+++ b/groovefingerprinter/fingerprinter.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2013 Andrew Kelley
+ *
+ * This file is part of libgroove, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "fingerprinter.h"
+#include <groove/queue.h>
+
+#include <chromaprint.h>
+
+#include <libavutil/mem.h>
+#include <libavutil/log.h>
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+struct GrooveFingerprinterPrivate {
+    struct GrooveFingerprinter externals;
+
+    int state_history_count;
+
+    // index into all_track_states
+    struct GrooveSink *sink;
+    struct GrooveQueue *info_queue;
+    pthread_t thread_id;
+
+    // info_head_mutex applies to variables inside this block.
+    pthread_mutex_t info_head_mutex;
+    char info_head_mutex_inited;
+    // current playlist item pointer
+    struct GroovePlaylistItem *info_head;
+    double info_pos;
+    // analyze_thread waits on this when the info queue is full
+    pthread_cond_t drain_cond;
+    char drain_cond_inited;
+    // how many items are in the queue
+    int info_queue_count;
+    double track_duration;
+    double album_duration;
+
+    ChromaprintContext *chroma_ctx;
+
+    // set temporarily
+    struct GroovePlaylistItem *purge_item;
+
+    int abort_request;
+};
+
+static int emit_track_info(struct GrooveFingerprinterPrivate *p) {
+    struct GrooveFingerprinterInfo *info = av_mallocz(sizeof(struct GrooveFingerprinterInfo));
+    if (!info) {
+        av_log(NULL, AV_LOG_ERROR, "unable to allocate fingerprinter info\n");
+        return -1;
+    }
+    info->item = p->info_head;
+    info->duration = p->track_duration;
+
+    chromaprint_finish(p->chroma_ctx);
+    chromaprint_get_fingerprint(p->chroma_ctx, &info->fingerprint);
+
+    groove_queue_put(p->info_queue, info);
+
+    return 0;
+}
+
+static void *print_thread(void *arg) {
+    struct GrooveFingerprinterPrivate *p = arg;
+    struct GrooveFingerprinter *printer = &p->externals;
+
+    struct GrooveBuffer *buffer;
+    while (!p->abort_request) {
+        pthread_mutex_lock(&p->info_head_mutex);
+
+        if (p->info_queue_count >= printer->info_queue_size) {
+            pthread_cond_wait(&p->drain_cond, &p->info_head_mutex);
+            pthread_mutex_unlock(&p->info_head_mutex);
+            continue;
+        }
+
+        // we definitely want to unlock the mutex while we wait for the
+        // next buffer. Otherwise there will be a deadlock when sink_flush or
+        // sink_purge is called.
+        pthread_mutex_unlock(&p->info_head_mutex);
+
+        int result = groove_sink_buffer_get(p->sink, &buffer, 1);
+
+        pthread_mutex_lock(&p->info_head_mutex);
+
+        if (result == GROOVE_BUFFER_END) {
+            // last file info
+            emit_track_info(p);
+
+            // send album info
+            struct GrooveFingerprinterInfo *info = av_mallocz(
+                    sizeof(struct GrooveFingerprinterInfo));
+            if (info) {
+                info->duration = p->album_duration;
+                groove_queue_put(p->info_queue, info);
+            } else {
+                av_log(NULL, AV_LOG_ERROR, "unable to allocate album fingerprint info\n");
+            }
+
+            p->album_duration = 0.0;
+
+            p->info_head = NULL;
+            p->info_pos = -1.0;
+
+            pthread_mutex_unlock(&p->info_head_mutex);
+            continue;
+        }
+
+        if (result != GROOVE_BUFFER_YES) {
+            pthread_mutex_unlock(&p->info_head_mutex);
+            break;
+        }
+
+        if (buffer->item != p->info_head) {
+            if (p->info_head) {
+                emit_track_info(p);
+            }
+            chromaprint_start(p->chroma_ctx, 44100, 2);
+            p->track_duration = 0.0;
+            p->info_head = buffer->item;
+            p->info_pos = buffer->pos;
+        }
+
+        double buffer_duration = buffer->frame_count / (double)buffer->format.sample_rate;
+        p->track_duration += buffer_duration;
+        p->album_duration += buffer_duration;
+        chromaprint_feed(p->chroma_ctx, buffer->data[0], buffer->frame_count * 2);
+
+        pthread_mutex_unlock(&p->info_head_mutex);
+        groove_buffer_unref(buffer);
+    }
+
+    return NULL;
+}
+
+static void info_queue_cleanup(struct GrooveQueue* queue, void *obj) {
+    struct GrooveFingerprinterInfo *info = obj;
+    struct GrooveFingerprinterPrivate *p = queue->context;
+    p->info_queue_count -= 1;
+    av_free(info);
+}
+
+static void info_queue_put(struct GrooveQueue *queue, void *obj) {
+    struct GrooveFingerprinterPrivate *p = queue->context;
+    p->info_queue_count += 1;
+}
+
+static void info_queue_get(struct GrooveQueue *queue, void *obj) {
+    struct GrooveFingerprinterPrivate *p = queue->context;
+    struct GrooveFingerprinter *printer = &p->externals;
+
+    p->info_queue_count -= 1;
+
+    if (p->info_queue_count < printer->info_queue_size)
+        pthread_cond_signal(&p->drain_cond);
+}
+
+static int info_queue_purge(struct GrooveQueue* queue, void *obj) {
+    struct GrooveFingerprinterInfo *info = obj;
+    struct GrooveFingerprinterPrivate *p = queue->context;
+
+    return info->item == p->purge_item;
+}
+
+static void sink_purge(struct GrooveSink *sink, struct GroovePlaylistItem *item) {
+    struct GrooveFingerprinterPrivate *p = sink->userdata;
+
+    pthread_mutex_lock(&p->info_head_mutex);
+    p->purge_item = item;
+    groove_queue_purge(p->info_queue);
+    p->purge_item = NULL;
+
+    if (p->info_head == item) {
+        p->info_head = NULL;
+        p->info_pos = -1.0;
+    }
+    pthread_cond_signal(&p->drain_cond);
+    pthread_mutex_unlock(&p->info_head_mutex);
+}
+
+static void sink_flush(struct GrooveSink *sink) {
+    struct GrooveFingerprinterPrivate *p = sink->userdata;
+
+    pthread_mutex_lock(&p->info_head_mutex);
+    groove_queue_flush(p->info_queue);
+    p->track_duration = 0.0;
+    p->info_head = NULL;
+    p->info_pos = -1.0;
+
+    pthread_cond_signal(&p->drain_cond);
+    pthread_mutex_unlock(&p->info_head_mutex);
+}
+
+struct GrooveFingerprinter *groove_fingerprinter_create(void) {
+    struct GrooveFingerprinterPrivate *p = av_mallocz(sizeof(struct GrooveFingerprinterPrivate));
+    if (!p) {
+        av_log(NULL, AV_LOG_ERROR, "unable to allocate fingerprinter\n");
+        return NULL;
+    }
+
+    struct GrooveFingerprinter *printer = &p->externals;
+
+    if (pthread_mutex_init(&p->info_head_mutex, NULL) != 0) {
+        groove_fingerprinter_destroy(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to create mutex\n");
+        return NULL;
+    }
+    p->info_head_mutex_inited = 1;
+
+    if (pthread_cond_init(&p->drain_cond, NULL) != 0) {
+        groove_fingerprinter_destroy(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to create mutex condition\n");
+        return NULL;
+    }
+    p->drain_cond_inited = 1;
+
+    p->info_queue = groove_queue_create();
+    if (!p->info_queue) {
+        groove_fingerprinter_destroy(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to allocate queue\n");
+        return NULL;
+    }
+    p->info_queue->context = printer;
+    p->info_queue->cleanup = info_queue_cleanup;
+    p->info_queue->put = info_queue_put;
+    p->info_queue->get = info_queue_get;
+    p->info_queue->purge = info_queue_purge;
+
+    p->sink = groove_sink_create();
+    if (!p->sink) {
+        groove_fingerprinter_destroy(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to allocate sink\n");
+        return NULL;
+    }
+    p->sink->audio_format.sample_rate = 44100;
+    p->sink->audio_format.channel_layout = GROOVE_CH_LAYOUT_STEREO;
+    p->sink->audio_format.sample_fmt = GROOVE_SAMPLE_FMT_S16;
+    p->sink->userdata = printer;
+    p->sink->purge = sink_purge;
+    p->sink->flush = sink_flush;
+
+    // set some defaults
+    printer->info_queue_size = INT_MAX;
+    printer->sink_buffer_size = p->sink->buffer_size;
+
+    return printer;
+}
+
+void groove_fingerprinter_destroy(struct GrooveFingerprinter *printer) {
+    if (!printer)
+        return;
+
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+
+    if (p->sink)
+        groove_sink_destroy(p->sink);
+
+    if (p->info_queue)
+        groove_queue_destroy(p->info_queue);
+
+    if (p->info_head_mutex_inited)
+        pthread_mutex_destroy(&p->info_head_mutex);
+
+    if (p->drain_cond_inited)
+        pthread_cond_destroy(&p->drain_cond);
+
+    av_free(p);
+}
+
+int groove_fingerprinter_attach(struct GrooveFingerprinter *printer,
+        struct GroovePlaylist *playlist)
+{
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+
+    printer->playlist = playlist;
+    groove_queue_reset(p->info_queue);
+
+    p->chroma_ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT);
+
+    if (groove_sink_attach(p->sink, playlist) < 0) {
+        groove_fingerprinter_detach(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to attach sink\n");
+        return -1;
+    }
+
+    if (pthread_create(&p->thread_id, NULL, print_thread, printer) != 0) {
+        groove_fingerprinter_detach(printer);
+        av_log(NULL, AV_LOG_ERROR, "unable to create printer thread\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int groove_fingerprinter_detach(struct GrooveFingerprinter *printer) {
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+
+    p->abort_request = 1;
+    groove_sink_detach(p->sink);
+    groove_queue_flush(p->info_queue);
+    groove_queue_abort(p->info_queue);
+    pthread_cond_signal(&p->drain_cond);
+    pthread_join(p->thread_id, NULL);
+
+    printer->playlist = NULL;
+
+    if (p->chroma_ctx) {
+        chromaprint_free(p->chroma_ctx);
+        p->chroma_ctx = NULL;
+    }
+
+    p->abort_request = 0;
+    p->info_head = NULL;
+    p->info_pos = 0;
+    p->track_duration = 0.0;
+
+    return 0;
+}
+
+int groove_fingerprinter_info_get(struct GrooveFingerprinter *printer,
+        struct GrooveFingerprinterInfo *info, int block)
+{
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+
+    struct GrooveFingerprinterInfo *info_ptr;
+    if (groove_queue_get(p->info_queue, (void**)&info_ptr, block) == 1) {
+        *info = *info_ptr;
+        av_free(info_ptr);
+        return 1;
+    }
+
+    return 0;
+}
+
+int groove_fingerprinter_info_peek(struct GrooveFingerprinter *printer,
+        int block)
+{
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+    return groove_queue_peek(p->info_queue, block);
+}
+
+void groove_fingerprinter_position(struct GrooveFingerprinter *printer,
+        struct GroovePlaylistItem **item, double *seconds)
+{
+    struct GrooveFingerprinterPrivate *p = (struct GrooveFingerprinterPrivate *) printer;
+
+    pthread_mutex_lock(&p->info_head_mutex);
+
+    if (item)
+        *item = p->info_head;
+
+    if (seconds)
+        *seconds = p->info_pos;
+
+    pthread_mutex_unlock(&p->info_head_mutex);
+}
+
+void groove_fingerprinter_free_info(struct GrooveFingerprinterInfo *info) {
+    if (!info->fingerprint) return;
+    chromaprint_dealloc((void*)info->fingerprint);
+    info->fingerprint = NULL;
+}
+
+int groove_fingerprinter_encode(void *fp, int size, char **encoded_fp) {
+    int encoded_size;
+    return chromaprint_encode_fingerprint(fp, size,
+            CHROMAPRINT_ALGORITHM_DEFAULT, (void*)encoded_fp, &encoded_size, 1);
+}
+
+int groove_fingerprinter_decode(char *encoded_fp, void **fp, int *size) {
+    int algorithm;
+    int encoded_size = strlen(encoded_fp);
+    return chromaprint_decode_fingerprint(encoded_fp, encoded_size, fp, size,
+            &algorithm, 1);
+}
+
+void groove_fingerprinter_dealloc(void *ptr) {
+    if (!ptr) return;
+    chromaprint_dealloc(ptr);
+}
diff --git a/groovefingerprinter/fingerprinter.h b/groovefingerprinter/fingerprinter.h
new file mode 100644
index 0000000..2fe6e9a
--- /dev/null
+++ b/groovefingerprinter/fingerprinter.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2013 Andrew Kelley
+ *
+ * This file is part of libgroove, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef GROOVE_FINGERPRINTER_H_INCLUDED
+#define GROOVE_FINGERPRINTER_H_INCLUDED
+
+#include <groove/groove.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/* use this to find out the unique id of an audio track */
+
+struct GrooveFingerprinterInfo {
+    /* Compressed string. */
+    char *fingerprint;
+    /* how many seconds long this song is */
+    double duration;
+
+    /* the playlist item that this info applies to.
+     * When this is NULL this is the end-of-playlist sentinel and
+     * other properties are undefined.
+     */
+    struct GroovePlaylistItem *item;
+};
+
+struct GrooveFingerprinter {
+    /* maximum number of GrooveFingerprinterInfo items to store in this
+     * fingerprinter's queue. this defaults to MAX_INT, meaning that
+     * the fingerprinter will cause the decoder to decode the entire
+     * playlist. if you want to instead, for example, obtain fingerprints
+     * at the same time as playback, you might set this value to 1.
+     */
+    int info_queue_size;
+
+    /* how big the sink buffer should be, in sample frames.
+     * groove_fingerprinter_create defaults this to 8192
+     */
+    int sink_buffer_size;
+
+    /* read-only. set when attached and cleared when detached */
+    struct GroovePlaylist *playlist;
+};
+
+struct GrooveFingerprinter *groove_fingerprinter_create(void);
+void groove_fingerprinter_destroy(struct GrooveFingerprinter *printer);
+
+/* once you attach, you must detach before destroying the playlist */
+int groove_fingerprinter_attach(struct GrooveFingerprinter *printer,
+        struct GroovePlaylist *playlist);
+int groove_fingerprinter_detach(struct GrooveFingerprinter *printer);
+
+/* returns < 0 on error, 0 on aborted (block=1) or no info ready (block=0),
+ * 1 on info returned.
+ * When you get info you must free it with groove_fingerprinter_free_info.
+ */
+int groove_fingerprinter_info_get(struct GrooveFingerprinter *printer,
+        struct GrooveFingerprinterInfo *info, int block);
+
+void groove_fingerprinter_free_info(struct GrooveFingerprinterInfo *info);
+
+/* returns < 0 on error, 0 on no info ready, 1 on info ready
+ * if block is 1, block until info is ready
+ */
+int groove_fingerprinter_info_peek(struct GrooveFingerprinter *printer,
+        int block);
+
+/* get the position of the printer head
+ * both the current playlist item and the position in seconds in the playlist
+ * item are given. item will be set to NULL if the playlist is empty
+ * you may pass NULL for item or seconds
+ */
+void groove_fingerprinter_position(struct GrooveFingerprinter *printer,
+        struct GroovePlaylistItem **item, double *seconds);
+
+/**
+ * Compress and base64-encode a raw fingerprint
+ *
+ * The caller is responsible for freeing the returned pointer using
+ * groove_fingerprinter_dealloc().
+ *
+ * Parameters:
+ *  - fp: pointer to an array of unsigned 32-bit integers representing the raw
+ *        fingerprint to be encoded
+ *  - size: number of items in the raw fingerprint
+ *  - encoded_fp: pointer to a pointer, where the encoded fingerprint will be
+ *                stored
+ *
+ * Returns:
+ *  - 0 on error, 1 on success
+ */
+int groove_fingerprinter_encode(void *fp, int size, char **encoded_fp);
+
+/**
+ * Uncompress and base64-decode an encoded fingerprint
+ *
+ * The caller is responsible for freeing the returned pointer using
+ * groove_fingerprinter_dealloc().
+ *
+ * Parameters:
+ *  - encoded_fp: Pointer to an encoded fingerprint
+ *  - encoded_size: Size of the encoded fingerprint in bytes
+ *  - fp: Pointer to a pointer, where the decoded raw fingerprint (array
+ *        of unsigned 32-bit integers) will be stored
+ *  - size: Number of items in the returned raw fingerprint
+ *
+ * Returns:
+ *  - 0 on error, 1 on success
+ */
+int groove_fingerprinter_decode(char *encoded_fp, void **fp, int *size);
+
+void groove_fingerprinter_dealloc(void *ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* GROOVE_FINGERPRINTER_H_INCLUDED */
diff --git a/grooveloudness/loudness.h b/grooveloudness/loudness.h
index 3f45dc4..30f0d57 100644
--- a/grooveloudness/loudness.h
+++ b/grooveloudness/loudness.h
@@ -8,13 +8,13 @@
 #ifndef GROOVE_LOUDNESS_H_INCLUDED
 #define GROOVE_LOUDNESS_H_INCLUDED
 
+#include <groove/groove.h>
+
 #ifdef __cplusplus
 extern "C"
 {
 #endif /* __cplusplus */
 
-#include <groove/groove.h>
-
 struct GrooveLoudnessDetectorInfo {
     /* loudness is in LUFS. 1 LUFS == 1 dB
      * EBU R128 specifies that playback should target -23 LUFS. replaygain on
diff --git a/grooveplayer/player.h b/grooveplayer/player.h
index cf0bdea..5104ef2 100644
--- a/grooveplayer/player.h
+++ b/grooveplayer/player.h
@@ -8,13 +8,13 @@
 #ifndef GROOVE_PLAYER_H_INCLUDED
 #define GROOVE_PLAYER_H_INCLUDED
 
+#include <groove/groove.h>
+
 #ifdef __cplusplus
 extern "C"
 {
 #endif /* __cplusplus */
 
-#include <groove/groove.h>
-
 /* use this to make a playlist utilize your speakers */
 
 enum GroovePlayerEventType {

-- 
libgroove packaging



More information about the pkg-multimedia-commits mailing list