Visualizer implementation for Android.
This commit is contained in:
parent
602dc44029
commit
1fecd5ac1f
|
@ -1,8 +1,14 @@
|
||||||
package com.ryanheise.just_audio;
|
package com.ryanheise.just_audio;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.audiofx.Visualizer;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
@ -35,13 +41,13 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import io.flutter.Log;
|
import io.flutter.Log;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
import io.flutter.plugin.common.EventChannel;
|
|
||||||
import io.flutter.plugin.common.EventChannel.EventSink;
|
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
import io.flutter.plugin.common.MethodChannel.Result;
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -50,7 +56,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class AudioPlayer implements MethodCallHandler, Player.EventListener, AudioListener, MetadataOutput {
|
public class AudioPlayer implements MethodCallHandler, Player.EventListener, AudioListener, MetadataOutput, PluginRegistry.RequestPermissionsResultListener {
|
||||||
|
|
||||||
static final String TAG = "AudioPlayer";
|
static final String TAG = "AudioPlayer";
|
||||||
|
|
||||||
|
@ -58,8 +64,8 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final MethodChannel methodChannel;
|
private final MethodChannel methodChannel;
|
||||||
private final EventChannel eventChannel;
|
private final BetterEventChannel eventChannel;
|
||||||
private EventSink eventSink;
|
private ActivityPluginBinding activityPluginBinding;
|
||||||
|
|
||||||
private ProcessingState processingState;
|
private ProcessingState processingState;
|
||||||
private long bufferedPosition;
|
private long bufferedPosition;
|
||||||
|
@ -77,6 +83,12 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
private IcyHeaders icyHeaders;
|
private IcyHeaders icyHeaders;
|
||||||
private int errorCount;
|
private int errorCount;
|
||||||
private AudioAttributes pendingAudioAttributes;
|
private AudioAttributes pendingAudioAttributes;
|
||||||
|
private BetterVisualizer visualizer;
|
||||||
|
private Result startVisualizerResult;
|
||||||
|
private boolean enableWaveform;
|
||||||
|
private boolean enableFft;
|
||||||
|
private Integer visualizerCaptureRate;
|
||||||
|
private Integer visualizerCaptureSize;
|
||||||
|
|
||||||
private SimpleExoPlayer player;
|
private SimpleExoPlayer player;
|
||||||
private Integer audioSessionId;
|
private Integer audioSessionId;
|
||||||
|
@ -114,26 +126,50 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
this.context = applicationContext;
|
this.context = applicationContext;
|
||||||
methodChannel = new MethodChannel(messenger, "com.ryanheise.just_audio.methods." + id);
|
methodChannel = new MethodChannel(messenger, "com.ryanheise.just_audio.methods." + id);
|
||||||
methodChannel.setMethodCallHandler(this);
|
methodChannel.setMethodCallHandler(this);
|
||||||
eventChannel = new EventChannel(messenger, "com.ryanheise.just_audio.events." + id);
|
eventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.events." + id);
|
||||||
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
|
visualizer = new BetterVisualizer(messenger, id);
|
||||||
@Override
|
|
||||||
public void onListen(final Object arguments, final EventSink eventSink) {
|
|
||||||
AudioPlayer.this.eventSink = eventSink;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancel(final Object arguments) {
|
|
||||||
eventSink = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
processingState = ProcessingState.none;
|
processingState = ProcessingState.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
ActivityCompat.requestPermissions(activityPluginBinding.getActivity(), new String[] { Manifest.permission.RECORD_AUDIO }, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivityPluginBinding(ActivityPluginBinding activityPluginBinding) {
|
||||||
|
if (this.activityPluginBinding != null && this.activityPluginBinding != activityPluginBinding) {
|
||||||
|
this.activityPluginBinding.removeRequestPermissionsResultListener(this);
|
||||||
|
}
|
||||||
|
this.activityPluginBinding = activityPluginBinding;
|
||||||
|
if (activityPluginBinding != null) {
|
||||||
|
activityPluginBinding.addRequestPermissionsResultListener(this);
|
||||||
|
// If there is a pending startVisualizer request
|
||||||
|
if (startVisualizerResult != null) {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void startWatchingBuffer() {
|
private void startWatchingBuffer() {
|
||||||
handler.removeCallbacks(bufferWatcher);
|
handler.removeCallbacks(bufferWatcher);
|
||||||
handler.post(bufferWatcher);
|
handler.post(bufferWatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
for (int i = 0; i < permissions.length; i++) {
|
||||||
|
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) {
|
||||||
|
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
visualizer.setHasPermission(true);
|
||||||
|
completeStartVisualizer(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
completeStartVisualizer(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAudioSessionId(int audioSessionId) {
|
public void onAudioSessionId(int audioSessionId) {
|
||||||
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
||||||
|
@ -141,6 +177,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
} else {
|
} else {
|
||||||
this.audioSessionId = audioSessionId;
|
this.audioSessionId = audioSessionId;
|
||||||
}
|
}
|
||||||
|
visualizer.onAudioSessionId(this.audioSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -277,12 +314,48 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
seekResult = null;
|
seekResult = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void completeStartVisualizer(boolean success) {
|
||||||
|
if (startVisualizerResult == null) return;
|
||||||
|
if (success) {
|
||||||
|
visualizer.start(visualizerCaptureRate, visualizerCaptureSize, enableWaveform, enableFft);
|
||||||
|
Map<String, Object> resultMap = new HashMap<String, Object>();
|
||||||
|
resultMap.put("samplingRate", visualizer.getSamplingRate());
|
||||||
|
startVisualizerResult.success(resultMap);
|
||||||
|
} else {
|
||||||
|
startVisualizerResult.error("RECORD_AUDIO permission denied", null, null);
|
||||||
|
}
|
||||||
|
startVisualizerResult = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(final MethodCall call, final Result result) {
|
public void onMethodCall(final MethodCall call, final Result result) {
|
||||||
ensurePlayerInitialized();
|
ensurePlayerInitialized();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
|
case "startVisualizer":
|
||||||
|
Boolean enableWaveform = call.argument("enableWaveform");
|
||||||
|
Boolean enableFft = call.argument("enableFft");
|
||||||
|
Integer captureRate = call.argument("captureRate");
|
||||||
|
Integer captureSize = call.argument("captureSize");
|
||||||
|
this.enableWaveform = enableWaveform;
|
||||||
|
this.enableFft = enableFft;
|
||||||
|
visualizerCaptureRate = captureRate;
|
||||||
|
visualizerCaptureSize = captureSize;
|
||||||
|
startVisualizerResult = result;
|
||||||
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
visualizer.setHasPermission(true);
|
||||||
|
completeStartVisualizer(true);
|
||||||
|
} else if (activityPluginBinding != null && activityPluginBinding.getActivity() != null) {
|
||||||
|
requestPermissions();
|
||||||
|
} else {
|
||||||
|
// Will request permission in setActivityPluginBinding
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "stopVisualizer":
|
||||||
|
visualizer.stop();
|
||||||
|
result.success(new HashMap<String, Object>());
|
||||||
|
break;
|
||||||
case "load":
|
case "load":
|
||||||
Long initialPosition = getLong(call.argument("initialPosition"));
|
Long initialPosition = getLong(call.argument("initialPosition"));
|
||||||
Integer initialIndex = call.argument("initialIndex");
|
Integer initialIndex = call.argument("initialIndex");
|
||||||
|
@ -565,9 +638,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
event.put("currentIndex", currentIndex);
|
event.put("currentIndex", currentIndex);
|
||||||
event.put("androidAudioSessionId", audioSessionId);
|
event.put("androidAudioSessionId", audioSessionId);
|
||||||
|
|
||||||
if (eventSink != null) {
|
eventChannel.success(event);
|
||||||
eventSink.success(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> collectIcyMetadata() {
|
private Map<String, Object> collectIcyMetadata() {
|
||||||
|
@ -615,9 +686,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
prepareResult = null;
|
prepareResult = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventSink != null) {
|
eventChannel.error(errorCode, errorMsg, null);
|
||||||
eventSink.error(errorCode, errorMsg, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transition(final ProcessingState newState) {
|
private void transition(final ProcessingState newState) {
|
||||||
|
@ -706,9 +775,8 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
player = null;
|
player = null;
|
||||||
transition(ProcessingState.none);
|
transition(ProcessingState.none);
|
||||||
}
|
}
|
||||||
if (eventSink != null) {
|
eventChannel.endOfStream();
|
||||||
eventSink.endOfStream();
|
visualizer.dispose();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void abortSeek() {
|
private void abortSeek() {
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.ryanheise.just_audio;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
|
import io.flutter.plugin.common.EventChannel;
|
||||||
|
import io.flutter.plugin.common.EventChannel.EventSink;
|
||||||
|
|
||||||
|
public class BetterEventChannel implements EventSink {
|
||||||
|
private EventSink eventSink;
|
||||||
|
|
||||||
|
public BetterEventChannel(final BinaryMessenger messenger, final String id) {
|
||||||
|
EventChannel eventChannel = new EventChannel(messenger, id);
|
||||||
|
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
|
||||||
|
@Override
|
||||||
|
public void onListen(final Object arguments, final EventSink eventSink) {
|
||||||
|
BetterEventChannel.this.eventSink = eventSink;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel(final Object arguments) {
|
||||||
|
eventSink = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void success(Object event) {
|
||||||
|
if (eventSink != null) eventSink.success(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String errorCode, String errorMessage, Object errorDetails) {
|
||||||
|
if (eventSink != null) eventSink.error(errorCode, errorMessage, errorDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endOfStream() {
|
||||||
|
if (eventSink != null) eventSink.endOfStream();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.ryanheise.just_audio;
|
||||||
|
|
||||||
|
import android.media.audiofx.Visualizer;
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
|
|
||||||
|
public class BetterVisualizer {
|
||||||
|
private Visualizer visualizer;
|
||||||
|
private final BetterEventChannel waveformEventChannel;
|
||||||
|
private final BetterEventChannel fftEventChannel;
|
||||||
|
private Integer audioSessionId;
|
||||||
|
private int captureRate;
|
||||||
|
private int captureSize;
|
||||||
|
private boolean enableWaveform;
|
||||||
|
private boolean enableFft;
|
||||||
|
private boolean pendingStartRequest;
|
||||||
|
private boolean hasPermission;
|
||||||
|
|
||||||
|
public BetterVisualizer(final BinaryMessenger messenger, String id) {
|
||||||
|
waveformEventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.waveform_events." + id);
|
||||||
|
fftEventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.fft_events." + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSamplingRate() {
|
||||||
|
return visualizer.getSamplingRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPermission(boolean hasPermission) {
|
||||||
|
this.hasPermission = hasPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAudioSessionId(Integer audioSessionId) {
|
||||||
|
this.audioSessionId = audioSessionId;
|
||||||
|
if (audioSessionId != null && hasPermission && pendingStartRequest) {
|
||||||
|
start(captureRate, captureSize, enableWaveform, enableFft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(Integer captureRate, Integer captureSize, final boolean enableWavefrom, final boolean enableFft) {
|
||||||
|
if (visualizer != null) return;
|
||||||
|
if (captureRate == null) {
|
||||||
|
captureRate = Visualizer.getMaxCaptureRate() / 2;
|
||||||
|
} else if (captureRate > Visualizer.getMaxCaptureRate()) {
|
||||||
|
captureRate = Visualizer.getMaxCaptureRate();
|
||||||
|
}
|
||||||
|
if (captureSize == null) {
|
||||||
|
captureSize = Visualizer.getCaptureSizeRange()[1];
|
||||||
|
} else if (captureSize > Visualizer.getCaptureSizeRange()[1]) {
|
||||||
|
captureSize = Visualizer.getCaptureSizeRange()[1];
|
||||||
|
} else if (captureSize < Visualizer.getCaptureSizeRange()[0]) {
|
||||||
|
captureSize = Visualizer.getCaptureSizeRange()[0];
|
||||||
|
}
|
||||||
|
this.enableWaveform = enableWaveform;
|
||||||
|
this.enableFft = enableFft;
|
||||||
|
this.captureRate = captureRate;
|
||||||
|
if (audioSessionId == null || !hasPermission) {
|
||||||
|
pendingStartRequest = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingStartRequest = false;
|
||||||
|
visualizer = new Visualizer(audioSessionId);
|
||||||
|
visualizer.setCaptureSize(captureSize);
|
||||||
|
visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
|
||||||
|
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
|
||||||
|
waveformEventChannel.success(waveform);
|
||||||
|
}
|
||||||
|
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
|
||||||
|
fftEventChannel.success(fft);
|
||||||
|
}
|
||||||
|
}, captureRate, enableWavefrom, enableFft);
|
||||||
|
visualizer.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (visualizer == null) return;
|
||||||
|
visualizer.setDataCaptureListener(null, captureRate, enableWaveform, enableFft);
|
||||||
|
visualizer.setEnabled(false);
|
||||||
|
visualizer.release();
|
||||||
|
visualizer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
stop();
|
||||||
|
waveformEventChannel.endOfStream();
|
||||||
|
fftEventChannel.endOfStream();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package com.ryanheise.just_audio;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||||
|
@ -10,7 +12,7 @@ import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||||
/**
|
/**
|
||||||
* JustAudioPlugin
|
* JustAudioPlugin
|
||||||
*/
|
*/
|
||||||
public class JustAudioPlugin implements FlutterPlugin {
|
public class JustAudioPlugin implements FlutterPlugin, ActivityAware {
|
||||||
|
|
||||||
private MethodChannel channel;
|
private MethodChannel channel;
|
||||||
private MainMethodCallHandler methodCallHandler;
|
private MainMethodCallHandler methodCallHandler;
|
||||||
|
@ -41,6 +43,25 @@ public class JustAudioPlugin implements FlutterPlugin {
|
||||||
stopListening();
|
stopListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToActivity(ActivityPluginBinding binding) {
|
||||||
|
methodCallHandler.setActivityPluginBinding(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivityForConfigChanges() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
|
||||||
|
methodCallHandler.setActivityPluginBinding(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivity() {
|
||||||
|
methodCallHandler.setActivityPluginBinding(null);
|
||||||
|
}
|
||||||
|
|
||||||
private void startListening(Context applicationContext, BinaryMessenger messenger) {
|
private void startListening(Context applicationContext, BinaryMessenger messenger) {
|
||||||
methodCallHandler = new MainMethodCallHandler(applicationContext, messenger);
|
methodCallHandler = new MainMethodCallHandler(applicationContext, messenger);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.ryanheise.just_audio;
|
package com.ryanheise.just_audio;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
|
@ -15,6 +17,7 @@ public class MainMethodCallHandler implements MethodCallHandler {
|
||||||
|
|
||||||
private final Context applicationContext;
|
private final Context applicationContext;
|
||||||
private final BinaryMessenger messenger;
|
private final BinaryMessenger messenger;
|
||||||
|
private ActivityPluginBinding activityPluginBinding;
|
||||||
|
|
||||||
private final Map<String, AudioPlayer> players = new HashMap<>();
|
private final Map<String, AudioPlayer> players = new HashMap<>();
|
||||||
|
|
||||||
|
@ -24,6 +27,13 @@ public class MainMethodCallHandler implements MethodCallHandler {
|
||||||
this.messenger = messenger;
|
this.messenger = messenger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setActivityPluginBinding(ActivityPluginBinding activityPluginBinding) {
|
||||||
|
this.activityPluginBinding = activityPluginBinding;
|
||||||
|
for (AudioPlayer player : players.values()) {
|
||||||
|
player.setActivityPluginBinding(activityPluginBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(MethodCall call, @NonNull Result result) {
|
public void onMethodCall(MethodCall call, @NonNull Result result) {
|
||||||
final Map<?, ?> request = call.arguments();
|
final Map<?, ?> request = call.arguments();
|
||||||
|
@ -34,7 +44,11 @@ public class MainMethodCallHandler implements MethodCallHandler {
|
||||||
result.error("Platform player " + id + " already exists", null, null);
|
result.error("Platform player " + id + " already exists", null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
players.put(id, new AudioPlayer(applicationContext, messenger, id));
|
final AudioPlayer player = new AudioPlayer(applicationContext, messenger, id);
|
||||||
|
players.put(id, player);
|
||||||
|
if (activityPluginBinding != null) {
|
||||||
|
player.setActivityPluginBinding(activityPluginBinding);
|
||||||
|
}
|
||||||
result.success(null);
|
result.success(null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.ryanheise.just_audio_example">
|
package="com.ryanheise.just_audio_example">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
|
|
||||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||||
In most cases you can leave this as-is, but you if you want to provide
|
In most cases you can leave this as-is, but you if you want to provide
|
||||||
|
|
|
@ -124,16 +124,16 @@ packages:
|
||||||
just_audio_platform_interface:
|
just_audio_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: just_audio_platform_interface
|
path: "../../just_audio_platform_interface"
|
||||||
url: "https://pub.dartlang.org"
|
relative: true
|
||||||
source: hosted
|
source: path
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
just_audio_web:
|
just_audio_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: just_audio_web
|
path: "../../just_audio_web"
|
||||||
url: "https://pub.dartlang.org"
|
relative: true
|
||||||
source: hosted
|
source: path
|
||||||
version: "0.2.1"
|
version: "0.2.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
|
@ -59,15 +60,24 @@ class AudioPlayer {
|
||||||
/// subscribe to the new platform's events.
|
/// subscribe to the new platform's events.
|
||||||
StreamSubscription _playbackEventSubscription;
|
StreamSubscription _playbackEventSubscription;
|
||||||
|
|
||||||
|
/// The subscription to the event channel for waveform data.
|
||||||
|
StreamSubscription _visualizerWaveformSubscription;
|
||||||
|
|
||||||
|
/// The subscription to the event channel for FFT data.
|
||||||
|
StreamSubscription _visualizerFftSubscription;
|
||||||
|
|
||||||
final String _id;
|
final String _id;
|
||||||
_ProxyHttpServer _proxy;
|
_ProxyHttpServer _proxy;
|
||||||
AudioSource _audioSource;
|
AudioSource _audioSource;
|
||||||
Map<String, AudioSource> _audioSources = {};
|
Map<String, AudioSource> _audioSources = {};
|
||||||
bool _disposed = false;
|
bool _disposed = false;
|
||||||
_InitialSeekValues _initialSeekValues;
|
_InitialSeekValues _initialSeekValues;
|
||||||
|
StartVisualizerRequest _startVisualizerRequest;
|
||||||
|
|
||||||
PlaybackEvent _playbackEvent;
|
PlaybackEvent _playbackEvent;
|
||||||
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
|
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
|
||||||
|
final _visualizerWaveformSubject = BehaviorSubject<Uint8List>();
|
||||||
|
final _visualizerFftSubject = BehaviorSubject<Uint8List>();
|
||||||
Future<Duration> _durationFuture;
|
Future<Duration> _durationFuture;
|
||||||
final _durationSubject = BehaviorSubject<Duration>();
|
final _durationSubject = BehaviorSubject<Duration>();
|
||||||
final _processingStateSubject = BehaviorSubject<ProcessingState>();
|
final _processingStateSubject = BehaviorSubject<ProcessingState>();
|
||||||
|
@ -227,6 +237,13 @@ class AudioPlayer {
|
||||||
/// A stream of [PlaybackEvent]s.
|
/// A stream of [PlaybackEvent]s.
|
||||||
Stream<PlaybackEvent> get playbackEventStream => _playbackEventSubject.stream;
|
Stream<PlaybackEvent> get playbackEventStream => _playbackEventSubject.stream;
|
||||||
|
|
||||||
|
/// A stream of visualizer waveform data in unsigned 8 bit PCM..
|
||||||
|
Stream<Uint8List> get visualizerWaveformStream =>
|
||||||
|
_visualizerWaveformSubject.stream;
|
||||||
|
|
||||||
|
/// A stream of visualizer FFT data.
|
||||||
|
Stream<Uint8List> get visualizerFftStream => _visualizerFftSubject.stream;
|
||||||
|
|
||||||
/// The duration of the current audio or null if unknown.
|
/// The duration of the current audio or null if unknown.
|
||||||
Duration get duration => _playbackEvent.duration;
|
Duration get duration => _playbackEvent.duration;
|
||||||
|
|
||||||
|
@ -894,11 +911,42 @@ class AudioPlayer {
|
||||||
usage: audioAttributes.usage.value));
|
usage: audioAttributes.usage.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the visualizer by capturing [captureSize] samples of audio at
|
||||||
|
/// [captureRate] millihertz and return the sampling rate of the audio. If
|
||||||
|
/// [enableWaveform] is `true`, the captured samples will be broadcast via
|
||||||
|
/// [visualizerWaveformStream]. If [enableFft] is `true`, the FFT data for
|
||||||
|
/// each capture will be broadcast via [visualizerFftStream]. You should call
|
||||||
|
/// [stopVisualizer] to stop capturing audio data.
|
||||||
|
Future<int> startVisualizer({
|
||||||
|
bool enableWaveform = true,
|
||||||
|
bool enableFft = true,
|
||||||
|
int captureRate,
|
||||||
|
int captureSize,
|
||||||
|
}) async {
|
||||||
|
return (await (await _platform).startVisualizer(_startVisualizerRequest =
|
||||||
|
StartVisualizerRequest(
|
||||||
|
enableWaveform: enableWaveform,
|
||||||
|
enableFft: enableFft,
|
||||||
|
captureRate: captureRate,
|
||||||
|
captureSize: captureSize)))
|
||||||
|
?.samplingRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop capturing audio data for the visualizer.
|
||||||
|
Future<void> stopVisualizer() async {
|
||||||
|
_startVisualizerRequest = null;
|
||||||
|
(await _platform).stopVisualizer(StopVisualizerRequest());
|
||||||
|
}
|
||||||
|
|
||||||
/// Release all resources associated with this player. You must invoke this
|
/// Release all resources associated with this player. You must invoke this
|
||||||
/// after you are done with the player.
|
/// after you are done with the player.
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
await _visualizerWaveformSubscription?.cancel();
|
||||||
|
await _visualizerFftSubscription?.cancel();
|
||||||
|
await _visualizerWaveformSubject.close();
|
||||||
|
await _visualizerFftSubject.close();
|
||||||
if (_nativePlatform != null) {
|
if (_nativePlatform != null) {
|
||||||
await _disposePlatform(await _nativePlatform);
|
await _disposePlatform(await _nativePlatform);
|
||||||
_nativePlatform = null;
|
_nativePlatform = null;
|
||||||
|
@ -940,6 +988,8 @@ class AudioPlayer {
|
||||||
final durationCompleter = Completer<Duration>();
|
final durationCompleter = Completer<Duration>();
|
||||||
_platform = Future<AudioPlayerPlatform>(() async {
|
_platform = Future<AudioPlayerPlatform>(() async {
|
||||||
_playbackEventSubscription?.cancel();
|
_playbackEventSubscription?.cancel();
|
||||||
|
_visualizerWaveformSubscription?.cancel();
|
||||||
|
_visualizerFftSubscription?.cancel();
|
||||||
if (oldPlatformFuture != null) {
|
if (oldPlatformFuture != null) {
|
||||||
final oldPlatform = await oldPlatformFuture;
|
final oldPlatform = await oldPlatformFuture;
|
||||||
if (oldPlatform != _idlePlatform) {
|
if (oldPlatform != _idlePlatform) {
|
||||||
|
@ -953,6 +1003,10 @@ class AudioPlayer {
|
||||||
? await (_nativePlatform =
|
? await (_nativePlatform =
|
||||||
JustAudioPlatform.instance.init(InitRequest(id: _id)))
|
JustAudioPlatform.instance.init(InitRequest(id: _id)))
|
||||||
: _idlePlatform;
|
: _idlePlatform;
|
||||||
|
_visualizerWaveformSubscription = platform.visualizerWaveformStream
|
||||||
|
.listen(_visualizerWaveformSubject.add);
|
||||||
|
_visualizerFftSubscription =
|
||||||
|
platform.visualizerFftStream.listen(_visualizerFftSubject.add);
|
||||||
_playbackEventSubscription =
|
_playbackEventSubscription =
|
||||||
platform.playbackEventMessageStream.listen((message) {
|
platform.playbackEventMessageStream.listen((message) {
|
||||||
var duration = message.duration;
|
var duration = message.duration;
|
||||||
|
@ -1005,6 +1059,9 @@ class AudioPlayer {
|
||||||
SetAutomaticallyWaitsToMinimizeStallingRequest(
|
SetAutomaticallyWaitsToMinimizeStallingRequest(
|
||||||
enabled: automaticallyWaitsToMinimizeStalling));
|
enabled: automaticallyWaitsToMinimizeStalling));
|
||||||
}
|
}
|
||||||
|
if (_startVisualizerRequest != null) {
|
||||||
|
await platform.startVisualizer(_startVisualizerRequest);
|
||||||
|
}
|
||||||
await platform.setVolume(SetVolumeRequest(volume: volume));
|
await platform.setVolume(SetVolumeRequest(volume: volume));
|
||||||
await platform.setSpeed(SetSpeedRequest(speed: speed));
|
await platform.setSpeed(SetSpeedRequest(speed: speed));
|
||||||
await platform.setLoopMode(SetLoopModeRequest(
|
await platform.setLoopMode(SetLoopModeRequest(
|
||||||
|
@ -2273,6 +2330,8 @@ enum LoopMode { off, one, all }
|
||||||
/// state and the native platform is deallocated.
|
/// state and the native platform is deallocated.
|
||||||
class _IdleAudioPlayer extends AudioPlayerPlatform {
|
class _IdleAudioPlayer extends AudioPlayerPlatform {
|
||||||
final _eventSubject = BehaviorSubject<PlaybackEventMessage>();
|
final _eventSubject = BehaviorSubject<PlaybackEventMessage>();
|
||||||
|
final _visualizerWaveformSubject = BehaviorSubject<Uint8List>();
|
||||||
|
final _visualizerFftSubject = BehaviorSubject<Uint8List>();
|
||||||
Duration _position;
|
Duration _position;
|
||||||
int _index;
|
int _index;
|
||||||
List<IndexedAudioSource> _sequence;
|
List<IndexedAudioSource> _sequence;
|
||||||
|
@ -2310,6 +2369,13 @@ class _IdleAudioPlayer extends AudioPlayerPlatform {
|
||||||
Stream<PlaybackEventMessage> get playbackEventMessageStream =>
|
Stream<PlaybackEventMessage> get playbackEventMessageStream =>
|
||||||
_eventSubject.stream;
|
_eventSubject.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Uint8List> get visualizerWaveformStream =>
|
||||||
|
_visualizerWaveformSubject.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Uint8List> get visualizerFftStream => _visualizerFftSubject.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LoadResponse> load(LoadRequest request) async {
|
Future<LoadResponse> load(LoadRequest request) async {
|
||||||
_index = request.initialIndex ?? 0;
|
_index = request.initialIndex ?? 0;
|
||||||
|
@ -2379,6 +2445,9 @@ class _IdleAudioPlayer extends AudioPlayerPlatform {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DisposeResponse> dispose(DisposeRequest request) async {
|
Future<DisposeResponse> dispose(DisposeRequest request) async {
|
||||||
|
await _eventSubject.close();
|
||||||
|
await _visualizerWaveformSubject.close();
|
||||||
|
await _visualizerFftSubject.close();
|
||||||
return DisposeResponse();
|
return DisposeResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2399,6 +2468,18 @@ class _IdleAudioPlayer extends AudioPlayerPlatform {
|
||||||
ConcatenatingMoveRequest request) async {
|
ConcatenatingMoveRequest request) async {
|
||||||
return ConcatenatingMoveResponse();
|
return ConcatenatingMoveResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StartVisualizerResponse> startVisualizer(
|
||||||
|
StartVisualizerRequest request) async {
|
||||||
|
return StartVisualizerResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StopVisualizerResponse> stopVisualizer(
|
||||||
|
StopVisualizerRequest request) async {
|
||||||
|
return StopVisualizerResponse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds the initial requested position and index for a newly loaded audio
|
/// Holds the initial requested position and index for a newly loaded audio
|
||||||
|
|
|
@ -194,16 +194,16 @@ packages:
|
||||||
just_audio_platform_interface:
|
just_audio_platform_interface:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: just_audio_platform_interface
|
path: "../just_audio_platform_interface"
|
||||||
url: "https://pub.dartlang.org"
|
relative: true
|
||||||
source: hosted
|
source: path
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
just_audio_web:
|
just_audio_web:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: just_audio_web
|
path: "../just_audio_web"
|
||||||
url: "https://pub.dartlang.org"
|
relative: true
|
||||||
source: hosted
|
source: path
|
||||||
version: "0.2.1"
|
version: "0.2.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
|
@ -8,8 +8,12 @@ environment:
|
||||||
flutter: ">=1.12.13+hotfix.5"
|
flutter: ">=1.12.13+hotfix.5"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
just_audio_platform_interface: ^2.0.0
|
# just_audio_platform_interface: ^2.0.0
|
||||||
just_audio_web: ^0.2.1
|
just_audio_platform_interface:
|
||||||
|
path: ../just_audio_platform_interface
|
||||||
|
# just_audio_web: ^0.2.1
|
||||||
|
just_audio_web:
|
||||||
|
path: ../just_audio_web
|
||||||
audio_session: ^0.0.10
|
audio_session: ^0.0.10
|
||||||
rxdart: ">= 0.24.1 < 0.26.0"
|
rxdart: ">= 0.24.1 < 0.26.0"
|
||||||
path: ^1.6.4
|
path: ^1.6.4
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:meta/meta.dart' show required;
|
import 'package:meta/meta.dart' show required;
|
||||||
|
@ -68,6 +69,17 @@ abstract class AudioPlayerPlatform {
|
||||||
'playbackEventMessageStream has not been implemented.');
|
'playbackEventMessageStream has not been implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A stream of visualizer waveform data.
|
||||||
|
Stream<Uint8List> get visualizerWaveformStream {
|
||||||
|
throw UnimplementedError(
|
||||||
|
'visualizerWaveformStream has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A stream of visualizer fft data.
|
||||||
|
Stream<Uint8List> get visualizerFftStream {
|
||||||
|
throw UnimplementedError('visualizerFftStream has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads an audio source.
|
/// Loads an audio source.
|
||||||
Future<LoadResponse> load(LoadRequest request) {
|
Future<LoadResponse> load(LoadRequest request) {
|
||||||
throw UnimplementedError("load() has not been implemented.");
|
throw UnimplementedError("load() has not been implemented.");
|
||||||
|
@ -157,6 +169,17 @@ abstract class AudioPlayerPlatform {
|
||||||
ConcatenatingMoveRequest request) {
|
ConcatenatingMoveRequest request) {
|
||||||
throw UnimplementedError("concatenatingMove() has not been implemented.");
|
throw UnimplementedError("concatenatingMove() has not been implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Starts the visualizer.
|
||||||
|
Future<StartVisualizerResponse> startVisualizer(
|
||||||
|
StartVisualizerRequest request) {
|
||||||
|
throw UnimplementedError("startVisualizer() has not been implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the visualizer.
|
||||||
|
Future<StopVisualizerResponse> stopVisualizer(StopVisualizerRequest request) {
|
||||||
|
throw UnimplementedError("stopVisualizer() has not been implemented.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A playback event communicated from the platform implementation to the
|
/// A playback event communicated from the platform implementation to the
|
||||||
|
@ -624,6 +647,53 @@ class ConcatenatingMoveResponse {
|
||||||
ConcatenatingMoveResponse();
|
ConcatenatingMoveResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information communicated to the platform implementation when starting the
|
||||||
|
/// visualizer.
|
||||||
|
class StartVisualizerRequest {
|
||||||
|
final bool enableWaveform;
|
||||||
|
final bool enableFft;
|
||||||
|
final int captureRate;
|
||||||
|
final int captureSize;
|
||||||
|
|
||||||
|
StartVisualizerRequest({
|
||||||
|
@required this.enableWaveform,
|
||||||
|
@required this.enableFft,
|
||||||
|
@required this.captureRate,
|
||||||
|
@required this.captureSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<dynamic, dynamic> toMap() => {
|
||||||
|
'enableWaveform': enableWaveform,
|
||||||
|
'enableFft': enableFft,
|
||||||
|
'captureRate': captureRate,
|
||||||
|
'captureSize': captureSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information returned by the platform implementation after starting the
|
||||||
|
/// visualizer.
|
||||||
|
class StartVisualizerResponse {
|
||||||
|
final int samplingRate;
|
||||||
|
|
||||||
|
StartVisualizerResponse({@required this.samplingRate});
|
||||||
|
|
||||||
|
static StartVisualizerResponse fromMap(Map<dynamic, dynamic> map) =>
|
||||||
|
StartVisualizerResponse(samplingRate: map['samplingRate']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information communicated to the platform implementation when stopping the
|
||||||
|
/// visualizer.
|
||||||
|
class StopVisualizerRequest {
|
||||||
|
Map<dynamic, dynamic> toMap() => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information returned by the platform implementation after stopping the
|
||||||
|
/// visualizer.
|
||||||
|
class StopVisualizerResponse {
|
||||||
|
static StopVisualizerResponse fromMap(Map<dynamic, dynamic> map) =>
|
||||||
|
StopVisualizerResponse();
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about an audio source to be communicated with the platform
|
/// Information about an audio source to be communicated with the platform
|
||||||
/// implementation.
|
/// implementation.
|
||||||
abstract class AudioSourceMessage {
|
abstract class AudioSourceMessage {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
@ -36,6 +37,18 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform {
|
||||||
.receiveBroadcastStream()
|
.receiveBroadcastStream()
|
||||||
.map((map) => PlaybackEventMessage.fromMap(map));
|
.map((map) => PlaybackEventMessage.fromMap(map));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Uint8List> get visualizerWaveformStream =>
|
||||||
|
EventChannel('com.ryanheise.just_audio.waveform_events.$id')
|
||||||
|
.receiveBroadcastStream()
|
||||||
|
.cast<Uint8List>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Uint8List> get visualizerFftStream =>
|
||||||
|
EventChannel('com.ryanheise.just_audio.fft_events.$id')
|
||||||
|
.receiveBroadcastStream()
|
||||||
|
.cast<Uint8List>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LoadResponse> load(LoadRequest request) async {
|
Future<LoadResponse> load(LoadRequest request) async {
|
||||||
return LoadResponse.fromMap(
|
return LoadResponse.fromMap(
|
||||||
|
@ -134,4 +147,18 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform {
|
||||||
return ConcatenatingMoveResponse.fromMap(
|
return ConcatenatingMoveResponse.fromMap(
|
||||||
await _channel.invokeMethod('concatenatingMove', request?.toMap()));
|
await _channel.invokeMethod('concatenatingMove', request?.toMap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StartVisualizerResponse> startVisualizer(
|
||||||
|
StartVisualizerRequest request) async {
|
||||||
|
return StartVisualizerResponse.fromMap(
|
||||||
|
await _channel.invokeMethod('startVisualizer', request?.toMap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StopVisualizerResponse> stopVisualizer(
|
||||||
|
StopVisualizerRequest request) async {
|
||||||
|
return StopVisualizerResponse.fromMap(
|
||||||
|
await _channel.invokeMethod('stopVisualizer', request?.toMap()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ flutter:
|
||||||
fileName: just_audio_web.dart
|
fileName: just_audio_web.dart
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
just_audio_platform_interface: ^2.0.0
|
# just_audio_platform_interface: ^2.0.0
|
||||||
|
just_audio_platform_interface:
|
||||||
|
path: ../just_audio_platform_interface
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
|
|
Loading…
Reference in New Issue