-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from lincollincol/dev
Handle result with AmplitudaResult. Simplified version of function calls
- Loading branch information
Showing
13 changed files
with
540 additions
and
501 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 160 additions & 0 deletions
160
app/src/main/java/linc/com/amplituda/AmplitudaProcessingOutput.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
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 final class AmplitudaProcessingOutput<T> { | ||
|
||
private final AmplitudaResult<T> amplitudaResult; | ||
private LinkedHashSet<AmplitudaException> processingErrors = new LinkedHashSet<>(); | ||
|
||
private AmplitudaProcessingOutput( | ||
final String amplitudes, | ||
final InputAudio<T> inputAudio | ||
) { | ||
amplitudaResult = new AmplitudaResult<>(amplitudes, inputAudio); | ||
} | ||
|
||
AmplitudaProcessingOutput( | ||
final AmplitudaResultJNI processingData, | ||
final InputAudio<T> inputAudio | ||
) { | ||
this( | ||
processingData.getAmplitudes(), | ||
inputAudio | ||
); | ||
this.processingErrors.addAll(processingData.getErrors()); | ||
} | ||
|
||
AmplitudaProcessingOutput(final AmplitudaException exception, final InputAudio<T> 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<T> compress(final int preferredSamplesPerSecond) { | ||
List<Integer> 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; | ||
} | ||
|
||
/** | ||
* 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<T> successListener, | ||
final AmplitudaErrorListener errorListener | ||
) { | ||
handleAmplitudaProcessingErrors(errorListener); | ||
successListener.onSuccess(amplitudaResult); | ||
} | ||
|
||
/** | ||
* Get Amplituda processing result. This function returns result in callback | ||
* @param successListener - success processing operation callback | ||
*/ | ||
public void get(final AmplitudaSuccessListener<T> successListener) { | ||
get(successListener, null); | ||
} | ||
|
||
/** | ||
* Get Amplituda processing result | ||
* @param errorListener - processing error callback | ||
* @return AmplitudaResult object | ||
*/ | ||
public AmplitudaResult<T> get(final AmplitudaErrorListener errorListener) { | ||
handleAmplitudaProcessingErrors(errorListener); | ||
return amplitudaResult; | ||
} | ||
|
||
/** | ||
* Get Amplituda processing result | ||
* @return AmplitudaResult object | ||
*/ | ||
public AmplitudaResult<T> 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); | ||
} | ||
|
||
} |
172 changes: 172 additions & 0 deletions
172
app/src/main/java/linc/com/amplituda/AmplitudaResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
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 final class AmplitudaResult<T> { | ||
|
||
private String amplitudes; | ||
private final InputAudio<T> inputAudio; | ||
|
||
AmplitudaResult( | ||
final String amplitudes, | ||
final InputAudio<T> 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<Integer> amplitudesAsList() { | ||
if(amplitudes == null || amplitudes.isEmpty()) | ||
return Collections.emptyList(); | ||
|
||
String[] log = amplitudes.split("\n"); | ||
List<Integer> 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<Integer> amplitudesForSecond(final int second) { | ||
List<Integer> 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<Second, Amplitudes> | ||
Map<Integer, List<Integer>> amplitudes = new LinkedHashMap<>(); | ||
|
||
// Temporary amplitudes list | ||
List<Integer> 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); | ||
} | ||
|
||
/** | ||
* Update amplitudes data | ||
* Call only from internal compress() | ||
*/ | ||
void setAmplitudes(final String amplitudes) { | ||
this.amplitudes = amplitudes; | ||
} | ||
|
||
public enum DurationUnit { | ||
SECONDS, MILLIS | ||
} | ||
|
||
public enum SequenceFormat { | ||
SINGLE_LINE, NEW_LINE | ||
} | ||
|
||
} |
Oops, something went wrong.