Skip to content

Instantly share code, notes, and snippets.

@ericlewis
Last active August 29, 2024 14:50
Show Gist options
  • Save ericlewis/102039f9692642c4427cb5bfd5425edf to your computer and use it in GitHub Desktop.
Save ericlewis/102039f9692642c4427cb5bfd5425edf to your computer and use it in GitHub Desktop.
package com.example.shazamwrapper;
import android.content.Context;
import com.shazam.shazamkit.AudioSampleRateInHz;
import com.shazam.shazamkit.Catalog;
import com.shazam.shazamkit.DeveloperToken;
import com.shazam.shazamkit.DeveloperTokenProvider;
import com.shazam.shazamkit.MatchResult;
import com.shazam.shazamkit.Session;
import com.shazam.shazamkit.ShazamCatalog;
import com.shazam.shazamkit.ShazamKit;
import com.shazam.shazamkit.ShazamKitException;
import com.shazam.shazamkit.ShazamKitResult;
import com.shazam.shazamkit.Signature;
import com.shazam.shazamkit.SignatureGenerator;
import com.shazam.shazamkit.StreamingSession;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.Dispatchers;
import kotlinx.coroutines.flow.Flow;
import kotlinx.coroutines.flow.FlowCollector;
public class ShazamManager {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final Context context;
private final String developerToken;
private ShazamCatalog shazamCatalog;
private SignatureGenerator signatureGenerator;
private StreamingSession streamingSession;
public ShazamManager(Context context, String developerToken) {
this.context = context;
this.developerToken = developerToken;
initialize();
}
private void initialize() {
DeveloperTokenProvider tokenProvider = new DeveloperTokenProvider() {
@Override
public DeveloperToken provideDeveloperToken() {
return new DeveloperToken(developerToken);
}
};
shazamCatalog = ShazamKit.INSTANCE.createShazamCatalog(tokenProvider, Locale.getDefault());
}
public void initializeSignatureGenerator(AudioSampleRateInHz sampleRate, SignatureGeneratorCallback callback) {
CompletableFuture.supplyAsync(() -> {
try {
ShazamKitResult<ShazamKitException, SignatureGenerator> result = ShazamKit.INSTANCE.createSignatureGenerator(sampleRate, new Continuation<ShazamKitResult<ShazamKitException, SignatureGenerator>>() {
@Override
public CoroutineContext getContext() {
return Dispatchers.getDefault();
}
@Override
public void resumeWith(Object o) {
// This method is called when the coroutine completes
}
});
if (result instanceof ShazamKitResult.Success) {
signatureGenerator = ((ShazamKitResult.Success<SignatureGenerator>) result).getData();
return true;
} else {
throw ((ShazamKitResult.Failure<ShazamKitException>) result).getReason();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService).thenAccept(success -> callback.onSuccess())
.exceptionally(e -> {
callback.onError(e);
return null;
});
}
public void appendAudioData(byte[] audioData, int meaningfulLengthInBytes, long timestamp) {
if (signatureGenerator != null) {
signatureGenerator.append(audioData, meaningfulLengthInBytes, timestamp);
}
}
public void generateSignature(SignatureCallback callback) {
if (signatureGenerator != null) {
Signature signature = signatureGenerator.generateSignature();
callback.onSignatureGenerated(signature);
} else {
callback.onError(new IllegalStateException("SignatureGenerator not initialized"));
}
}
public void matchSignature(Signature signature, MatchResultCallback callback) {
CompletableFuture.supplyAsync(() -> {
try {
ShazamKitResult<ShazamKitException, Session> sessionResult = ShazamKit.INSTANCE.createSession(shazamCatalog, new Continuation<ShazamKitResult<ShazamKitException, Session>>() {
@Override
public CoroutineContext getContext() {
return Dispatchers.getDefault();
}
@Override
public void resumeWith(Object o) {
// This method is called when the coroutine completes
}
});
if (sessionResult instanceof ShazamKitResult.Success) {
Session session = ((ShazamKitResult.Success<Session>) sessionResult).getData();
return session.match(signature, new Continuation<MatchResult>() {
@Override
public CoroutineContext getContext() {
return Dispatchers.getDefault();
}
@Override
public void resumeWith(Object o) {
// This method is called when the coroutine completes
}
});
} else {
throw ((ShazamKitResult.Failure<ShazamKitException>) sessionResult).getReason();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService).thenAccept(matchResult -> callback.onMatchResult((MatchResult) matchResult))
.exceptionally(e -> {
callback.onError(e);
return null;
});
}
public void initializeStreamingSession(AudioSampleRateInHz sampleRate, int bufferSize, StreamingSessionCallback callback) {
CompletableFuture.supplyAsync(() -> {
try {
ShazamKitResult<ShazamKitException, StreamingSession> result = ShazamKit.INSTANCE.createStreamingSession(shazamCatalog, sampleRate, bufferSize, new Continuation<ShazamKitResult<ShazamKitException, StreamingSession>>() {
@Override
public CoroutineContext getContext() {
return Dispatchers.getDefault();
}
@Override
public void resumeWith(Object o) {
// This method is called when the coroutine completes
}
});
if (result instanceof ShazamKitResult.Success) {
streamingSession = ((ShazamKitResult.Success<StreamingSession>) result).getData();
return true;
} else {
throw ((ShazamKitResult.Failure<ShazamKitException>) result).getReason();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService).thenAccept(success -> callback.onSuccess())
.exceptionally(e -> {
callback.onError(e);
return null;
});
}
public void matchStream(byte[] audioData, int meaningfulLengthInBytes, long timestampInMs) {
if (streamingSession != null) {
streamingSession.matchStream(audioData, meaningfulLengthInBytes, timestampInMs);
}
}
public void startStreamingRecognition(final StreamingRecognitionCallback callback) {
if (streamingSession != null) {
CompletableFuture.runAsync(() -> {
Flow<MatchResult> flow = streamingSession.recognitionResults();
try {
flow.collect(new FlowCollector<MatchResult>() {
@Override
public Object emit(MatchResult matchResult, Continuation<? super kotlin.Unit> continuation) {
callback.onRecognitionResult(matchResult);
return null;
}
}, new Continuation<kotlin.Unit>() {
@Override
public CoroutineContext getContext() {
return Dispatchers.getDefault();
}
@Override
public void resumeWith(Object o) {
// This method is called when the coroutine completes
}
});
} catch (Exception e) {
callback.onError(e);
}
}, executorService);
} else {
callback.onError(new IllegalStateException("StreamingSession not initialized"));
}
}
public void shutdown() {
executorService.shutdown();
}
public interface SignatureGeneratorCallback {
void onSuccess();
void onError(Exception e);
}
public interface SignatureCallback {
void onSignatureGenerated(Signature signature);
void onError(Exception e);
}
public interface MatchResultCallback {
void onMatchResult(MatchResult matchResult);
void onError(Exception e);
}
public interface StreamingSessionCallback {
void onSuccess();
void onError(Exception e);
}
public interface StreamingRecognitionCallback {
void onRecognitionResult(MatchResult matchResult);
void onError(Exception e);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment