1 // Copyright (C) 2010-2016 Lukas Lalinsky
2 // Distributed under the MIT license, see the LICENSE file for details.
3
4 #include <vector>
5 #include <string>
6 #include <algorithm>
7 #include <memory>
8 #include <cstring>
9 #include <chromaprint.h>
10 #include "fingerprinter.h"
11 #include "fingerprint_compressor.h"
12 #include "fingerprint_decompressor.h"
13 #include "fingerprint_matcher.h"
14 #include "fingerprinter_configuration.h"
15 #include "utils/base64.h"
16 #include "simhash.h"
17 #include "debug.h"
18
19 using namespace chromaprint;
20
21 struct ChromaprintContextPrivate {
ChromaprintContextPrivateChromaprintContextPrivate22 ChromaprintContextPrivate(int algorithm)
23 : algorithm(algorithm),
24 fingerprinter(CreateFingerprinterConfiguration(algorithm)) {}
25 int algorithm;
26 Fingerprinter fingerprinter;
27 FingerprintCompressor compressor;
28 std::string tmp_fingerprint;
29 };
30
31 struct ChromaprintMatcherContextPrivate {
32 int algorithm = -1;
33 std::unique_ptr<FingerprintMatcher> matcher;
34 std::vector<uint32_t> fp[2];
35 FingerprintDecompressor decompressor;
36 };
37
38 extern "C" {
39
40 #define FAIL_IF(x, msg) if (x) { DEBUG(msg); return 0; }
41
42 #define STR(x) #x
43 #define VERSION_STR(major, minor, patch) \
44 STR(major) "." STR(minor) "." STR(patch)
45
46 static const char *version_str = VERSION_STR(
47 CHROMAPRINT_VERSION_MAJOR,
48 CHROMAPRINT_VERSION_MINOR,
49 CHROMAPRINT_VERSION_PATCH);
50
chromaprint_get_version(void)51 const char *chromaprint_get_version(void)
52 {
53 return version_str;
54 }
55
chromaprint_new(int algorithm)56 ChromaprintContext *chromaprint_new(int algorithm)
57 {
58 return new ChromaprintContextPrivate(algorithm);
59 }
60
chromaprint_free(ChromaprintContext * ctx)61 void chromaprint_free(ChromaprintContext *ctx)
62 {
63 if (ctx) {
64 delete ctx;
65 }
66 }
67
chromaprint_set_option(ChromaprintContext * ctx,const char * name,int value)68 int chromaprint_set_option(ChromaprintContext *ctx, const char *name, int value)
69 {
70 FAIL_IF(!ctx, "context can't be NULL");
71 return ctx->fingerprinter.SetOption(name, value) ? 1 : 0;
72 }
73
chromaprint_get_num_channels(ChromaprintContext * ctx)74 int chromaprint_get_num_channels(ChromaprintContext *ctx)
75 {
76 return 1;
77 }
78
chromaprint_get_sample_rate(ChromaprintContext * ctx)79 int chromaprint_get_sample_rate(ChromaprintContext *ctx)
80 {
81 return ctx ? ctx->fingerprinter.config()->sample_rate() : 0;
82 }
83
chromaprint_get_item_duration(ChromaprintContext * ctx)84 int chromaprint_get_item_duration(ChromaprintContext *ctx)
85 {
86 return ctx ? ctx->fingerprinter.config()->item_duration() : 0;
87 }
88
chromaprint_get_item_duration_ms(ChromaprintContext * ctx)89 int chromaprint_get_item_duration_ms(ChromaprintContext *ctx)
90 {
91 return ctx ? ctx->fingerprinter.config()->item_duration_in_seconds() * 1000 : 0;
92 }
93
chromaprint_get_delay(ChromaprintContext * ctx)94 int chromaprint_get_delay(ChromaprintContext *ctx)
95 {
96 return ctx ? ctx->fingerprinter.config()->delay() : 0;
97 }
98
chromaprint_get_delay_ms(ChromaprintContext * ctx)99 int chromaprint_get_delay_ms(ChromaprintContext *ctx)
100 {
101 return ctx ? ctx->fingerprinter.config()->delay_in_seconds() * 1000 : 0;
102 }
103
chromaprint_start(ChromaprintContext * ctx,int sample_rate,int num_channels)104 int chromaprint_start(ChromaprintContext *ctx, int sample_rate, int num_channels)
105 {
106 FAIL_IF(!ctx, "context can't be NULL");
107 return ctx->fingerprinter.Start(sample_rate, num_channels) ? 1 : 0;
108 }
109
chromaprint_feed(ChromaprintContext * ctx,const int16_t * data,int length)110 int chromaprint_feed(ChromaprintContext *ctx, const int16_t *data, int length)
111 {
112 FAIL_IF(!ctx, "context can't be NULL");
113 ctx->fingerprinter.Consume(data, length);
114 return 1;
115 }
116
chromaprint_finish(ChromaprintContext * ctx)117 int chromaprint_finish(ChromaprintContext *ctx)
118 {
119 FAIL_IF(!ctx, "context can't be NULL");
120 ctx->fingerprinter.Finish();
121 return 1;
122 }
123
chromaprint_get_fingerprint(ChromaprintContext * ctx,char ** data)124 int chromaprint_get_fingerprint(ChromaprintContext *ctx, char **data)
125 {
126 FAIL_IF(!ctx, "context can't be NULL");
127 ctx->compressor.Compress(ctx->fingerprinter.GetFingerprint(), ctx->algorithm, ctx->tmp_fingerprint);
128 *data = (char *) malloc(GetBase64EncodedSize(ctx->tmp_fingerprint.size()) + 1);
129 FAIL_IF(!*data, "can't allocate memory for the result");
130 Base64Encode(ctx->tmp_fingerprint.begin(), ctx->tmp_fingerprint.end(), *data, true);
131 return 1;
132 }
133
chromaprint_get_raw_fingerprint(ChromaprintContext * ctx,uint32_t ** data,int * size)134 int chromaprint_get_raw_fingerprint(ChromaprintContext *ctx, uint32_t **data, int *size)
135 {
136 FAIL_IF(!ctx, "context can't be NULL");
137 const auto fingerprint = ctx->fingerprinter.GetFingerprint();
138 *data = (uint32_t *) malloc(sizeof(uint32_t) * fingerprint.size());
139 FAIL_IF(!*data, "can't allocate memory for the result");
140 *size = fingerprint.size();
141 std::copy(fingerprint.begin(), fingerprint.end(), *data);
142 return 1;
143 }
144
chromaprint_get_raw_fingerprint_size(ChromaprintContext * ctx,int * size)145 int chromaprint_get_raw_fingerprint_size(ChromaprintContext *ctx, int *size)
146 {
147 FAIL_IF(!ctx, "context can't be NULL");
148 const auto fingerprint = ctx->fingerprinter.GetFingerprint();
149 *size = fingerprint.size();
150 return 1;
151 }
152
chromaprint_get_fingerprint_hash(ChromaprintContext * ctx,uint32_t * hash)153 int chromaprint_get_fingerprint_hash(ChromaprintContext *ctx, uint32_t *hash)
154 {
155 FAIL_IF(!ctx, "context can't be NULL");
156 *hash = SimHash(ctx->fingerprinter.GetFingerprint());
157 return 1;
158 }
159
chromaprint_clear_fingerprint(ChromaprintContext * ctx)160 int chromaprint_clear_fingerprint(ChromaprintContext *ctx)
161 {
162 FAIL_IF(!ctx, "context can't be NULL");
163 ctx->fingerprinter.ClearFingerprint();
164 return 1;
165 }
166
chromaprint_encode_fingerprint(const uint32_t * fp,int size,int algorithm,char ** encoded_fp,int * encoded_size,int base64)167 int chromaprint_encode_fingerprint(const uint32_t *fp, int size, int algorithm, char **encoded_fp, int *encoded_size, int base64)
168 {
169 std::vector<uint32_t> uncompressed(fp, fp + size);
170 std::string encoded = CompressFingerprint(uncompressed, algorithm);
171 if (base64) {
172 encoded = Base64Encode(encoded);
173 }
174 *encoded_fp = (char *) malloc(encoded.size() + 1);
175 *encoded_size = encoded.size();
176 std::copy(encoded.data(), encoded.data() + encoded.size() + 1, *encoded_fp);
177 return 1;
178 }
179
chromaprint_decode_fingerprint(const char * encoded_fp,int encoded_size,uint32_t ** fp,int * size,int * algorithm,int base64)180 int chromaprint_decode_fingerprint(const char *encoded_fp, int encoded_size, uint32_t **fp, int *size, int *algorithm, int base64)
181 {
182 std::string encoded(encoded_fp, encoded_size);
183 if (base64) {
184 encoded = Base64Decode(encoded);
185 }
186 std::vector<uint32_t> uncompressed;
187 int algo;
188 auto ok = DecompressFingerprint(encoded, uncompressed, algo);
189 if (!ok) {
190 *fp = nullptr;
191 *size = 0;
192 if (algorithm) {
193 *algorithm = 0;
194 }
195 return 0;
196 }
197 *fp = (uint32_t *) malloc(sizeof(uint32_t) * uncompressed.size());
198 *size = uncompressed.size();
199 if (algorithm) {
200 *algorithm = algo;
201 }
202 std::copy(uncompressed.begin(), uncompressed.end(), *fp);
203 return 1;
204 }
205
chromaprint_hash_fingerprint(const uint32_t * fp,int size,uint32_t * hash)206 int chromaprint_hash_fingerprint(const uint32_t *fp, int size, uint32_t *hash)
207 {
208 if (fp == NULL || size < 0 || hash == NULL) {
209 return 0;
210 }
211 *hash = SimHash(fp, size);
212 return 1;
213 }
214
chromaprint_dealloc(void * ptr)215 void chromaprint_dealloc(void *ptr)
216 {
217 free(ptr);
218 }
219
220 }; // extern "C"
221