Amplituda - an android library based on FFMPEG which process audio file and provide an array of samples. Based on the processed data, you can easily draw custom waveform using the value of the processed data array as the height of the single column.
Average processing time is equal to 1 second for audio with duration 3 min 20 seconds and 1 hour audio will be processed in approximately 20 seconds.
Amplituda could be used with:
- Compose AudioWaveform - lightweight
Jetpack Compose
library which draws waveform. - WaveformSeekBar - an android library which draws waveform using
android.view.View
(XML). - Custom
View
orComposable
implementation.
/* 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!");
}
});
/* And that's all! You can read full documentation below for more information about Amplituda features */
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.lincollincol:amplituda:x.y.z'
}
Amplituda amplituda = new Amplituda(context);
/**
* AmplitudaProcessingOutput<T> - wrapper class for Amplituda processing result.
* This class stores the output audio processing data
* and provides functions to obtain the result
* Generic type T - audio source type: File, String (URL or path), Integer (res/raw)
*/
AmplitudaProcessingOutput<T> processingOutput;
/* Process audio */
// 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);
/**
* Compress result data (optional)
* The output data can contain a lot of samples.
* For example:
* - 5-second audio can contain 100+ samples
* - 3-minute audio can contain 7000+ samples
* You can pass `Compress` params with the number of
* preferred samples per second and compress type as a parameter.
* Compress types:
* - Compress.SKIP - take first number of `preferredSamplesPerSecond` and skip others
* - Compress.PEEK - take peek number of `preferredSamplesPerSecond` and skip others
* - Compress.AVERAGE - merge all samples to number of `preferredSamplesPerSecond`
*/
// Example: input audio duration - 10 seconds
amplituda.processAudio(<audio>, Compress.withParams(Compress.AVERAGE, 1));
// Output: data with 1 sample per 1 second.
// Approximate output data size - 60 [duration] * 1 [preferredSamplePerSecond] = 60 [samples]
amplituda.processAudio(<audio>, Compress.withParams(Compress.AVERAGE, 5));
// Output: data with 5 sample per 1 second
// Approximate output data size - 60 [duration] * 5 [preferredSamplePerSecond] = 300 [samples]
/**
* Cache result data (optional)
* Amplituda provides cache implementation.
* You can reuse already processed audio files.
* Cache states:
* - Cache.REUSE - use already cached data or write
* processed data to cache (if cache data doesn't exist)
* - Cache.REFRESH - update existing cache data.
* WARNING: use this cache state, when you need to update existing cache data
* Key (optional) - custom cache key. Should be unique for each audio.
*/
// Enable amplituda cache feature:
amplituda.processAudio(<audio>, Cache.withParams(Cache.REUSE));
// Cache with custom key. The key should be unique
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(<audio>);
amplituda.clearCache("/storage/emulated/0/Music/Linc - Amplituda.mp3", false);
// Clear cache data by custom key.
amplituda.clearCache("my-unique-key", true);
/**
* AmplitudaProgressListener - progress listener class.
* This class has 3 methods that describe current progress:
* - void onStartProgress() - amplituda start processing (optional)
* - void onStopProgress() - amplituda stop processing (optional)
* - void onProgress(ProgressOperation operation, int progress) -
* amplituda process audio and share current operation and progress in percent (0-100)
* onProgress() also inform about current operation:
* - PROCESSING - amplituda start process audio
* - DECODING - amplituda decode raw/res audio
* - DOWNLOADING - amplituda download audio from 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);
}
}
)
AmplitudaProcessingOutput<T> processingOutput;
// . . . process audio . . .
/**
* AmplitudaResult<T> - wrapper class for the final result
* This class also provides many functions to format result: List<Integer>, String etc
* and info about input audio:
* - Input audio source: path to audio/resource or url.
* - Source type according to input audio: FILE, PATH, URL or RESOURCE
* - Audio duration
* Generic type T - audio source type: File, String (URL or path), Integer (res/raw)
*/
AmplitudaResult<T> result;
/**
* After processing you can get the result by calling the function get()
* This function has multiple overloads:
* - void get(successListener, errorListener)
* - void get(successListener)
* - AmplitudaResult<T> get(errorListener)
* - AmplitudaResult<T> get(successListener)
*/
// 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();
/**
* Exceptions. More info about exceptions here:
* https://github.com/lincollincol/Amplituda/tree/master/app/src/main/java/linc/com/amplituda/exceptions
*/
processingOutput.get((AmplitudaException exception) -> {
if(exception instanceof AmplitudaIOException) {
// Handle io exceptions
} else if(exception instanceof AmplitudaProcessingException) {
// Handle processing exceptions
} else {
exception.printStackTrace();
}
});
/**
* Format result. As written earlier, AmplitudaResult
* has many functions for formatting the result
*/
AmplitudaResult<T> result;
// Get result as list:
List<Integer> samples = result.amplitudesAsList();
System.out.println(Arrays.toString(samples.toArray()));
// Output: [0, 0, 0, 0, 0, 5, 3, 6, . . . , 6, 4, 7, 1, 0, 0, 0]
// Get result as list only for second `1`
List<Integer> samplesForFirstSecond = result.amplitudesForSecond(1);
System.out.println(Arrays.toString(samples.toArray()));
// Output: [0, 0, 5, . . . 6, 4, 0]
// Get result as json format String
System.out.println(result.amplitudesAsJson());
// Output: [0, 0, 0, 0, 0, 5, 3, 6, . . . , 6, 4, 7, 1, 0, 0, 0]
// Get result as single line sequence format String (horizontal String)
System.out.println(result.amplitudesAsSequence(
AmplitudaResult.SequenceFormat.SINGLE_LINE)
);
// Output: 0 0 0 0 0 5 3 6 . . . 6 4 7 1 0 0 0
// Get result as single line sequence format String with custom delimiter `*` (horizontal String)
System.out.println(result.amplitudesAsSequence(
AmplitudaResult.SequenceFormat.SINGLE_LINE, " * ")
);
// Output: 0 * 0 * 0 * 0 * 0 * 5 * 3 * 6 * . . . 6 * 4 * 7 * 1 * 0 * 0 * 0
// Get result as new line sequence format String
System.out.println(result.amplitudesAsSequence(
AmplitudaResult.SequenceFormat.NEW_LINE)
);
/* Output:
0
0
5
3
. . .
6
1
0
0
*/
Amplituda amplituda = new Amplituda(context);
// Use default android Log constants to set priority. The second parameter - enable or disable logs.
// Amplituda logs are disabled by default
amplituda.setLogConfig(Log.DEBUG, true);
Add permissions to Manifest.xml file in your app and grant it, before using Amplituda
<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+ -->
<application
android:requestLegacyExternalStorage="true">
<activity . . ./>
</application>
Add android:extractNativeLibs="false"
to application in the Manifest.xml
<application
. . .
android:extractNativeLibs="false"
. . . >
<activity . . ./>
</application>
Copyright 2020-present lincollincol
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.