Amplituda

repository·master·Indexed 43 days ago

https://github.com/lincollincol/amplituda

An Android library powered by FFmpeg used to extract amplitude data from audio files (local files, URLs, and resources) to generate custom waveforms. It features data compression, caching, progress monitoring via AmplitudaProgressListener, and support for various output formats including JSON and sequences.

Tokens
4.2K
Snippets
9
Records
10
Agent score
5%

What's inside Amplituda

  1. What is Amplituda?

    master

    Amplituda is an Android library powered by FFmpeg that processes audio files to generate an array of amplitude samples. These samples can be used to draw custom waveforms, where each value in the array represents the height of a waveform column.

    Performance Benchmarks:

    • 3 min 20 sec audio: ~1 second processing time.
    • 1 hour audio: ~20 seconds processing time.

    Common Integrations:

    • Compose AudioWaveform: A Jetpack Compose library for drawing waveforms.
    • WaveformSeekBar: An Android library for drawing waveforms using XML-based android.view.View.
    • Custom View or Composable implementations.
  2. How to use Amplituda

    master

    To use Amplituda, initialize the library with a Context, then call processAudio with the path to your audio file. The processing is asynchronous and returns a result object containing the amplitude data and audio metadata.

    Implementation Steps

    1. Initialize: Create an instance of Amplituda using your application or activity context.
    2. Process: Call processAudio(path) and provide callbacks for success and failure.
    3. Extract Data: Use the AmplitudaResult object to retrieve amplitude lists, duration, and audio type.

    Available Result Data

    • amplitudesAsList(): Returns a List<Integer> of all processed amplitude samples.
    • amplitudesForSecond(int second): Returns a List<Integer> of amplitudes for a specific second.
    • getAudioDuration(AmplitudaResult.DurationUnit unit): Returns the audio duration in the specified unit (e.g., SECONDS).
    • getAudioSource(): Returns the source path of the audio.
    • getInputAudioType(): Returns the InputAudio.Type of the processed file.

    Error Handling

    If processing fails, the exception can be checked against AmplitudaIOException to handle file-related errors.

    /* Step 1: create Amplituda */
    Amplituda amplituda = new Amplituda(context);
    
    /* Step 2: process audio and handle result */
    amplituda.processAudio("/storage/emulated/0/Music/Linc - Amplituda.mp3")
            .get(result -> {
                List<Integer> amplitudesData = result.amplitudesAsList();
                List<Integer> amplitudesForFirstSecond = result.amplitudesForSecond(1);
                long duration = result.getAudioDuration(AmplitudaResult.DurationUnit.SECONDS);
                String source = result.getAudioSource();
                InputAudio.Type sourceType = result.getInputAudioType();
                // etc
            }, exception -> {
                if(exception instanceof AmplitudaIOException) {
                    System.out.println("IO Exception!");
                }
            });
  3. Configure Android permissions and Manifest settings

    master

    Amplituda requires specific permissions in your AndroidManifest.xml to access storage and the internet. For Android 10+, you must also enable legacy external storage.

    Required Permissions:

    • android.permission.WRITE_EXTERNAL_STORAGE
    • android.permission.READ_EXTERNAL_STORAGE
    • android.permission.INTERNET

    To reduce APK size: Add android:extractNativeLibs="false" to your <application> tag in the Manifest.

    <!-- Permissions -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    
    <!-- Android 10+ and Size Reduction -->
    <application
        android:requestLegacyExternalStorage="true"
        android:extractNativeLibs="false">
        <activity . . ./>
    </application>
  4. Install Amplituda via JitPack

    master

    To use Amplituda in your Android project, add the JitPack repository to your allprojects block and then add the Amplituda dependency to your dependencies block in your Gradle files.

    allprojects {
      repositories {
        maven { url 'https://jitpack.io' }
      }
    }
    
    dependencies {
      implementation 'com.github.lincollincol:amplituda:x.y.z'
    }
  5. Compress audio amplitude output data

    master

    To reduce the size of the output data, you can pass Compress parameters to processAudio. This allows you to specify a preferred number of samples per second and a compression type.

    Compression Types:

    • Compress.SKIP: Takes the first preferredSamplesPerSecond and skips the rest.
    • Compress.PEEK: Takes a peek of the preferredSamplesPerSecond and skips the rest.
    • Compress.AVERAGE: Merges all samples to match the preferredSamplesPerSecond count.

    Example: For a 10-second audio, Compress.withParams(Compress.AVERAGE, 5) will result in approximately 50 samples (5 samples per second * 10 seconds).

    // Example: input audio duration - 10 seconds
    
    amplituda.processAudio(<audio>, Compress.withParams(Compress.AVERAGE, 1));
    // Output: data with 1 sample per 1 second.
    
    amplituda.processAudio(<audio>, Compress.withParams(Compress.AVERAGE, 5));
    // Output: data with 5 sample per 1 second
  6. Handle processing results and errors

    master

    To retrieve the processed data from AmplitudaProcessingOutput<T>, use the .get() method. This method has several overloads to support both callback-based and synchronous styles, as well as error handling.

    Error Handling: Exceptions can be caught and checked using instanceof. Common exceptions include:

    • AmplitudaIOException
    • AmplitudaProcessingException
    AmplitudaProcessingOutput<T> processingOutput;
    // . . . process audio . . .
    
    // Overload #1: result as a callback with error listener
    processingOutput.get(result -> { 
            /* handle result here */ 
        }, exception -> { 
            /* handle errors here */ 
        });
    
    // Overload #2: result as a callback without error listener
    processingOutput.get((AmplitudaSuccessListener<T>) result -> {
            /* handle result here */
        });
    
    // Overload #3: result as a returned object with error listener
    result = processingOutput.get((AmplitudaException exception) -> {
            /* handle errors here */
        });
    
    // Overload #4: result as a returned object without error listener
    result = processingOutput.get();
    
    // Example of specific error handling
    processingOutput.get((AmplitudaException exception) -> {
        if(exception instanceof AmplitudaIOException) {
            // Handle io exceptions
        } else if(exception instanceof AmplitudaProcessingException) {
            // Handle processing exceptions
        } else {
            exception.printStackTrace();
        }
    });
  7. Cache audio processing results

    master

    Amplituda provides a built-in caching mechanism to reuse processed audio data. You can configure this using Cache.withParams().

    Cache States:

    • Cache.REUSE: Uses cached data if available; otherwise, processes the audio and writes it to the cache.
    • Cache.REFRESH: Updates existing cache data (use this only when you need to force a refresh).

    You can optionally provide a unique String key for each audio to manage the cache manually.

    Cache Management:

    • amplituda.clearCache(): Clears all cache data.
    • amplituda.clearCache(<audio>): Clears cache for a specific audio.
    • amplituda.clearCache("key", true): Clears cache by a custom key.
    // Enable amplituda cache feature:
    amplituda.processAudio(<audio>, Cache.withParams(Cache.REUSE));
    
    // Cache with custom key. The key should be unique for each audio
    amplituda.processAudio(<audio>, Cache.withParams(Cache.REUSE, "my-unique-key"));
    
    // Clear all cache data
    amplituda.clearCache();
    
    // Clear cache data for specific audio (if exist)
    amplituda.clearCache("/storage/emulated/0/Music/Linc - Amplituda.mp3", false);
    
    // Clear cache data by custom key.
    amplituda.clearCache("my-unique-key", true);
  8. Format amplitude results

    master

    Once you have an AmplitudaResult<T>, you can extract the data in various formats:

    • amplitudesAsList(): Returns a List<Integer> of all samples.
    • amplitudesForSecond(int second): Returns a List<Integer> for a specific second of audio.
    • amplitudesAsJson(): Returns a JSON formatted String.
    • amplitudesAsSequence(SequenceFormat format): Returns a formatted String sequence.

    Sequence Formats:

    • AmplitudaResult.SequenceFormat.SINGLE_LINE: A single line of numbers.
    • AmplitudaResult.SequenceFormat.SINGLE_LINE (with delimiter): A single line with a custom delimiter (e.g., " * ").
    • AmplitudaResult.SequenceFormat.NEW_LINE: Each sample on a new line.
    AmplitudaResult<T> result;
     
    // Get result as list:
    List<Integer> samples = result.amplitudesAsList(); 
     
    // Get result as list only for second `1`
    List<Integer> samplesForFirstSecond = result.amplitudesForSecond(1);
     
    // Get result as json format String
    System.out.println(result.amplitudesAsJson());
    
    // Get result as single line sequence format String (horizontal String)
    System.out.println(result.amplitudesAsSequence(
            AmplitudaResult.SequenceFormat.SINGLE_LINE)
    );
    
    // Get result as single line sequence format String with custom delimiter `*` (horizontal String)
    System.out.println(result.amplitudesAsSequence(
            AmplitudaResult.SequenceFormat.SINGLE_LINE, " * ")
    );
    
    // Get result as new line sequence format String  
    System.out.println(result.amplitudesAsSequence(
            AmplitudaResult.SequenceFormat.NEW_LINE)
    );
  9. Process audio files with Amplituda

    master

    Initialize Amplituda with your Android context and use processAudio() to extract amplitude data. You can process audio from several source types:

    • File: A local file object.
    • String: A local file path or a URL.
    • Integer: A resource ID (e.g., R.raw.audio_file).

    The method returns an AmplitudaProcessingOutput<T> object which acts as a wrapper for the processing result.

    Amplituda amplituda = new Amplituda(context);
    
    // Local audio file
    processingOutput = amplituda.processAudio(new File("/storage/emulated/0/Music/Linc - Amplituda.mp3"));
    
    // Path to local audio file
    processingOutput = amplituda.processAudio("/storage/emulated/0/Music/Linc - Amplituda.mp3");
    
    // URL audio
    processingOutput = amplituda.processAudio("https://audio-url-example.com/amplituda.mp3");
    
    // Resource audio
    processingOutput = amplituda.processAudio(R.raw.amplituda);
  10. Monitor audio processing progress

    master

    Implement AmplitudaProgressListener to track the status of the audio processing task. The onProgress method provides a ProgressOperation and an integer representing the percentage (0-100).

    Progress Operations:

    • PROCESSING: General audio processing.
    • DECODING: Decoding raw or resource audio.
    • DOWNLOADING: Downloading audio from a URL.
    amplituda.processAudio(
        <audio>,
        new AmplitudaProgressListener() {
            @Override
            public void onStartProgress() {
                super.onStartProgress();
                System.out.println("Start Progress");
            }
    
            @Override
            public void onStopProgress() {
                super.onStopProgress();
                System.out.println("Stop Progress");
            }
    
            @Override
            public void onProgress(ProgressOperation operation, int progress) {
                String currentOperation = "";
                switch (operation) {
                    case PROCESSING: currentOperation = "Process audio"; break;
                    case DECODING: currentOperation = "Decode resource"; break;
                    case DOWNLOADING: currentOperation = "Download audio from url"; break;
                }
                System.out.printf("%s: %d%% %n", currentOperation, progress);
            }
        }
    )