I'm trying to get ShazamKit for Android to work. I have a catalog that I am downloading from an external service, caching in internal app storage and reading from internal app storage. Doing so I'm getting no matches.
However if I manually download the file from internal app storage to my computer and put it in the assets folder and read it from there I'm getting matches. So the issue must be in the reading of the file. See comments in the code below.
Here's the code:
private const val BUFFER_SIZE = 3840
class ShazamService(private val app: Application) {
private val coroutineScope = CoroutineScope(Dispatchers.IO + Job())
private val repository = ShazamRepository(...)
private val catalog = ShazamKit.createCustomCatalog()
private val recorder by lazy {
AudioRecording(app)
}
private var session: StreamingSession? = null
suspend fun initialize(source: Source) {
// This method does not work
addCatalog(source)
// This works when used
// loadCustomCatalog()
session = (ShazamKit.createStreamingSession(
catalog,
AudioSampleRateInHz.SAMPLE_RATE_48000,
BUFFER_SIZE
) as ShazamKitResult.Success).data
session?.recognitionResults()?.onEach { matchResult ->
onMatch(matchResult)
}?.flowOn(Dispatchers.Main)?.launchIn(coroutineScope)
}
// This works
private suspend fun loadCustomCatalog() {
val assetManager = app.assets
val inputStream: InputStream = assetManager.open("catalog.shazamcatalog")
catalog.addFromCatalog(inputStream)
}
// This does not work
private suspend fun addCatalog(source: Source) {
repository.loadFile(app.applicationContext, source)?.use { data ->
val result = this.catalog.addFromCatalog(data)
Timber.d("Catalog added: $result")
}
}
fun start() {
recorder.startRecording { data ->
session?.matchStream(data, data.size, 0)
}
}
fun stop() {
recorder.stopRecording()
}
private fun onMatch(result: com.shazam.shazamkit.MatchResult) {
Timber.d("Received MatchResult: $result")
}
fun destroy() {
coroutineScope.cancel()
}
}
Here's the repository responsible for providing the catalog file. Source contains an id and a url from which a catalog can be downloaded. It downloads the catalog and saves it as a file in internal app storage and returns a FileInputStream.
class ShazamRepository(
private val shazamClient: ShazamClient
) {
suspend fun loadFile(context: Context, source: Source): FileInputStream? {
val file = File(context.filesDir, source.id + ".shazamcatalog")
val catalog = loadFile(file)
if (catalog == null) {
val response = shazamClient.getCatalog(source.url)
if (response.isSuccessful) {
response.body()?.let { data ->
saveResponseData(data, file)
}
}
} else {
return catalog
}
return loadFile(file)
}
private fun saveResponseData(data: ResponseBody, file: File) {
data.byteStream().use { inputStream ->
FileOutputStream(file).use { outputStream ->
val buffer = ByteArray(4 * 1024)
var read: Int
while (inputStream.read(buffer).also { read = it } != -1) {
outputStream.write(buffer, 0, read)
}
outputStream.flush()
}
inputStream.close()
}
}
private fun loadFile(file: File): FileInputStream? {
return if (file.exists()) {
FileInputStream(file)
} else {
return null
}
}
}
To summarise:
The catalog is downloaded and saved correctly.
If the catalog file is opened with assetManager.open I'm getting matches.
When using FileInputStream(file) no matches are received.
What could be wrong with the File-API approach? Why does it work when using the AssetManager but not when using it as a File?