lincollincol / Amplituda

Audio processing library, which provides waveform data
Apache License 2.0
221 stars 31 forks source link

How to process with content uri #30

Closed abkoradiya closed 3 years ago

abkoradiya commented 3 years ago

If I pick audio and have content uri instead of file uri then how to process that?

lincollincol commented 3 years ago

@abkoradiya Hi! Here are kotlin extensions to convert uri to audio file

fun Context.uriToFile(uri: Uri) = with(contentResolver) {
    val data = readUriBytes(uri) ?: return@with null
    val extension = getUriExtension(uri)
    File(
        cacheDir.path,
        "${UUID.randomUUID()}.$extension"
    ).also { audio -> audio.writeBytes(data) }
}

fun ContentResolver.readUriBytes(uri: Uri) = openInputStream(uri)
        ?.buffered()?.use { it.readBytes() }

fun ContentResolver.getUriExtension(uri: Uri) = MimeTypeMap.getSingleton()
        .getMimeTypeFromExtension(getType(uri))

You can use it with Amplituda

val audioFile = uriToFile(audioUri)
val result = amplituda.processAudio(audioFile)
    .get()
println(result.amplitudesAsJson())

And you here is full sample code with ActivityResultApi

class MainActivity : AppCompatActivity() {

    private val amplituda: Amplituda by lazy { Amplituda(this) }

    private val openAudioResult = registerForActivityResult(
        ActivityResultContracts.OpenDocument()
    ) { audioUri ->
        val audioFile = uriToFile(audioUri)
        val result = amplituda.processAudio(audioFile)
            .get()
        println(result.amplitudesAsJson())
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        openAudioResult.launch(arrayOf("audio/*"))
    }

    fun Context.uriToFile(uri: Uri) = with(contentResolver) {
        val data = readUriBytes(uri) ?: return@with null
        val extension = getUriExtension(uri)
        File(
            cacheDir.path,
            "${UUID.randomUUID()}.$extension"
        ).also { audio -> audio.writeBytes(data) }
    }

    fun ContentResolver.readUriBytes(uri: Uri) = openInputStream(uri)
        ?.buffered()?.use { it.readBytes() }

    fun ContentResolver.getUriExtension(uri: Uri) = MimeTypeMap.getSingleton()
        .getMimeTypeFromExtension(getType(uri))

}
niklasdahlheimer commented 2 years ago

There is a mistake in getUriExtension(): It should be getMimeTypeFromExtension() and not getMimeTypeFromExtension() because we want to map MimeType to extension and not the other way around.

If anyone needs the (obviously not so compact) Java version for this, here you go:

static File uriToFile(ContentResolver resolver, Uri uri) {
    byte[] data = readUriBytes(resolver, uri);
    if (data == null) return null;
    File cachedFile = new File(App.getContext().getCacheDir().getPath(),
            UUID.randomUUID() + "." + getUriExtension(resolver, uri));
    try (FileOutputStream outputStream = new FileOutputStream(cachedFile)) {
        outputStream.write(data);
        return cachedFile;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

static byte[] readUriBytes(ContentResolver resolver, Uri uri) {
    ByteArrayOutputStream oStream = new ByteArrayOutputStream();
    try (InputStream iStream = new BufferedInputStream(resolver.openInputStream(uri))) {
        byte[] buf = new byte[1024];
        int readBytes;
        while (-1 != (readBytes = iStream.read(buf))) {
            oStream.write(buf, 0, readBytes);
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
    return oStream.toByteArray();
}

private static String getUriExtension(ContentResolver resolver, Uri uri) {
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(resolver.getType(uri));
}