From 889b1dab59f83888f00c25bd667654edb8d3c612 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 14 Aug 2021 19:56:37 +0300 Subject: [PATCH 1/2] Move result format logic to AmplitudaResult. Amplituda class only for ndk processing. Issue #16 --- .../java/linc/com/amplituda/Amplituda.java | 476 ++++-------------- .../linc/com/amplituda/AmplitudaLogger.java | 4 +- .../amplituda/AmplitudaProcessingOutput.java | 143 ++++++ .../linc/com/amplituda/AmplitudaResult.java | 168 +++++++ .../java/linc/com/amplituda/FileManager.java | 44 +- .../java/linc/com/amplituda/InputAudio.java | 51 ++ .../amplituda/callback/AmplitudaCallback.java | 8 - ...tener.java => AmplitudaErrorListener.java} | 3 +- .../callback/AmplitudaSuccessListener.java | 10 + .../com/amplituda/callback/ListCallback.java | 9 - .../amplituda/callback/StringCallback.java | 7 - .../ExtendedProcessingDisabledException.java | 9 - .../java/linc/com/example/MainActivity.java | 97 ++-- example/src/main/res/raw/clap.ogg | Bin 0 -> 43388 bytes 14 files changed, 534 insertions(+), 495 deletions(-) create mode 100644 app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java create mode 100644 app/src/main/java/linc/com/amplituda/AmplitudaResult.java create mode 100644 app/src/main/java/linc/com/amplituda/InputAudio.java delete mode 100644 app/src/main/java/linc/com/amplituda/callback/AmplitudaCallback.java rename app/src/main/java/linc/com/amplituda/callback/{ErrorListener.java => AmplitudaErrorListener.java} (60%) create mode 100644 app/src/main/java/linc/com/amplituda/callback/AmplitudaSuccessListener.java delete mode 100644 app/src/main/java/linc/com/amplituda/callback/ListCallback.java delete mode 100644 app/src/main/java/linc/com/amplituda/callback/StringCallback.java delete mode 100644 app/src/main/java/linc/com/amplituda/exceptions/io/ExtendedProcessingDisabledException.java create mode 100644 example/src/main/res/raw/clap.ogg diff --git a/app/src/main/java/linc/com/amplituda/Amplituda.java b/app/src/main/java/linc/com/amplituda/Amplituda.java index 2b3380c..2511dee 100644 --- a/app/src/main/java/linc/com/amplituda/Amplituda.java +++ b/app/src/main/java/linc/com/amplituda/Amplituda.java @@ -1,408 +1,168 @@ package linc.com.amplituda; import android.content.Context; -import android.text.TextUtils; -import android.util.Log; import android.webkit.URLUtil; import java.io.File; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import linc.com.amplituda.callback.ErrorListener; -import linc.com.amplituda.callback.ListCallback; -import linc.com.amplituda.callback.StringCallback; + import linc.com.amplituda.exceptions.*; import linc.com.amplituda.exceptions.io.*; -import linc.com.amplituda.exceptions.processing.*; public final class Amplituda { - public static final int SINGLE_LINE_SEQUENCE_FORMAT = 0; - public static final int NEW_LINE_SEQUENCE_FORMAT = 1; - - public static final int SECONDS = 2; - public static final int MILLIS = 3; - private static final String OPERATION_PROCESSING = "Processing"; private static final String OPERATION_PREPARING = "Preparing"; - private final ErrorListener errorListener; private final FileManager fileManager; - private final List errors = new LinkedList<>(); - - private String amplitudes; - - private Amplituda( - ErrorListener errorListener, - FileManager fileManager, - final int priority, - final boolean enable - ) { - this.errorListener = errorListener; - this.fileManager = fileManager; - AmplitudaLogger.priority(priority); + + public Amplituda(final Context context) { + fileManager = new FileManager(context); + } + + /** + * Enable Amplituda logs for mor processing information + * @param priority - android Log constant. For example Log.DEBUG + * @param enable - turn on / off logs + */ + public Amplituda setLogConfig(final int priority, final boolean enable) { AmplitudaLogger.enable(enable); + AmplitudaLogger.priority(priority); + return this; } /** * Calculate amplitudes from file * @param audio - source file */ - public synchronized Amplituda fromFile(final File audio) { - // Clear previous data when Amplituda used repeatedly - clearPreviousAmplitudaData(); - + private AmplitudaResultJNI processFileJNI(final File audio) throws AmplitudaException { // Process audio if(!audio.exists()) { - throwException(new FileNotFoundException()); - } else { - if(!fileManager.isAudioFile(audio.getPath())) { - throwException(new FileOpenException()); - return this; - } - - // Save start time - long startTime = System.currentTimeMillis(); - - // Save current audio path - fileManager.retainPath(audio.getPath()); + throw new FileNotFoundException(); + } - // Process input audio - AmplitudaResultJNI result = amplitudesFromAudioJNI(audio.getPath()); + if(!fileManager.isAudioFile(audio.getPath())) { + throw new FileOpenException(); + } - // Copy result data - amplitudes = result.getAmplitudes(); - errors.addAll(result.getErrors()); + // Save start time + long startTime = System.currentTimeMillis(); - // Emit all exceptions after subscribe - handleAmplitudaErrors(); + // Process input audio + AmplitudaResultJNI result = amplitudesFromAudioJNI(audio.getPath()); - // Log operation time - AmplitudaLogger.logOperationTime(OPERATION_PROCESSING, startTime); + // Log operation time + AmplitudaLogger.logOperationTime(OPERATION_PROCESSING, startTime); + return result; + } + + public AmplitudaProcessingOutput processAudio(final File audio) { + InputAudio inputAudio = new InputAudio<>( + audio, + fileManager.getAudioDuration(audio.getPath()), + InputAudio.Type.FILE + ); + try { + return new AmplitudaProcessingOutput<>(processFileJNI(audio), inputAudio); + } catch (AmplitudaException exception) { + // Handle processing error + return new AmplitudaProcessingOutput<>(exception, inputAudio); } - return this; } /** * Calculate amplitudes from file - * @param audio - path or url to input audio file - */ - public Amplituda fromFile(final String audio) { - if(URLUtil.isValidUrl(audio)) { - if(!fileManager.cacheNotNull()) { - throwException(new ExtendedProcessingDisabledException()); - return this; + * @param audio - local path or url to input audio file + */ + public AmplitudaProcessingOutput processAudio(final String audio) { + InputAudio inputAudio = new InputAudio<>(audio); + + try { + // When audio is local file - process as file + if(!URLUtil.isValidUrl(audio)) { + inputAudio.setType(InputAudio.Type.PATH); + inputAudio.setDuration(fileManager.getAudioDuration(audio)); + return new AmplitudaProcessingOutput<>( + processFileJNI(new File(audio)), + inputAudio + ); } + // When audio is URL + inputAudio.setType(InputAudio.Type.URL); // Save start time long startTime = System.currentTimeMillis(); // Copy audio from url to local storage File tempAudio = fileManager.getUrlFile(audio); + + // Check for success copy operation from url to local tmp if(tempAudio == null) { - throwException(new InvalidAudioUrlException()); - return this; + return new AmplitudaProcessingOutput<>( + new InvalidAudioUrlException(), + inputAudio + ); } // Log operation time AmplitudaLogger.logOperationTime(OPERATION_PREPARING, startTime); // Process local audio - fromFile(tempAudio); + AmplitudaResultJNI result = processFileJNI(tempAudio); + + // Save tmp file duration + inputAudio.setDuration(fileManager.getAudioDuration(tempAudio.getPath())); + + // Remove tmp file fileManager.deleteFile(tempAudio); - } else { - fromFile(new File(audio)); + + return new AmplitudaProcessingOutput<>(result, inputAudio); + } catch (AmplitudaException exception) { + // Handle processing error + return new AmplitudaProcessingOutput<>(exception, inputAudio); } - return this; } /** * Calculate amplitudes from file - * @param rawId - path to source file + * @param audio - path to res/raw source file */ - public Amplituda fromFile(final int rawId) { - if(!fileManager.cacheNotNull()) { - throwException(new ExtendedProcessingDisabledException()); - return this; - } + public AmplitudaProcessingOutput processAudio(final int audio) { + InputAudio inputAudio = new InputAudio<>(audio, InputAudio.Type.RESOURCE); // Save start time long startTime = System.currentTimeMillis(); // Copy raw to local file - File tempAudio = fileManager.getRawFile(rawId); + File tempAudio = fileManager.getRawFile(audio); + + // Check for success copy operation from res to local tmp if(tempAudio == null) { - throwException(new InvalidRawResourceException()); - return this; + return new AmplitudaProcessingOutput<>( + new InvalidRawResourceException(), + inputAudio + ); } // Log operation time AmplitudaLogger.logOperationTime(OPERATION_PREPARING, startTime); - // Process local raw file - fromFile(tempAudio); - fileManager.deleteFile(tempAudio); - - return this; - } - - /** - * Calculate amplitudes from file - * @param audio - path or url to input audio file - * Please use `fromFile(final String audio)` instead of this method - * - * ONLY TO SUPPORT WaveformSeekBar library 3.0.0 version with new Ampituda versions - * [https://github.com/massoudss/waveformSeekBar] - */ - @Deprecated - public Amplituda fromPath(final String audio) { - fromFile(audio); - return this; - } - - /** - * Convert result amplitudes to List - * @param listCallback - result callback - */ - public Amplituda amplitudesAsList(final ListCallback listCallback) { - if(amplitudes == null || amplitudes.isEmpty()) - return this; - - String[] log = amplitudes.split("\n"); - List amplitudes = new ArrayList<>(); - - for (String amplitude : log) { - if(amplitude.isEmpty()) { - break; - } - amplitudes.add(Integer.valueOf(amplitude)); - } - listCallback.call(amplitudes); - return this; - } - - /** - * Convert result amplitudes to JSON format - * @param jsonCallback - result callback - */ - public Amplituda amplitudesAsJson(final StringCallback jsonCallback) { - if(amplitudes == null || amplitudes.isEmpty()) - return this; - jsonCallback.call("[" + amplitudesToSingleLineSequence(amplitudes, ", ") + "]"); - return this; - } - - /** - * Overload for amplitudesAsSequence method. Use space (" ") as a default delimiter - * @param format - output format: single line or multiline output string - * @param stringCallback - result callback - */ - public Amplituda amplitudesAsSequence(final int format, final StringCallback stringCallback) { - if(amplitudes == null || amplitudes.isEmpty()) - return this; - - amplitudesAsSequence(format, " ", stringCallback); - return this; - } - - /** - * Convert result amplitudes to single line string with custom delimiter and send result to user via stringCallback - * @param format - output format: single line or multiline output string - * @param singleLineDelimiter - delimiter between amplitudes. WARNING: this parameter will be ignored when NEW_LINE_SEQUENCE_FORMAT passed as a parameter - * @param stringCallback - result callback - */ - public Amplituda amplitudesAsSequence( - final int format, - final String singleLineDelimiter, - final StringCallback stringCallback - ) { - if(amplitudes == null || amplitudes.isEmpty()) - return this; - - switch (format) { - case SINGLE_LINE_SEQUENCE_FORMAT: stringCallback.call(amplitudesToSingleLineSequence( - amplitudes, - singleLineDelimiter - )); break; - case NEW_LINE_SEQUENCE_FORMAT: stringCallback.call(amplitudes); break; - default: throwException(new InvalidParameterFlagException()); break; - } - return this; - } - - /** - * Extracts list of amplitudes per specific second - * @param second - specific second from input file - * @param listCallback - result callback - */ - public Amplituda amplitudesForSecond(final int second, final ListCallback listCallback) { - amplitudesAsList(new ListCallback() { - @Override - public void call(List data) { - int duration = (int) getDuration(SECONDS); - int aps = data.size() / duration; // amplitudes per second - // Use second as a map key - int currentSecond = 0; - // Map with format = Map - Map> amplitudes = new LinkedHashMap<>(); - // Temporary amplitudes list - List amplitudesPerSecond = new ArrayList<>(); - - for(int sampleIndex = 0; sampleIndex < data.size(); sampleIndex++) { - if(sampleIndex % aps == 0) { // Save all amplitudes when current frame index equals to aps - // Save amplitudes to map - amplitudes.put(currentSecond, new ArrayList<>(amplitudesPerSecond)); - // Clear temporary amplitudes - amplitudesPerSecond.clear(); - // Increase current second - currentSecond++; - } else { - // Add amplitude to temporary list - amplitudesPerSecond.add(data.get(sampleIndex)); - } - } - - if(second > duration) { - throwException(new SecondOutOfBoundsException(second, duration)); - } else { - listCallback.call(amplitudes.get(second)); - } - } - }); - return this; - } - - /** - * Merge result amplitudes according to samplesPerSecond - * @param samplesPerSecond - number of samples per audio second - * For example: - * audio duration = 200 seconds - * after Amplituda processing, 1 second contains 40 samples - * 200 seconds contains 200 * 40 = 8000 - * case 1: samplesPerSecond = 1, function will merge this 40 samples to 1. - * Output size will be 200 amplitudes - * case 2: samplesPerSecond = 20, function will merge this 40 samples to 20. - * Output size will be 4000 amplitudes - * Advantage: small output size - * Disadvantage: output quality - */ - public Amplituda compressAmplitudes(final int samplesPerSecond) { - amplitudesAsList(new ListCallback() { - @Override - public void call(List data) { - if(samplesPerSecond <= 0) { - throwException(new InvalidParameterFlagException()); - return; - } - - int duration = (int) getDuration(SECONDS); - int aps = data.size() / duration; - - if(samplesPerSecond > aps) { - throwException(new SampleOutOfBoundsException(aps, samplesPerSecond)); - return; - } - - if(aps == samplesPerSecond) { - return; - } - - int apsDivider = aps / samplesPerSecond; - int sum = 0; - StringBuilder compressed = new StringBuilder(); - - if(apsDivider < 2) { - apsDivider = 2; - } - - for(int sampleIndex = 0; sampleIndex < data.size(); sampleIndex++) { - if(sampleIndex % apsDivider == 0) { - compressed.append(sum / apsDivider); - compressed.append('\n'); - sum = 0; - } else { - sum += data.get(sampleIndex); - } - } - - amplitudes = compressed.toString(); - } - }); - return this; - } - - /** - * Returns duration from file in seconds or millis - * @param format - output time format: SECONDS or MILLIS - */ - public long getDuration(final int format) { - String inputAudioFile = fileManager.getCachePath(); - - if(inputAudioFile == null) { - throwException(new NoInputFileException()); - return 0; - } - - if(format != SECONDS && format != MILLIS) { - throwException(new InvalidParameterFlagException()); - return 0; - } - - long duration = fileManager.getAudioDuration(inputAudioFile); + try { + // Process local raw file + AmplitudaResultJNI result = processFileJNI(tempAudio); - if (format == SECONDS) { - return duration / 1000; - } - return duration; - } + // Save tmp resource file duration + inputAudio.setDuration(fileManager.getAudioDuration(tempAudio.getPath())); - /** - * Convert result amplitudes to single line string with delimiter - * @param amplitudes - result from native c++ code - * @param delimiter - amplitudes separator - * @return string from amplitudes with custom delimiter. Example -> 0, 1, 2 | delimiter = ", " - */ - private String amplitudesToSingleLineSequence(final String amplitudes, final String delimiter) { - String[] log = amplitudes.split("\n"); - return TextUtils.join(delimiter, log); - } + // Delete tmp + fileManager.deleteFile(tempAudio); - /** - * Emit new exception event for listener - * @param exception - cause - */ - private void throwException(final AmplitudaException exception) { - if(errorListener == null) { - errors.add(exception); - return; + return new AmplitudaProcessingOutput<>(result, inputAudio); + } catch (AmplitudaException exception) { + // Handle processing error + return new AmplitudaProcessingOutput<>(exception, inputAudio); } - errorListener.call(exception); } - /** - * Handle errors from ndk side - */ - private synchronized void handleAmplitudaErrors() { - if(errors.isEmpty()) - return; - for(final AmplitudaException exception : errors) { - throwException(exception); - } - errors.clear(); - } - - /** - * Clear local variables. Call this function when Amplituda object used repeatedly - */ - private void clearPreviousAmplitudaData() { - amplitudes = null; - errors.clear(); - fileManager.clearPath(); - } /** * NDK part @@ -413,52 +173,4 @@ private void clearPreviousAmplitudaData() { native AmplitudaResultJNI amplitudesFromAudioJNI(String pathToAudio); - public static final class Builder { - - private int logPriority = Log.DEBUG; - private boolean logEnable = false; - private ErrorListener errorListener = null; - private final FileManager fileManager = new FileManager(); - - /** - * Observe and handle errors - * @param errorListener - callback with exception as a parameter - */ - public Builder setErrorListener(final ErrorListener errorListener) { - this.errorListener = errorListener; - return this; - } - - /** - * Enable processing audio from url or res/raw - * @param context - this param used for initialization - * cache directory and resources for res/raw - */ - public Builder enableExtendedProcessing(final Context context) { - fileManager.initCache(context); - fileManager.initResources(context); - return this; - } - - /** - * Enable Amplituda logs for mor processing information - * @param priority - android Log constant. For example Log.DEBUG - * @param enable - turn on / off logs - */ - public Builder setLogConfig(final int priority, final boolean enable) { - this.logPriority = priority; - this.logEnable = enable; - return this; - } - - public Amplituda build() { - return new Amplituda( - this.errorListener, - this.fileManager, - this.logPriority, - this.logEnable - ); - } - } - } diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java b/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java index 93d7017..9a3a739 100644 --- a/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java +++ b/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java @@ -7,8 +7,8 @@ public final class AmplitudaLogger { private static final String LIB_TAG = "AMPLITUDA"; - private static int priority; - private static boolean enable; + private static int priority = Log.DEBUG; + private static boolean enable = false; /** * Print message to logcat diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java b/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java new file mode 100644 index 0000000..7b7a3d8 --- /dev/null +++ b/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java @@ -0,0 +1,143 @@ +package linc.com.amplituda; + +import java.util.LinkedHashSet; +import java.util.List; + +import linc.com.amplituda.callback.AmplitudaErrorListener; +import linc.com.amplituda.callback.AmplitudaSuccessListener; +import linc.com.amplituda.exceptions.AmplitudaException; +import linc.com.amplituda.exceptions.processing.InvalidParameterFlagException; +import linc.com.amplituda.exceptions.processing.SampleOutOfBoundsException; + +public class AmplitudaProcessingOutput { + + private final AmplitudaResult amplitudaResult; + private LinkedHashSet processingErrors = new LinkedHashSet<>(); + + private AmplitudaProcessingOutput( + final String amplitudes, + final InputAudio inputAudio + ) { + amplitudaResult = new AmplitudaResult<>(amplitudes, inputAudio); + } + + AmplitudaProcessingOutput( + final AmplitudaResultJNI processingData, + final InputAudio inputAudio + ) { + this( + processingData.getAmplitudes(), + inputAudio + ); + this.processingErrors.addAll(processingData.getErrors()); + } + + AmplitudaProcessingOutput(final AmplitudaException exception, final InputAudio inputAudio) { + this("", inputAudio); + this.processingErrors.add(exception); + } + + /** + * Merge result amplitudes according to samplesPerSecond + * @param preferredSamplesPerSecond - number of samples per audio second + * For example: + * audio duration = 200 seconds + * after Amplituda processing, 1 second contains 40 samples + * 200 seconds contains 200 * 40 = 8000 + * case 1: samplesPerSecond = 1, function will merge this 40 samples to 1. + * Output size will be 200 amplitudes + * case 2: samplesPerSecond = 20, function will merge this 40 samples to 20. + * Output size will be 4000 amplitudes + * Advantage: small output size + * Disadvantage: output quality + */ + public AmplitudaProcessingOutput compress(final int preferredSamplesPerSecond) { + List data = amplitudaResult.amplitudesAsList(); + + if(preferredSamplesPerSecond <= 0) { + throwException(new InvalidParameterFlagException(), null); + return this; + } + + int duration = (int) amplitudaResult.getAudioDuration(AmplitudaResult.DurationUnit.SECONDS); + int aps = data.size() / duration; + + if(preferredSamplesPerSecond > aps) { + throwException(new SampleOutOfBoundsException(aps, preferredSamplesPerSecond), null); + return this; + } + + if(aps == preferredSamplesPerSecond) { + return this; + } + + int apsDivider = aps / preferredSamplesPerSecond; + int sum = 0; + StringBuilder compressed = new StringBuilder(); + + if(apsDivider < 2) { + apsDivider = 2; + } + + for(int sampleIndex = 0; sampleIndex < data.size(); sampleIndex++) { + if(sampleIndex % apsDivider == 0) { + compressed.append(sum / apsDivider); + compressed.append('\n'); + sum = 0; + } else { + sum += data.get(sampleIndex); + } + } + + amplitudaResult.setAmplitudes(compressed.toString()); + return this; + } + + + public void get( + final AmplitudaSuccessListener successListener, + final AmplitudaErrorListener errorListener + ) { + handleAmplitudaProcessingErrors(errorListener); + successListener.onSuccess(amplitudaResult); + } + + public void get(final AmplitudaSuccessListener successListener) { + get(successListener, null); + } + + public AmplitudaResult get(final AmplitudaErrorListener errorListener) { + handleAmplitudaProcessingErrors(errorListener); + return amplitudaResult; + } + + public AmplitudaResult get() { + return amplitudaResult; + } + + private void handleAmplitudaProcessingErrors(final AmplitudaErrorListener errorListener) { + if(processingErrors.isEmpty()){ + processingErrors = null; + return; + } + + for(final AmplitudaException exception : processingErrors) { + throwException(exception, errorListener); + } + + processingErrors.clear(); + processingErrors = null; + } + + private void throwException( + final AmplitudaException exception, + final AmplitudaErrorListener errorListener + ) { + if(errorListener == null) { + processingErrors.add(exception); + return; + } + errorListener.onError(exception); + } + +} diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaResult.java b/app/src/main/java/linc/com/amplituda/AmplitudaResult.java new file mode 100644 index 0000000..2fc005d --- /dev/null +++ b/app/src/main/java/linc/com/amplituda/AmplitudaResult.java @@ -0,0 +1,168 @@ +package linc.com.amplituda; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class AmplitudaResult { + + private String amplitudes; + private final InputAudio inputAudio; + + AmplitudaResult( + final String amplitudes, + final InputAudio inputAudio + ) { + this.amplitudes = amplitudes; + this.inputAudio = inputAudio; + } + + /** + * Returns input audio source: String (path/url), File or Integer resource + */ + public T getAudioSource() { + return inputAudio.getSource(); + } + + /** + * Returns duration from file in seconds or millis + * @param unit - output time unit: SECONDS or MILLIS + */ + public long getAudioDuration(DurationUnit unit) { + if (unit == DurationUnit.SECONDS) { + return inputAudio.getDuration() / 1000; + } + return inputAudio.getDuration(); + } + + /** + * Returns input audio type: URL, RESOURCE or FILE + */ + public InputAudio.Type getInputAudioType() { + return inputAudio.getType(); + } + + /** + * Convert result amplitudes to List + */ + public List amplitudesAsList() { + if(amplitudes == null || amplitudes.isEmpty()) + return Collections.emptyList(); + + String[] log = amplitudes.split("\n"); + List amplitudes = new ArrayList<>(); + + for (String amplitude : log) { + if(amplitude.isEmpty()) { + break; + } + amplitudes.add(Integer.valueOf(amplitude)); + } + return amplitudes; + } + + /** + * Convert result amplitudes to JSON format + */ + public String amplitudesAsJson() { + if(amplitudes == null || amplitudes.isEmpty()) + return ""; + return Arrays.toString(amplitudesAsList().toArray()); + } + + /** + * Overload for amplitudesAsSequence method. Use space (" ") as a default delimiter + * @param format - output format: single line or multiline output string + */ + public String amplitudesAsSequence(final SequenceFormat format) { + if(amplitudes == null || amplitudes.isEmpty()) + return ""; + return amplitudesAsSequence(format, " "); + } + + /** + * Convert result amplitudes to single line string with custom delimiter and send result to user via stringCallback + * @param format - output format: single line or multiline output string + * @param singleLineDelimiter - delimiter between amplitudes. WARNING: this parameter will be ignored when NEW_LINE_SEQUENCE_FORMAT passed as a parameter + */ + public String amplitudesAsSequence( + final SequenceFormat format, + final String singleLineDelimiter + ) { + if(amplitudes == null || amplitudes.isEmpty()) + return ""; + + if (format == SequenceFormat.SINGLE_LINE) { + return amplitudesToSingleLineSequence( + amplitudes, + singleLineDelimiter + ); + } + return amplitudes; + } + + /** + * Extracts list of amplitudes per specific second + * @param second - specific second from input file + */ + public List amplitudesForSecond(final int second) { + List data = amplitudesAsList(); + + final int duration = (int) getAudioDuration(DurationUnit.SECONDS); + final int aps = data.size() / duration; // amplitudes per second + + // Use second as a map key + int currentSecond = 0; + + // Map with format = Map + Map> amplitudes = new LinkedHashMap<>(); + + // Temporary amplitudes list + List amplitudesPerSecond = new ArrayList<>(); + + for(int sampleIndex = 0; sampleIndex < data.size(); sampleIndex++) { + if(sampleIndex % aps == 0) { // Save all amplitudes when current frame index equals to aps + // Save amplitudes to map + amplitudes.put(currentSecond, new ArrayList<>(amplitudesPerSecond)); + // Clear temporary amplitudes + amplitudesPerSecond.clear(); + // Increase current second + currentSecond++; + } else { + // Add amplitude to temporary list + amplitudesPerSecond.add(data.get(sampleIndex)); + } + } + + return amplitudes.get(second); + } + + /** + * Convert result amplitudes to single line string with delimiter + * @param amplitudes - result from native c++ code + * @param delimiter - amplitudes separator + * @return string from amplitudes with custom delimiter. Example -> 0, 1, 2 | delimiter = ", " + */ + private String amplitudesToSingleLineSequence(final String amplitudes, final String delimiter) { + String[] log = amplitudes.split("\n"); + return TextUtils.join(delimiter, log); + } + + void setAmplitudes(final String amplitudes) { + this.amplitudes = amplitudes; + } + + public enum DurationUnit { + SECONDS, MILLIS + } + + public enum SequenceFormat { + SINGLE_LINE, NEW_LINE + } + +} diff --git a/app/src/main/java/linc/com/amplituda/FileManager.java b/app/src/main/java/linc/com/amplituda/FileManager.java index 289569c..3bcd775 100644 --- a/app/src/main/java/linc/com/amplituda/FileManager.java +++ b/app/src/main/java/linc/com/amplituda/FileManager.java @@ -18,30 +18,13 @@ final class FileManager { + static final String RAW_TEMP = "amplituda_tmp_raw"; private Resources resources; - private String cachePath; private String cache; - static final String RAW_TEMP = "amplituda_tmp_raw"; - /** - * Check not null for cache directory - */ - boolean cacheNotNull() { - return cache != null; - } - - /** - * Init cache directory path - */ - synchronized void initCache(final Context context) { - cache = context.getCacheDir().getPath() + File.separator; - } - - /** - * Init resources for res/raw decoding - */ - synchronized void initResources(final Context context) { + public FileManager(final Context context) { resources = context.getResources(); + cache = context.getCacheDir().getPath() + File.separator; } /** @@ -53,27 +36,6 @@ synchronized void deleteFile(final File file) { } } - /** - * Retain current audio path - */ - synchronized void retainPath(final String path) { - cachePath = path; - } - - /** - * Clear saved path - */ - synchronized void clearPath() { - cachePath = ""; - } - - /** - * Return stashed path - */ - synchronized String getCachePath() { - return cachePath; - } - /** * Validate audio file * @param path - audio file path diff --git a/app/src/main/java/linc/com/amplituda/InputAudio.java b/app/src/main/java/linc/com/amplituda/InputAudio.java new file mode 100644 index 0000000..b00d149 --- /dev/null +++ b/app/src/main/java/linc/com/amplituda/InputAudio.java @@ -0,0 +1,51 @@ +package linc.com.amplituda; + +public class InputAudio { + private final T source; + private long duration; + private InputAudio.Type type; + + InputAudio(final T source, long duration, final InputAudio.Type type) { + this.source = source; + this.type = type; + this.duration = duration; + } + + public InputAudio(T source, long duration) { + this.source = source; + this.duration = duration; + } + + public InputAudio(T source, Type type) { + this.source = source; + this.type = type; + } + + public InputAudio(T source) { + this.source = source; + } + + public T getSource() { + return source; + } + + public long getDuration() { + return duration; + } + + void setDuration(final long duration) { + this.duration = duration; + } + + public Type getType() { + return type; + } + + void setType(Type type) { + this.type = type; + } + + public enum Type { + FILE, PATH, URL, RESOURCE + } +} diff --git a/app/src/main/java/linc/com/amplituda/callback/AmplitudaCallback.java b/app/src/main/java/linc/com/amplituda/callback/AmplitudaCallback.java deleted file mode 100644 index ccaaffe..0000000 --- a/app/src/main/java/linc/com/amplituda/callback/AmplitudaCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package linc.com.amplituda.callback; - -/** - * Base Callback interface - */ -interface AmplitudaCallback { - void call(final T data); -} diff --git a/app/src/main/java/linc/com/amplituda/callback/ErrorListener.java b/app/src/main/java/linc/com/amplituda/callback/AmplitudaErrorListener.java similarity index 60% rename from app/src/main/java/linc/com/amplituda/callback/ErrorListener.java rename to app/src/main/java/linc/com/amplituda/callback/AmplitudaErrorListener.java index ff5682a..ee15f99 100644 --- a/app/src/main/java/linc/com/amplituda/callback/ErrorListener.java +++ b/app/src/main/java/linc/com/amplituda/callback/AmplitudaErrorListener.java @@ -6,5 +6,6 @@ /** * Callback interface for error events */ -public interface ErrorListener extends AmplitudaCallback { +public interface AmplitudaErrorListener { + void onError(final AmplitudaException exception); } diff --git a/app/src/main/java/linc/com/amplituda/callback/AmplitudaSuccessListener.java b/app/src/main/java/linc/com/amplituda/callback/AmplitudaSuccessListener.java new file mode 100644 index 0000000..31d8ce7 --- /dev/null +++ b/app/src/main/java/linc/com/amplituda/callback/AmplitudaSuccessListener.java @@ -0,0 +1,10 @@ +package linc.com.amplituda.callback; + +import linc.com.amplituda.AmplitudaResult; + +/** + * Callback interface for success processing event + */ +public interface AmplitudaSuccessListener { + void onSuccess(final AmplitudaResult result); +} diff --git a/app/src/main/java/linc/com/amplituda/callback/ListCallback.java b/app/src/main/java/linc/com/amplituda/callback/ListCallback.java deleted file mode 100644 index f72a84d..0000000 --- a/app/src/main/java/linc/com/amplituda/callback/ListCallback.java +++ /dev/null @@ -1,9 +0,0 @@ -package linc.com.amplituda.callback; - -import java.util.List; - -/** - * Callback interface for list output - */ -public interface ListCallback extends AmplitudaCallback> { -} diff --git a/app/src/main/java/linc/com/amplituda/callback/StringCallback.java b/app/src/main/java/linc/com/amplituda/callback/StringCallback.java deleted file mode 100644 index 8d747fc..0000000 --- a/app/src/main/java/linc/com/amplituda/callback/StringCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package linc.com.amplituda.callback; - -/** - * Callback interface for string output - */ -public interface StringCallback extends AmplitudaCallback { -} diff --git a/app/src/main/java/linc/com/amplituda/exceptions/io/ExtendedProcessingDisabledException.java b/app/src/main/java/linc/com/amplituda/exceptions/io/ExtendedProcessingDisabledException.java deleted file mode 100644 index 32db8b9..0000000 --- a/app/src/main/java/linc/com/amplituda/exceptions/io/ExtendedProcessingDisabledException.java +++ /dev/null @@ -1,9 +0,0 @@ -package linc.com.amplituda.exceptions.io; - -import static linc.com.amplituda.ErrorCode.EXTENDED_PROCESSING_DISABLED_IO_CODE; - -public class ExtendedProcessingDisabledException extends AmplitudaIOException { - public ExtendedProcessingDisabledException() { - super("You are trying to process audio from res/raw or url with disabled extended processing.\nPlease call `enableExtendedProcessing(final Context context)` while building Amplituda", EXTENDED_PROCESSING_DISABLED_IO_CODE); - } -} diff --git a/example/src/main/java/linc/com/example/MainActivity.java b/example/src/main/java/linc/com/example/MainActivity.java index bc6041b..52e2283 100644 --- a/example/src/main/java/linc/com/example/MainActivity.java +++ b/example/src/main/java/linc/com/example/MainActivity.java @@ -5,13 +5,14 @@ import android.os.Bundle; import android.util.Log; -import java.io.FileWriter; -import java.io.IOException; +import java.io.File; import java.util.Arrays; +import java.util.Locale; import linc.com.amplituda.Amplituda; -import linc.com.amplituda.exceptions.io.AmplitudaIOException; -import linc.com.amplituda.exceptions.processing.AmplitudaProcessingException; +import linc.com.amplituda.AmplitudaProcessingOutput; +import linc.com.amplituda.AmplitudaResult; +import linc.com.amplituda.InputAudio; public class MainActivity extends AppCompatActivity { @@ -20,37 +21,61 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - Amplituda amplituda = new Amplituda.Builder() - .enableExtendedProcessing(this) - .setErrorListener(error -> { - if(error instanceof AmplitudaIOException) { - System.out.println("IO exception!"); - } else if(error instanceof AmplitudaProcessingException) { - System.out.println("Processing exception!"); - } - }) - .setLogConfig(Log.ERROR, true) - .build(); - - amplituda.fromFile("/storage/emulated/0/Music/Linc - Amplituda.mp3") - .compressAmplitudes(1) - .amplitudesAsJson(json -> { - System.out.println("As json: " + json); - }) - .amplitudesAsList(list -> { - System.out.print("As list: " + Arrays.toString(list.toArray())); - }) - .amplitudesAsSequence(Amplituda.SINGLE_LINE_SEQUENCE_FORMAT, defSeq -> { - System.out.println("As sequence default: " + defSeq); - }) - .amplitudesAsSequence(Amplituda.SINGLE_LINE_SEQUENCE_FORMAT, " * ", custSeq -> { - System.out.println("As sequence custom: " + custSeq); - }) - .amplitudesAsSequence(Amplituda.NEW_LINE_SEQUENCE_FORMAT, newLineSeq -> { - System.out.println("As new line sequence: " + newLineSeq); - }) - .amplitudesForSecond(1, amps -> { - System.out.print("For second: " + Arrays.toString(amps.toArray())); - }); + Amplituda amplituda = new Amplituda(this); + amplituda.setLogConfig(Log.DEBUG, true); + + AmplitudaResult localPathResult = amplituda.processAudio("/storage/emulated/0/Music/kygo.mp3") + .get(); + printResult(localPathResult); + + AmplitudaResult localFileResult = amplituda.processAudio(new File("/storage/emulated/0/Music/kygo.mp3")) + .get(); + printResult(localFileResult); + + + Thread urlTask = new Thread(() -> { + AmplitudaResult urlResult = amplituda.processAudio("http://commondatastorage.googleapis.com/codeskulptor-assets/Evillaugh.ogg") + .get(); + printResult(urlResult); + }); + urlTask.start(); + /*try { + urlTask.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + }*/ + + AmplitudaResult resourceResult = amplituda.processAudio(R.raw.clap) + .get(); + printResult(resourceResult); + + } + + private void printResult(AmplitudaResult result) { + System.out.printf(Locale.US, + "Audio info:\n" + + "millis = %d\n" + + "seconds = %d\n\n" + + "source = %s\n" + + "source type = %s\n\n" + + "Amplitudes:\n" + + "size: = %d\n" + + "list: = %s\n" + + "json: = %s\n" + + "single line sequence = %s\n" + + "new line sequence = %s\n" + + "custom delimiter sequence = %s\n%n", + result.getAudioDuration(AmplitudaResult.DurationUnit.MILLIS), + result.getAudioDuration(AmplitudaResult.DurationUnit.SECONDS), + result.getInputAudioType() == InputAudio.Type.FILE ? ((File) result.getAudioSource()).getAbsolutePath() : result.getAudioSource(), + result.getInputAudioType().name(), + result.amplitudesAsList().size(), + Arrays.toString(result.amplitudesAsList().toArray()), + result.amplitudesAsJson(), + result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE), + result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE), + result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE, " * ") + ); } + } diff --git a/example/src/main/res/raw/clap.ogg b/example/src/main/res/raw/clap.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fe0a63a96745b90f53bdb4033e6f6a6b428a2280 GIT binary patch literal 43388 zcmeEubyQtHv*wVlyA=whcyTXIaY|d<-Q9~7EyZ10^k4;gaBFdQ*P_LXJLhfM z-*?w{?|N&!yVkpZz4bzNPBN2Bb|y2)B-!DWnVA}Z0Q}?Bba3qMqu(sVB_R|LS35^T z3#WSkf`7yP59m%He|~BqukK6!hr2I%kI^FRv&R*K{}(5S_?HwWm_xz9)s&m}*$d8R z?9ZRy7oh?l6I&xYV-rUq5NQM=*`MD( ~}4F48{Jox*|Brc%|0Z;+JpGJYG-G4Yn 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 QgI5-oWhh{_pa2wj03Ok>0RR91 literal 0 HcmV?d00001 From 476efa58e3c1e9e25a999aa494139a6d2a741f17 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 14 Aug 2021 23:50:00 +0300 Subject: [PATCH 2/2] Multithreading error fixed. Refactoring. Issue #16 --- .../java/linc/com/amplituda/Amplituda.java | 38 +++++++----------- .../linc/com/amplituda/AmplitudaLogger.java | 6 ++- .../amplituda/AmplitudaProcessingOutput.java | 21 +++++++++- .../linc/com/amplituda/AmplitudaResult.java | 6 ++- .../java/linc/com/amplituda/FileManager.java | 12 ++++-- .../java/linc/com/amplituda/InputAudio.java | 2 +- .../java/linc/com/example/MainActivity.java | 33 ++++----------- example/src/main/res/raw/clap.ogg | Bin 43388 -> 0 bytes 8 files changed, 59 insertions(+), 59 deletions(-) delete mode 100644 example/src/main/res/raw/clap.ogg diff --git a/app/src/main/java/linc/com/amplituda/Amplituda.java b/app/src/main/java/linc/com/amplituda/Amplituda.java index 2511dee..9f80b84 100644 --- a/app/src/main/java/linc/com/amplituda/Amplituda.java +++ b/app/src/main/java/linc/com/amplituda/Amplituda.java @@ -10,9 +10,6 @@ public final class Amplituda { - private static final String OPERATION_PROCESSING = "Processing"; - private static final String OPERATION_PREPARING = "Preparing"; - private final FileManager fileManager; public Amplituda(final Context context) { @@ -34,7 +31,10 @@ public Amplituda setLogConfig(final int priority, final boolean enable) { * Calculate amplitudes from file * @param audio - source file */ - private AmplitudaResultJNI processFileJNI(final File audio) throws AmplitudaException { + private synchronized AmplitudaResultJNI processFileJNI( + final File audio, + final InputAudio inputAudio + ) throws AmplitudaException { // Process audio if(!audio.exists()) { throw new FileNotFoundException(); @@ -50,19 +50,18 @@ private AmplitudaResultJNI processFileJNI(final File audio) throws AmplitudaExce // Process input audio AmplitudaResultJNI result = amplitudesFromAudioJNI(audio.getPath()); + // Save audio duration when file is valid and was processed + inputAudio.setDuration(fileManager.getAudioDuration(audio.getPath())); + // Log operation time - AmplitudaLogger.logOperationTime(OPERATION_PROCESSING, startTime); + AmplitudaLogger.logOperationTime(AmplitudaLogger.OPERATION_PROCESSING, startTime); return result; } public AmplitudaProcessingOutput processAudio(final File audio) { - InputAudio inputAudio = new InputAudio<>( - audio, - fileManager.getAudioDuration(audio.getPath()), - InputAudio.Type.FILE - ); + InputAudio inputAudio = new InputAudio<>(audio, InputAudio.Type.FILE); try { - return new AmplitudaProcessingOutput<>(processFileJNI(audio), inputAudio); + return new AmplitudaProcessingOutput<>(processFileJNI(audio, inputAudio), inputAudio); } catch (AmplitudaException exception) { // Handle processing error return new AmplitudaProcessingOutput<>(exception, inputAudio); @@ -80,9 +79,8 @@ public AmplitudaProcessingOutput processAudio(final String audio) { // When audio is local file - process as file if(!URLUtil.isValidUrl(audio)) { inputAudio.setType(InputAudio.Type.PATH); - inputAudio.setDuration(fileManager.getAudioDuration(audio)); return new AmplitudaProcessingOutput<>( - processFileJNI(new File(audio)), + processFileJNI(new File(audio), inputAudio), inputAudio ); } @@ -104,13 +102,10 @@ public AmplitudaProcessingOutput processAudio(final String audio) { } // Log operation time - AmplitudaLogger.logOperationTime(OPERATION_PREPARING, startTime); + AmplitudaLogger.logOperationTime(AmplitudaLogger.OPERATION_PREPARING, startTime); // Process local audio - AmplitudaResultJNI result = processFileJNI(tempAudio); - - // Save tmp file duration - inputAudio.setDuration(fileManager.getAudioDuration(tempAudio.getPath())); + AmplitudaResultJNI result = processFileJNI(tempAudio, inputAudio); // Remove tmp file fileManager.deleteFile(tempAudio); @@ -144,14 +139,11 @@ public AmplitudaProcessingOutput processAudio(final int audio) { } // Log operation time - AmplitudaLogger.logOperationTime(OPERATION_PREPARING, startTime); + AmplitudaLogger.logOperationTime(AmplitudaLogger.OPERATION_PREPARING, startTime); try { // Process local raw file - AmplitudaResultJNI result = processFileJNI(tempAudio); - - // Save tmp resource file duration - inputAudio.setDuration(fileManager.getAudioDuration(tempAudio.getPath())); + AmplitudaResultJNI result = processFileJNI(tempAudio, inputAudio); // Delete tmp fileManager.deleteFile(tempAudio); diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java b/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java index 9a3a739..3f5834c 100644 --- a/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java +++ b/app/src/main/java/linc/com/amplituda/AmplitudaLogger.java @@ -4,8 +4,10 @@ import java.util.Locale; -public final class AmplitudaLogger { +final class AmplitudaLogger { + static final String OPERATION_PROCESSING = "Processing"; + static final String OPERATION_PREPARING = "Preparing"; private static final String LIB_TAG = "AMPLITUDA"; private static int priority = Log.DEBUG; private static boolean enable = false; @@ -46,7 +48,7 @@ synchronized static void enable(final boolean logEnable) { /** * Set log message priority - * @param - android Log priority constant. + * @param logPrior - android Log priority constant. */ synchronized static void priority(final int logPrior) { priority = logPrior; diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java b/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java index 7b7a3d8..d92abe9 100644 --- a/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java +++ b/app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java @@ -9,7 +9,7 @@ import linc.com.amplituda.exceptions.processing.InvalidParameterFlagException; import linc.com.amplituda.exceptions.processing.SampleOutOfBoundsException; -public class AmplitudaProcessingOutput { +public final class AmplitudaProcessingOutput { private final AmplitudaResult amplitudaResult; private LinkedHashSet processingErrors = new LinkedHashSet<>(); @@ -93,7 +93,11 @@ public AmplitudaProcessingOutput compress(final int preferredSamplesPerSecond return this; } - + /** + * Get Amplituda processing result. This function returns result in callback + * @param successListener - success processing operation callback + * @param errorListener - processing error callback + */ public void get( final AmplitudaSuccessListener successListener, final AmplitudaErrorListener errorListener @@ -102,15 +106,28 @@ public void get( successListener.onSuccess(amplitudaResult); } + /** + * Get Amplituda processing result. This function returns result in callback + * @param successListener - success processing operation callback + */ public void get(final AmplitudaSuccessListener successListener) { get(successListener, null); } + /** + * Get Amplituda processing result + * @param errorListener - processing error callback + * @return AmplitudaResult object + */ public AmplitudaResult get(final AmplitudaErrorListener errorListener) { handleAmplitudaProcessingErrors(errorListener); return amplitudaResult; } + /** + * Get Amplituda processing result + * @return AmplitudaResult object + */ public AmplitudaResult get() { return amplitudaResult; } diff --git a/app/src/main/java/linc/com/amplituda/AmplitudaResult.java b/app/src/main/java/linc/com/amplituda/AmplitudaResult.java index 2fc005d..226367a 100644 --- a/app/src/main/java/linc/com/amplituda/AmplitudaResult.java +++ b/app/src/main/java/linc/com/amplituda/AmplitudaResult.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; -public class AmplitudaResult { +public final class AmplitudaResult { private String amplitudes; private final InputAudio inputAudio; @@ -153,6 +153,10 @@ private String amplitudesToSingleLineSequence(final String amplitudes, final Str return TextUtils.join(delimiter, log); } + /** + * Update amplitudes data + * Call only from internal compress() + */ void setAmplitudes(final String amplitudes) { this.amplitudes = amplitudes; } diff --git a/app/src/main/java/linc/com/amplituda/FileManager.java b/app/src/main/java/linc/com/amplituda/FileManager.java index 3bcd775..ee57791 100644 --- a/app/src/main/java/linc/com/amplituda/FileManager.java +++ b/app/src/main/java/linc/com/amplituda/FileManager.java @@ -19,8 +19,8 @@ final class FileManager { static final String RAW_TEMP = "amplituda_tmp_raw"; - private Resources resources; - private String cache; + private final Resources resources; + private final String cache; public FileManager(final Context context) { resources = context.getResources(); @@ -68,8 +68,12 @@ synchronized long getAudioDuration(final String path) { */ synchronized File getRawFile(final int resource) { File temp = new File(cache, RAW_TEMP); - streamToFile(resources.openRawResource(resource), temp, 1024 * 4); - return guessAudioExtension(temp); + try { + streamToFile(resources.openRawResource(resource), temp, 1024 * 4); + return guessAudioExtension(temp); + } catch (Resources.NotFoundException ignored) { + return null; + } } /** diff --git a/app/src/main/java/linc/com/amplituda/InputAudio.java b/app/src/main/java/linc/com/amplituda/InputAudio.java index b00d149..01d889f 100644 --- a/app/src/main/java/linc/com/amplituda/InputAudio.java +++ b/app/src/main/java/linc/com/amplituda/InputAudio.java @@ -1,6 +1,6 @@ package linc.com.amplituda; -public class InputAudio { +public final class InputAudio { private final T source; private long duration; private InputAudio.Type type; diff --git a/example/src/main/java/linc/com/example/MainActivity.java b/example/src/main/java/linc/com/example/MainActivity.java index 52e2283..5094958 100644 --- a/example/src/main/java/linc/com/example/MainActivity.java +++ b/example/src/main/java/linc/com/example/MainActivity.java @@ -2,10 +2,13 @@ import androidx.appcompat.app.AppCompatActivity; +import android.hardware.camera2.CameraManager; import android.os.Bundle; import android.util.Log; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.Arrays; import java.util.Locale; @@ -13,6 +16,7 @@ import linc.com.amplituda.AmplitudaProcessingOutput; import linc.com.amplituda.AmplitudaResult; import linc.com.amplituda.InputAudio; +import linc.com.amplituda.exceptions.AmplitudaException; public class MainActivity extends AppCompatActivity { @@ -22,32 +26,9 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); Amplituda amplituda = new Amplituda(this); - amplituda.setLogConfig(Log.DEBUG, true); - AmplitudaResult localPathResult = amplituda.processAudio("/storage/emulated/0/Music/kygo.mp3") - .get(); - printResult(localPathResult); - - AmplitudaResult localFileResult = amplituda.processAudio(new File("/storage/emulated/0/Music/kygo.mp3")) - .get(); - printResult(localFileResult); - - - Thread urlTask = new Thread(() -> { - AmplitudaResult urlResult = amplituda.processAudio("http://commondatastorage.googleapis.com/codeskulptor-assets/Evillaugh.ogg") - .get(); - printResult(urlResult); - }); - urlTask.start(); - /*try { - urlTask.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - }*/ - - AmplitudaResult resourceResult = amplituda.processAudio(R.raw.clap) - .get(); - printResult(resourceResult); + amplituda.processAudio("/storage/emulated/0/Music/Linc - Amplituda.mp3") + .get(this::printResult, Throwable::printStackTrace); } @@ -73,7 +54,7 @@ private void printResult(AmplitudaResult result) { Arrays.toString(result.amplitudesAsList().toArray()), result.amplitudesAsJson(), result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE), - result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE), + result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.NEW_LINE), result.amplitudesAsSequence(AmplitudaResult.SequenceFormat.SINGLE_LINE, " * ") ); } diff --git a/example/src/main/res/raw/clap.ogg b/example/src/main/res/raw/clap.ogg deleted file mode 100644 index fe0a63a96745b90f53bdb4033e6f6a6b428a2280..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43388 zcmeEubyQtHv*wVlyA=whcyTXIaY|d<-Q9~7EyZ10^k4;gaBFdQ*P_LXJLhfM z-*?w{?|N&!yVkpZz4bzNPBN2Bb|y2)B-!DWnVA}Z0Q}?Bba3qMqu(sVB_R|LS35^T z3#WSkf`7yP59m%He|~BqukK6!hr2I%kI^FRv&R*K{}(5S_?HwWm_xz9)s&m}*$d8R z?9ZRy7oh?l6I&xYV-rUq5NQM=*`MD( ~}4F48{Jox*|Brc%|0Z;+JpGJYG-G4Yn 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 QgI5-oWhh{_pa2wj03Ok>0RR91