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