I'm working on migrating my Android app from the legacy Gracenote Mobile Client to the newer GNSDK for Mobile SDK, and I've hit a few snags:
- In mobile client, I used GNOperations.recognizeMIDStreamFromRadio(GNSearchResultReady, GNConfig, samplePCMBuffer) to initiate a fingerprint and lookup operation on a PCM buffer. My application can only supply prerecorded audio to Gracenote (as opposed to simply pointing Gracenote at a streaming audio source), ideally as raw PCM though I could encode to standard compression formats if necessary. What should I use from the GNSDK for Mobile API to effect the same fingerprint and lookup operation on supplied prerecorded audio data, which can hopefully still be raw PCM?
- The class GnMusicId looks like it might be a handy generic fingerprint generator and query issuer class, so it might be the answer to #1 above. However, I haven't found a way to determine when it finishes writing a fingerprint and, therefore, we are ready to issue a query. How can I get a callback letting me know that GnMusicId has finished writing a fingerprint from the GnMusicId.fingerprintWrite(byte[] audioData, long audioDataSize) method, and that the fingerprint is ready to be used in a query via GnMusicId.findAlbums(fingerprintDataGet(), GnFingerprintType.kFingerprintTypeStream6)?
- In Mobile Client I was able to cancel ongoing Gracenote operations using GNOperations.cancel(GNSearchResultReady) -- I've read that the new architecture requires that specific operations be canceled individually due to a more modular design, but I haven't found a standard cancellation API on the various operations the GNSDK for Mobile can perform -- how should I cancel fingerprint and song lookup operations in GNSDK for Mobile?
Turns out you can recognize a given PCM array in GNSDK for Android with the following three GnMusicIdStream API calls:
As far as I can see, using this approach you don't need to wait on fingerprint generation etc. explicitly -- you just call those three methods in order and then GNSDK handles the rest and will eventually issue a callback. The full id operation ends up looking like this:
The return data is kind of confusing, with multiple potential ways to extract metadata about the track which may be null or empty when queried certain ways and not when queried in other ways. Interesting points I've found about the GnResponseAlbums response object so far (I'm not sure about the nullability contract of the return values mentioned below, so watch out for nullpointerexceptions):
gnResponseAlbums.resultCount() will be 0 if nothing explicitly went wrong but there were no matches found.
the matched GnTrack can be retrieved with albumResponse.trackMatched()
the track title can be retrieved as a String with albumResponse.trackMatched().title().display()
the track artist can be retrieved with albumResponse.artist().name().display()
the current track position in time can be extracted with albumResponse.trackMatched().currentPosition(), which seems to be pretty accurate in determining the time the song will end as {endTime = currentTime + duration - currentPosition}
the duration of the track can be extracted with albumResponse.trackMatched().duration()
the cover art URL can be extracted with albumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).urlHttp().
I haven't had any luck getting the image bundled as a base64 string via albumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).imageDataBase64(), but the GNSDK provided a simple GnAssetFetch class which can be used to pull down cover art data as follows
As for cancelling an operation in progress, the GnMusicIdStream instance's identifyCancel() method can be used. If the cancellation is going to occur in the IGnMusicIdStreamEvents callback methods, the provided IGnCancellable canceller should be used instead.