Оказывается, вы можете распознать заданный массив PCM в GNSDK для Android с помощью следующих трех вызовов API GnMusicIdStream:
- GnMusicIdStream.audioProcessStart(sampleRateHz,pcmBitcount,channelCount) для подготовки механизма распознавания к входящему PCM.
- GnMusicIdStream.audioProcess(pcmArray,pcmArray.length) для передачи массива PCM, который вы хотите распознать
- GnMusicIdStream.identifyAlbumAsync() для создания отпечатка пальца и последующего использования его для операции поиска. Это вызовет обратные вызовы к объекту IGnMusicIdStreamEvents, переданному вашему экземпляру GnMusicIdStream, и musicIdStreamAlbumResult() предоставит любые результаты.
Насколько я понимаю, используя этот подход, вам не нужно явно ждать генерации отпечатков пальцев и т. д. — вы просто вызываете эти три метода по порядку, а затем GNSDK обрабатывает остальные и в конечном итоге выполняет обратный вызов. Полная операция id выглядит так:
try {
mGnMusicIdStream = new GnMusicIdStream(mGnUser, GnMusicIdStreamPreset.kPresetRadio, new IGnMusicIdStreamEvents() {
@Override
public void musicIdStreamProcessingStatusEvent(GnMusicIdStreamProcessingStatus gnMusicIdStreamProcessingStatus, IGnCancellable iGnCancellable) {
Log.d(TAG,"gracenote gnsdk -- musicIdStreamProcessingStatusEvent(); event is: "+gnMusicIdStreamProcessingStatus);
}
@Override
public void musicIdStreamIdentifyingStatusEvent(GnMusicIdStreamIdentifyingStatus gnMusicIdStreamIdentifyingStatus, IGnCancellable iGnCancellable) {
Log.d(TAG,"gracenote gnsdk -- musicIdStreamIdentifyingStatusEvent(); event is: "+gnMusicIdStreamIdentifyingStatus);
}
@Override
public void musicIdStreamAlbumResult(GnResponseAlbums gnResponseAlbums, IGnCancellable iGnCancellable) {
Log.d(TAG,"gracenote gnsdk -- musicIdStreamAlbumResult(); responsealbums matches: "+gnResponseAlbums.resultCount());
if (gnResponseAlbums.resultCount() > 0) {
try {
final GnAlbum albumResponse = gnResponseAlbums.albums().at(0).next();
final GnTrack trackResponse = albumResponse.trackMatched();
if (trackResponse != null) {
mEvent.postOnGNSearchResult(new ISongRecognitionResponse() {
@Override
public
@NonNull
String extractTrackTitle() {
// seems that track title comes reliably from GnTrack and much of the rest is locked
// up in the GnAlbum?
if (trackResponse.title() != null) {
return trackResponse.title().display();
} else {
return "";
}
}
@Override
public
@NonNull
String extractTrackArtist() {
if (albumResponse.artist() != null) {
if(BuildConfig.RULE_DEBUG_LEVEL>0)
Log.d(TAG,"gnsdk -- album artist says "+albumResponse.artist().name().display());
return albumResponse.artist().name().display();
} else {
return "";
}
}
@Override
public long extractTrackPosition() {
return trackResponse.currentPosition();
}
@Override
public long extractTrackDuration() {
return trackResponse.duration();
}
@Override
public byte[] extractCoverArtImageData() {
// seems that base64 string of the image is not always/commonly available
// at least as we're trying to access it here. The sample app downloads the image
// asynchronously from the URL, which seems more reliable
String img64 = albumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).imageDataBase64(); //trackResponse.content(GnContentType.kContentTypeImageCover).asset(GnImageSize.kImageSize220).imageDataBase64();
if(img64 != null && !img64.isEmpty()) {
return Base64.decode(img64, Base64.DEFAULT);
}else{
return null;
}
}
@NonNull
@Override
public String extractCoverArtImageURL() {
// beware: asking for specific image sizes has been known to cause
// no cover art to come back even if there might be cover art at another size.
// The sample app uses the categorical size qualifier constant kImageSizeSmall
String httpURL = albumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).urlHttp();
return httpURL;
}
});
}//end if track response data is non-null
else {
mEvent.postOnGNSearchResult(null);
}
}catch(GnException e){
Log.e(TAG, "we received a response clbk, but failed to process it", e);
}
}//end if greater than 0 results
else{
//no results, so pass a null result to indicate a miss
mEvent.postOnGNSearchResult(null);
}
}
@Override
public void musicIdStreamIdentifyCompletedWithError(GnError gnError) {
Log.e(TAG,"gnsdk -- musicIdStreamIdentifyCompletedWithError(); we received a response clbk, but failed to process it");
mEvent.postOnGNSearchFailure(gnError.errorDescription());
}
@Override
public void statusEvent(GnStatus gnStatus, long l, long l1, long l2, IGnCancellable iGnCancellable) {
Log.e(TAG,"gnsdk -- statusEvent(); status is: "+gnStatus);
}
});
//configure the options on the gnmusicidstream instance
mGnMusicIdStream.options().lookupData(GnLookupData.kLookupDataContent, true);
mGnMusicIdStream.options().lookupData(GnLookupData.kLookupDataSonicData, true);
mGnMusicIdStream.options().lookupMode(GnLookupMode.kLookupModeOnline);
mGnMusicIdStream.options().preferResultCoverart(true);
mGnMusicIdStream.options().resultSingle(true);
//configure audio processing params on gnmusicidstream
mGnMusicIdStream.audioProcessStart(sampleRateHz,pcmBitcount,channelCount);
//pass the pcm array to the gnmusicidstream for processing
mGnMusicIdStream.audioProcess(pcmArray,pcmArray.length);
//initiate the lookup operation based on the processed pcm
mGnMusicIdStream.identifyAlbumAsync();
}catch(GnException e){
Log.e(TAG,"gnsdk -- failed recognition operation",e);
}
Возвращаемые данные немного сбивают с толку, с несколькими потенциальными способами извлечения метаданных о дорожке, которые могут быть нулевыми или пустыми при запросе определенными способами, а не при запросе другими способами. Интересные моменты, которые я нашел об объекте ответа GnResponseAlbums до сих пор (я не уверен в договоре о допустимости значений NULL для возвращаемых значений, упомянутых ниже, поэтому следите за исключениями nullpointerexceptions):
gnResponseAlbums.resultCount() будет равно 0, если явно ничего не пошло не так, но совпадений не найдено.
совпадающий GnTrack можно получить с помощью albumResponse.trackMatched()
название трека можно получить в виде строки с помощью albumResponse.trackMatched().title().display()
исполнителя трека можно получить с помощью AlbumResponse.artist().name().display()
текущая позиция трека во времени может быть извлечена с помощью AlbumResponse.trackMatched().currentPosition(), который кажется довольно точным в определении времени окончания песни как {endTime = currentTime + duration - currentPosition}
продолжительность трека может быть извлечена с помощью albumResponse.trackMatched().duration()
URL-адрес обложки можно извлечь с помощью AlbumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).urlHttp().
Мне не удалось получить изображение в виде строки base64 с помощью AlbumResponse.coverArt().asset(GnImageSize.kImageSizeSmall).imageDataBase64(), но GNSDK предоставил простой класс GnAssetFetch, который можно использовать для извлечения обложки. данные следующим образом
GnAssetFetch assetData = new GnAssetFetch(mGnUser,coverArtUrl);
byte[] data = assetData.data();
Что касается отмены выполняемой операции, то можно использовать метод identityCancel() экземпляра GnMusicIdStream. Если отмена будет происходить в методах обратного вызова IGnMusicIdStreamEvents, вместо этого следует использовать предоставленный отменитель IGnCancellable.
person
CCJ
schedule
08.12.2017