Control over shuffle order.
This commit is contained in:
parent
a864c2f87d
commit
e92fd8c863
|
@ -80,8 +80,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
private Integer audioSessionId;
|
private Integer audioSessionId;
|
||||||
private MediaSource mediaSource;
|
private MediaSource mediaSource;
|
||||||
private Integer currentIndex;
|
private Integer currentIndex;
|
||||||
private Map<LoopingMediaSource, MediaSource> loopingChildren = new HashMap<>();
|
|
||||||
private Map<LoopingMediaSource, Integer> loopingCounts = new HashMap<>();
|
|
||||||
private final Handler handler = new Handler();
|
private final Handler handler = new Handler();
|
||||||
private final Runnable bufferWatcher = new Runnable() {
|
private final Runnable bufferWatcher = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,6 +319,10 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
setShuffleModeEnabled((Integer) request.get("shuffleMode") == 1);
|
setShuffleModeEnabled((Integer) request.get("shuffleMode") == 1);
|
||||||
result.success(new HashMap<String, Object>());
|
result.success(new HashMap<String, Object>());
|
||||||
break;
|
break;
|
||||||
|
case "setShuffleOrder":
|
||||||
|
setShuffleOrder(request.get("audioSource"));
|
||||||
|
result.success(new HashMap<String, Object>());
|
||||||
|
break;
|
||||||
case "setAutomaticallyWaitsToMinimizeStalling":
|
case "setAutomaticallyWaitsToMinimizeStalling":
|
||||||
result.success(new HashMap<String, Object>());
|
result.success(new HashMap<String, Object>());
|
||||||
break;
|
break;
|
||||||
|
@ -332,14 +334,20 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
case "concatenatingInsertAll":
|
case "concatenatingInsertAll":
|
||||||
concatenating(request.get("id"))
|
concatenating(request.get("id"))
|
||||||
.addMediaSources((Integer)request.get("index"), getAudioSources(request.get("children")), handler, () -> result.success(new HashMap<String, Object>()));
|
.addMediaSources((Integer)request.get("index"), getAudioSources(request.get("children")), handler, () -> result.success(new HashMap<String, Object>()));
|
||||||
|
concatenating(request.get("id"))
|
||||||
|
.setShuffleOrder(decodeShuffleOrder((List<Integer>)request.get("shuffleOrder")));
|
||||||
break;
|
break;
|
||||||
case "concatenatingRemoveRange":
|
case "concatenatingRemoveRange":
|
||||||
concatenating(request.get("id"))
|
concatenating(request.get("id"))
|
||||||
.removeMediaSourceRange((Integer)request.get("startIndex"), (Integer)request.get("endIndex"), handler, () -> result.success(new HashMap<String, Object>()));
|
.removeMediaSourceRange((Integer)request.get("startIndex"), (Integer)request.get("endIndex"), handler, () -> result.success(new HashMap<String, Object>()));
|
||||||
|
concatenating(request.get("id"))
|
||||||
|
.setShuffleOrder(decodeShuffleOrder((List<Integer>)request.get("shuffleOrder")));
|
||||||
break;
|
break;
|
||||||
case "concatenatingMove":
|
case "concatenatingMove":
|
||||||
concatenating(request.get("id"))
|
concatenating(request.get("id"))
|
||||||
.moveMediaSource((Integer)request.get("currentIndex"), (Integer)request.get("newIndex"), handler, () -> result.success(new HashMap<String, Object>()));
|
.moveMediaSource((Integer)request.get("currentIndex"), (Integer)request.get("newIndex"), handler, () -> result.success(new HashMap<String, Object>()));
|
||||||
|
concatenating(request.get("id"))
|
||||||
|
.setShuffleOrder(decodeShuffleOrder((List<Integer>)request.get("shuffleOrder")));
|
||||||
break;
|
break;
|
||||||
case "setAndroidAudioAttributes":
|
case "setAndroidAudioAttributes":
|
||||||
setAudioAttributes((Integer)request.get("contentType"), (Integer)request.get("flags"), (Integer)request.get("usage"));
|
setAudioAttributes((Integer)request.get("contentType"), (Integer)request.get("flags"), (Integer)request.get("usage"));
|
||||||
|
@ -358,39 +366,12 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the shuffle order for mediaSource, with currentIndex at
|
private ShuffleOrder decodeShuffleOrder(List<Integer> indexList) {
|
||||||
// the first position. Traverse the tree incrementing index at each
|
int[] shuffleIndices = new int[indexList.size()];
|
||||||
// node.
|
for (int i = 0; i < shuffleIndices.length; i++) {
|
||||||
private int setShuffleOrder(MediaSource mediaSource, int index) {
|
shuffleIndices[i] = indexList.get(i);
|
||||||
if (mediaSource instanceof ConcatenatingMediaSource) {
|
|
||||||
final ConcatenatingMediaSource source = (ConcatenatingMediaSource)mediaSource;
|
|
||||||
// Find which child is current
|
|
||||||
Integer currentChildIndex = null;
|
|
||||||
for (int i = 0; i < source.getSize(); i++) {
|
|
||||||
final int indexBefore = index;
|
|
||||||
final MediaSource child = source.getMediaSource(i);
|
|
||||||
index = setShuffleOrder(child, index);
|
|
||||||
// If currentIndex falls within this child, make this child come first.
|
|
||||||
if (currentIndex >= indexBefore && currentIndex < index) {
|
|
||||||
currentChildIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Shuffle so that the current child is first in the shuffle order
|
|
||||||
source.setShuffleOrder(createShuffleOrder(source.getSize(), currentChildIndex));
|
|
||||||
} else if (mediaSource instanceof LoopingMediaSource) {
|
|
||||||
final LoopingMediaSource source = (LoopingMediaSource)mediaSource;
|
|
||||||
// The ExoPlayer API doesn't provide accessors for these so we have
|
|
||||||
// to index them ourselves.
|
|
||||||
MediaSource child = loopingChildren.get(source);
|
|
||||||
int count = loopingCounts.get(source);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
index = setShuffleOrder(child, index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// An actual media item takes up one spot in the playlist.
|
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
return index;
|
return new DefaultShuffleOrder(shuffleIndices, random.nextLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] shuffle(int length, Integer firstIndex) {
|
private static int[] shuffle(int length, Integer firstIndex) {
|
||||||
|
@ -423,6 +404,26 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
return (ConcatenatingMediaSource)mediaSources.get((String)index);
|
return (ConcatenatingMediaSource)mediaSources.get((String)index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setShuffleOrder(final Object json) {
|
||||||
|
Map<?, ?> map = (Map<?, ?>)json;
|
||||||
|
String id = (String)map.get("id");
|
||||||
|
MediaSource mediaSource = mediaSources.get(id);
|
||||||
|
if (mediaSource == null) return;
|
||||||
|
switch ((String)map.get("type")) {
|
||||||
|
case "concatenating":
|
||||||
|
ConcatenatingMediaSource concatenatingMediaSource = (ConcatenatingMediaSource)mediaSource;
|
||||||
|
concatenatingMediaSource.setShuffleOrder(decodeShuffleOrder((List<Integer>)map.get("shuffleOrder")));
|
||||||
|
List<Object> children = (List<Object>)map.get("children");
|
||||||
|
for (Object child : children) {
|
||||||
|
setShuffleOrder(child);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "looping":
|
||||||
|
setShuffleOrder(map.get("child"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MediaSource getAudioSource(final Object json) {
|
private MediaSource getAudioSource(final Object json) {
|
||||||
Map<?, ?> map = (Map<?, ?>)json;
|
Map<?, ?> map = (Map<?, ?>)json;
|
||||||
String id = (String)map.get("id");
|
String id = (String)map.get("id");
|
||||||
|
@ -455,7 +456,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
return new ConcatenatingMediaSource(
|
return new ConcatenatingMediaSource(
|
||||||
false, // isAtomic
|
false, // isAtomic
|
||||||
(Boolean)map.get("useLazyPreparation"),
|
(Boolean)map.get("useLazyPreparation"),
|
||||||
new DefaultShuffleOrder(mediaSources.length),
|
decodeShuffleOrder((List<Integer>)map.get("shuffleOrder")),
|
||||||
mediaSources);
|
mediaSources);
|
||||||
case "clipping":
|
case "clipping":
|
||||||
Long start = getLong(map.get("start"));
|
Long start = getLong(map.get("start"));
|
||||||
|
@ -466,11 +467,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
case "looping":
|
case "looping":
|
||||||
Integer count = (Integer)map.get("count");
|
Integer count = (Integer)map.get("count");
|
||||||
MediaSource looperChild = getAudioSource(map.get("child"));
|
MediaSource looperChild = getAudioSource(map.get("child"));
|
||||||
LoopingMediaSource looper = new LoopingMediaSource(looperChild, count);
|
return new LoopingMediaSource(looperChild, count);
|
||||||
// TODO: store both in a single map
|
|
||||||
loopingChildren.put(looper, looperChild);
|
|
||||||
loopingCounts.put(looper, count);
|
|
||||||
return looper;
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown AudioSource type: " + map.get("type"));
|
throw new IllegalArgumentException("Unknown AudioSource type: " + map.get("type"));
|
||||||
}
|
}
|
||||||
|
@ -520,9 +517,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
errorCount = 0;
|
errorCount = 0;
|
||||||
prepareResult = result;
|
prepareResult = result;
|
||||||
transition(ProcessingState.loading);
|
transition(ProcessingState.loading);
|
||||||
if (player.getShuffleModeEnabled()) {
|
|
||||||
setShuffleOrder(mediaSource, 0);
|
|
||||||
}
|
|
||||||
this.mediaSource = mediaSource;
|
this.mediaSource = mediaSource;
|
||||||
player.prepare(mediaSource);
|
player.prepare(mediaSource);
|
||||||
}
|
}
|
||||||
|
@ -669,9 +663,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShuffleModeEnabled(final boolean enabled) {
|
public void setShuffleModeEnabled(final boolean enabled) {
|
||||||
if (enabled) {
|
|
||||||
setShuffleOrder(mediaSource, 0);
|
|
||||||
}
|
|
||||||
player.setShuffleModeEnabled(enabled);
|
player.setShuffleModeEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +685,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
||||||
}
|
}
|
||||||
mediaSources.clear();
|
mediaSources.clear();
|
||||||
mediaSource = null;
|
mediaSource = null;
|
||||||
loopingChildren.clear();
|
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
player.release();
|
player.release();
|
||||||
player = null;
|
player = null;
|
||||||
|
|
|
@ -106,6 +106,9 @@
|
||||||
} else if ([@"setShuffleMode" isEqualToString:call.method]) {
|
} else if ([@"setShuffleMode" isEqualToString:call.method]) {
|
||||||
[self setShuffleModeEnabled:(BOOL)([request[@"shuffleMode"] intValue] == 1)];
|
[self setShuffleModeEnabled:(BOOL)([request[@"shuffleMode"] intValue] == 1)];
|
||||||
result(@{});
|
result(@{});
|
||||||
|
} else if ([@"setShuffleOrder" isEqualToString:call.method]) {
|
||||||
|
[self setShuffleOrder:(NSDictionary *)request[@"audioSources"]];
|
||||||
|
result(@{});
|
||||||
} else if ([@"setAutomaticallyWaitsToMinimizeStalling" isEqualToString:call.method]) {
|
} else if ([@"setAutomaticallyWaitsToMinimizeStalling" isEqualToString:call.method]) {
|
||||||
[self setAutomaticallyWaitsToMinimizeStalling:(BOOL)[request[@"enabled"] boolValue]];
|
[self setAutomaticallyWaitsToMinimizeStalling:(BOOL)[request[@"enabled"] boolValue]];
|
||||||
result(@{});
|
result(@{});
|
||||||
|
@ -115,13 +118,13 @@
|
||||||
result(@{});
|
result(@{});
|
||||||
}];
|
}];
|
||||||
} else if ([@"concatenatingInsertAll" isEqualToString:call.method]) {
|
} else if ([@"concatenatingInsertAll" isEqualToString:call.method]) {
|
||||||
[self concatenatingInsertAll:(NSString *)request[@"id"] index:[request[@"index"] intValue] sources:(NSArray *)request[@"children"]];
|
[self concatenatingInsertAll:(NSString *)request[@"id"] index:[request[@"index"] intValue] sources:(NSArray *)request[@"children"] shuffleOrder:(NSArray<NSNumber *> *)request[@"shuffleOrder"]];
|
||||||
result(@{});
|
result(@{});
|
||||||
} else if ([@"concatenatingRemoveRange" isEqualToString:call.method]) {
|
} else if ([@"concatenatingRemoveRange" isEqualToString:call.method]) {
|
||||||
[self concatenatingRemoveRange:(NSString *)request[@"id"] start:[request[@"startIndex"] intValue] end:[request[@"endIndex"] intValue]];
|
[self concatenatingRemoveRange:(NSString *)request[@"id"] start:[request[@"startIndex"] intValue] end:[request[@"endIndex"] intValue] shuffleOrder:(NSArray<NSNumber *> *)request[@"shuffleOrder"]];
|
||||||
result(@{});
|
result(@{});
|
||||||
} else if ([@"concatenatingMove" isEqualToString:call.method]) {
|
} else if ([@"concatenatingMove" isEqualToString:call.method]) {
|
||||||
[self concatenatingMove:(NSString *)request[@"id"] currentIndex:[request[@"currentIndex"] intValue] newIndex:[request[@"newIndex"] intValue]];
|
[self concatenatingMove:(NSString *)request[@"id"] currentIndex:[request[@"currentIndex"] intValue] newIndex:[request[@"newIndex"] intValue] shuffleOrder:(NSArray<NSNumber *> *)request[@"shuffleOrder"]];
|
||||||
result(@{});
|
result(@{});
|
||||||
} else if ([@"setAndroidAudioAttributes" isEqualToString:call.method]) {
|
} else if ([@"setAndroidAudioAttributes" isEqualToString:call.method]) {
|
||||||
result(@{});
|
result(@{});
|
||||||
|
@ -144,22 +147,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untested
|
// Untested
|
||||||
- (void)concatenatingAdd:(NSString *)catId source:(NSDictionary *)source {
|
- (void)concatenatingInsertAll:(NSString *)catId index:(int)index sources:(NSArray *)sources shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||||
[self concatenatingInsertAll:catId index:-1 sources:@[source]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Untested
|
|
||||||
- (void)concatenatingInsert:(NSString *)catId index:(int)index source:(NSDictionary *)source {
|
|
||||||
[self concatenatingInsertAll:catId index:index sources:@[source]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Untested
|
|
||||||
- (void)concatenatingAddAll:(NSString *)catId sources:(NSArray *)sources {
|
|
||||||
[self concatenatingInsertAll:catId index:-1 sources:sources];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Untested
|
|
||||||
- (void)concatenatingInsertAll:(NSString *)catId index:(int)index sources:(NSArray *)sources {
|
|
||||||
// Find all duplicates of the identified ConcatenatingAudioSource.
|
// Find all duplicates of the identified ConcatenatingAudioSource.
|
||||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||||
[_audioSource findById:catId matches:matches];
|
[_audioSource findById:catId matches:matches];
|
||||||
|
@ -172,6 +160,7 @@
|
||||||
AudioSource *audioSource = audioSources[j];
|
AudioSource *audioSource = audioSources[j];
|
||||||
[catSource insertSource:audioSource atIndex:(idx + j)];
|
[catSource insertSource:audioSource atIndex:(idx + j)];
|
||||||
}
|
}
|
||||||
|
[catSource setShuffleOrder:shuffleOrder];
|
||||||
}
|
}
|
||||||
// Index the new audio sources.
|
// Index the new audio sources.
|
||||||
_indexedAudioSources = [[NSMutableArray alloc] init];
|
_indexedAudioSources = [[NSMutableArray alloc] init];
|
||||||
|
@ -200,12 +189,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untested
|
// Untested
|
||||||
- (void)concatenatingRemoveAt:(NSString *)catId index:(int)index {
|
- (void)concatenatingRemoveRange:(NSString *)catId start:(int)start end:(int)end shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||||
[self concatenatingRemoveRange:catId start:index end:(index + 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Untested
|
|
||||||
- (void)concatenatingRemoveRange:(NSString *)catId start:(int)start end:(int)end {
|
|
||||||
// Find all duplicates of the identified ConcatenatingAudioSource.
|
// Find all duplicates of the identified ConcatenatingAudioSource.
|
||||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||||
[_audioSource findById:catId matches:matches];
|
[_audioSource findById:catId matches:matches];
|
||||||
|
@ -214,6 +198,7 @@
|
||||||
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
||||||
int endIndex = end >= 0 ? end : catSource.count;
|
int endIndex = end >= 0 ? end : catSource.count;
|
||||||
[catSource removeSourcesFromIndex:start toIndex:endIndex];
|
[catSource removeSourcesFromIndex:start toIndex:endIndex];
|
||||||
|
[catSource setShuffleOrder:shuffleOrder];
|
||||||
}
|
}
|
||||||
// Re-index the remaining audio sources.
|
// Re-index the remaining audio sources.
|
||||||
NSArray<IndexedAudioSource *> *oldIndexedAudioSources = _indexedAudioSources;
|
NSArray<IndexedAudioSource *> *oldIndexedAudioSources = _indexedAudioSources;
|
||||||
|
@ -239,7 +224,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untested
|
// Untested
|
||||||
- (void)concatenatingMove:(NSString *)catId currentIndex:(int)currentIndex newIndex:(int)newIndex {
|
- (void)concatenatingMove:(NSString *)catId currentIndex:(int)currentIndex newIndex:(int)newIndex shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||||
// Find all duplicates of the identified ConcatenatingAudioSource.
|
// Find all duplicates of the identified ConcatenatingAudioSource.
|
||||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||||
[_audioSource findById:catId matches:matches];
|
[_audioSource findById:catId matches:matches];
|
||||||
|
@ -247,6 +232,7 @@
|
||||||
for (int i = 0; i < matches.count; i++) {
|
for (int i = 0; i < matches.count; i++) {
|
||||||
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
||||||
[catSource moveSourceFromIndex:currentIndex toIndex:newIndex];
|
[catSource moveSourceFromIndex:currentIndex toIndex:newIndex];
|
||||||
|
[catSource setShuffleOrder:shuffleOrder];
|
||||||
}
|
}
|
||||||
// Re-index the audio sources.
|
// Re-index the audio sources.
|
||||||
_indexedAudioSources = [[NSMutableArray alloc] init];
|
_indexedAudioSources = [[NSMutableArray alloc] init];
|
||||||
|
@ -256,11 +242,6 @@
|
||||||
[self broadcastPlaybackEvent];
|
[self broadcastPlaybackEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untested
|
|
||||||
- (void)concatenatingClear:(NSString *)catId {
|
|
||||||
[self concatenatingRemoveRange:catId start:0 end:-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
|
- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
|
||||||
_eventSink = eventSink;
|
_eventSink = eventSink;
|
||||||
return nil;
|
return nil;
|
||||||
|
@ -408,7 +389,8 @@
|
||||||
return [[UriAudioSource alloc] initWithId:data[@"id"] uri:data[@"uri"]];
|
return [[UriAudioSource alloc] initWithId:data[@"id"] uri:data[@"uri"]];
|
||||||
} else if ([@"concatenating" isEqualToString:type]) {
|
} else if ([@"concatenating" isEqualToString:type]) {
|
||||||
return [[ConcatenatingAudioSource alloc] initWithId:data[@"id"]
|
return [[ConcatenatingAudioSource alloc] initWithId:data[@"id"]
|
||||||
audioSources:[self decodeAudioSources:data[@"children"]]];
|
audioSources:[self decodeAudioSources:data[@"children"]]
|
||||||
|
shuffleOrder:(NSArray<NSNumber *> *)data[@"shuffleOrder"]];
|
||||||
} else if ([@"clipping" isEqualToString:type]) {
|
} else if ([@"clipping" isEqualToString:type]) {
|
||||||
return [[ClippingAudioSource alloc] initWithId:data[@"id"]
|
return [[ClippingAudioSource alloc] initWithId:data[@"id"]
|
||||||
audioSource:(UriAudioSource *)[self decodeAudioSource:data[@"child"]]
|
audioSource:(UriAudioSource *)[self decodeAudioSource:data[@"child"]]
|
||||||
|
@ -590,15 +572,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateOrder {
|
- (void)updateOrder {
|
||||||
if (_shuffleModeEnabled) {
|
|
||||||
[_audioSource shuffle:0 currentIndex: _index];
|
|
||||||
}
|
|
||||||
_orderInv = [NSMutableArray arrayWithCapacity:[_indexedAudioSources count]];
|
_orderInv = [NSMutableArray arrayWithCapacity:[_indexedAudioSources count]];
|
||||||
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
||||||
[_orderInv addObject:@(0)];
|
[_orderInv addObject:@(0)];
|
||||||
}
|
}
|
||||||
if (_shuffleModeEnabled) {
|
if (_shuffleModeEnabled) {
|
||||||
_order = [_audioSource getShuffleOrder];
|
_order = [_audioSource getShuffleIndices];
|
||||||
} else {
|
} else {
|
||||||
NSMutableArray *order = [[NSMutableArray alloc] init];
|
NSMutableArray *order = [[NSMutableArray alloc] init];
|
||||||
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
||||||
|
@ -1031,6 +1010,10 @@
|
||||||
[self enqueueFrom:_index];
|
[self enqueueFrom:_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setShuffleOrder:(NSDictionary *)dict {
|
||||||
|
[_audioSource decodeShuffleOrder:dict];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dumpQueue {
|
- (void)dumpQueue {
|
||||||
for (int i = 0; i < _player.items.count; i++) {
|
for (int i = 0; i < _player.items.count; i++) {
|
||||||
IndexedPlayerItem *playerItem = (IndexedPlayerItem *)_player.items[i];
|
IndexedPlayerItem *playerItem = (IndexedPlayerItem *)_player.items[i];
|
||||||
|
|
|
@ -26,12 +26,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||||
return @[];
|
return @[];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
- (void)decodeShuffleOrder:(NSDictionary *)dict {
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
return _audioSource.playerItem;
|
return _audioSource.playerItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||||
return @[@(0)];
|
return @[@(0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,14 @@
|
||||||
|
|
||||||
@implementation ConcatenatingAudioSource {
|
@implementation ConcatenatingAudioSource {
|
||||||
NSMutableArray<AudioSource *> *_audioSources;
|
NSMutableArray<AudioSource *> *_audioSources;
|
||||||
NSMutableArray<NSNumber *> *_shuffleOrder;
|
NSArray<NSNumber *> *_shuffleOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources {
|
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||||
self = [super initWithId:sid];
|
self = [super initWithId:sid];
|
||||||
NSAssert(self, @"super init cannot be nil");
|
NSAssert(self, @"super init cannot be nil");
|
||||||
_audioSources = audioSources;
|
_audioSources = audioSources;
|
||||||
|
_shuffleOrder = shuffleOrder;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,19 +51,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||||
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
||||||
int offset = (int)[order count];
|
int offset = (int)[order count];
|
||||||
NSMutableArray<NSArray<NSNumber *> *> *childOrders = [NSMutableArray new]; // array of array of ints
|
NSMutableArray<NSArray<NSNumber *> *> *childOrders = [NSMutableArray new]; // array of array of ints
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
for (int i = 0; i < [_audioSources count]; i++) {
|
||||||
AudioSource *audioSource = _audioSources[i];
|
AudioSource *audioSource = _audioSources[i];
|
||||||
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleOrder];
|
NSArray<NSNumber *> *childShuffleIndices = [audioSource getShuffleIndices];
|
||||||
NSMutableArray<NSNumber *> *offsetChildShuffleOrder = [NSMutableArray new];
|
NSMutableArray<NSNumber *> *offsetChildShuffleOrder = [NSMutableArray new];
|
||||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
for (int j = 0; j < [childShuffleIndices count]; j++) {
|
||||||
[offsetChildShuffleOrder addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
[offsetChildShuffleOrder addObject:@([childShuffleIndices[j] integerValue] + offset)];
|
||||||
}
|
}
|
||||||
[childOrders addObject:offsetChildShuffleOrder];
|
[childOrders addObject:offsetChildShuffleOrder];
|
||||||
offset += [childShuffleOrder count];
|
offset += [childShuffleIndices count];
|
||||||
}
|
}
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
for (int i = 0; i < [_audioSources count]; i++) {
|
||||||
[order addObjectsFromArray:childOrders[[_shuffleOrder[i] integerValue]]];
|
[order addObjectsFromArray:childOrders[[_shuffleOrder[i] integerValue]]];
|
||||||
|
@ -70,40 +71,22 @@
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||||
int currentChildIndex = -1;
|
_shuffleOrder = shuffleOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)decodeShuffleOrder:(NSDictionary *)dict {
|
||||||
|
_shuffleOrder = (NSArray<NSNumber *> *)dict[@"shuffleOrder"];
|
||||||
|
NSArray *dictChildren = (NSArray *)dict[@"children"];
|
||||||
|
if (_audioSources.count != dictChildren.count) {
|
||||||
|
NSLog(@"decodeShuffleOrder Concatenating children don't match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
for (int i = 0; i < [_audioSources count]; i++) {
|
||||||
int indexBefore = treeIndex;
|
|
||||||
AudioSource *child = _audioSources[i];
|
AudioSource *child = _audioSources[i];
|
||||||
treeIndex = [child shuffle:treeIndex currentIndex:currentIndex];
|
NSDictionary *dictChild = (NSDictionary *)dictChildren[i];
|
||||||
if (currentIndex >= indexBefore && currentIndex < treeIndex) {
|
[child decodesetShuffleOrder:dictChild];
|
||||||
currentChildIndex = i;
|
|
||||||
} else {}
|
|
||||||
}
|
}
|
||||||
// Shuffle so that the current child is first in the shuffle order
|
|
||||||
_shuffleOrder = [NSMutableArray arrayWithCapacity:[_audioSources count]];
|
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
|
||||||
[_shuffleOrder addObject:@(0)];
|
|
||||||
}
|
|
||||||
NSLog(@"shuffle: audioSources.count=%d and shuffleOrder.count=%d", (int)[_audioSources count], (int)[_shuffleOrder count]);
|
|
||||||
// First generate a random shuffle
|
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
|
||||||
int j = arc4random_uniform(i + 1);
|
|
||||||
_shuffleOrder[i] = _shuffleOrder[j];
|
|
||||||
_shuffleOrder[j] = @(i);
|
|
||||||
}
|
|
||||||
// Then bring currentIndex to the front
|
|
||||||
if (currentChildIndex != -1) {
|
|
||||||
for (int i = 1; i < [_audioSources count]; i++) {
|
|
||||||
if ([_shuffleOrder[i] integerValue] == currentChildIndex) {
|
|
||||||
NSNumber *v = _shuffleOrder[0];
|
|
||||||
_shuffleOrder[0] = _shuffleOrder[i];
|
|
||||||
_shuffleOrder[i] = v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return treeIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -42,10 +42,6 @@
|
||||||
return treeIndex + 1;
|
return treeIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
|
||||||
return treeIndex + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)attach:(AVQueuePlayer *)player {
|
- (void)attach:(AVQueuePlayer *)player {
|
||||||
_isAttached = YES;
|
_isAttached = YES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||||
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
||||||
int offset = (int)[order count];
|
int offset = (int)[order count];
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
for (int i = 0; i < [_audioSources count]; i++) {
|
||||||
AudioSource *audioSource = _audioSources[i];
|
AudioSource *audioSource = _audioSources[i];
|
||||||
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleOrder];
|
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleIndices];
|
||||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
||||||
[order addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
[order addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,12 @@
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
- (void)decodeShuffleOrder:(NSDictionary *)dict {
|
||||||
// TODO: This should probably shuffle the same way on all duplicates.
|
NSDictionary *dictChild = (NSDictionary *)dict[@"child"];
|
||||||
for (int i = 0; i < [_audioSources count]; i++) {
|
for (int i = 0; i < [_audioSources count]; i++) {
|
||||||
treeIndex = [_audioSources[i] shuffle:treeIndex currentIndex:currentIndex];
|
AudioSource *child = _audioSources[i];
|
||||||
|
[child decodeShuffleOrder:dictChild];
|
||||||
}
|
}
|
||||||
return treeIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
return _playerItem;
|
return _playerItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||||
return @[@(0)];
|
return @[@(0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,8 +179,12 @@ class _MyAppState extends State<MyApp> {
|
||||||
icon: shuffleModeEnabled
|
icon: shuffleModeEnabled
|
||||||
? Icon(Icons.shuffle, color: Colors.orange)
|
? Icon(Icons.shuffle, color: Colors.orange)
|
||||||
: Icon(Icons.shuffle, color: Colors.grey),
|
: Icon(Icons.shuffle, color: Colors.grey),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
_player.setShuffleModeEnabled(!shuffleModeEnabled);
|
final enable = !shuffleModeEnabled;
|
||||||
|
if (enable) {
|
||||||
|
await _player.shuffle();
|
||||||
|
}
|
||||||
|
await _player.setShuffleModeEnabled(enable);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,7 @@ packages:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0-nullsafety.1"
|
version: "2.5.0-nullsafety.3"
|
||||||
audio_session:
|
audio_session:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -21,35 +21,35 @@ packages:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.1"
|
version: "2.1.0-nullsafety.3"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.3"
|
version: "1.1.0-nullsafety.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.1"
|
version: "1.1.0-nullsafety.3"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0-nullsafety.3"
|
version: "1.15.0-nullsafety.5"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -77,7 +77,7 @@ packages:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -114,6 +114,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.16.1"
|
version: "0.16.1"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.3-nullsafety.3"
|
||||||
just_audio:
|
just_audio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -124,16 +131,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: "1.1.1"
|
version: "1.1.1"
|
||||||
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.1.1"
|
version: "0.1.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
@ -141,21 +148,21 @@ packages:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10-nullsafety.1"
|
version: "0.12.10-nullsafety.3"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.3"
|
version: "1.3.0-nullsafety.6"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.1"
|
version: "1.8.0-nullsafety.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -230,49 +237,49 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.2"
|
version: "1.8.0-nullsafety.4"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0-nullsafety.1"
|
version: "1.10.0-nullsafety.6"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.1"
|
version: "2.1.0-nullsafety.3"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.1"
|
version: "1.1.0-nullsafety.3"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.19-nullsafety.2"
|
version: "0.2.19-nullsafety.6"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.3"
|
version: "1.3.0-nullsafety.5"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -286,7 +293,7 @@ packages:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.3"
|
version: "2.1.0-nullsafety.5"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -302,5 +309,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.2"
|
version: "0.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.10.0-110 <2.11.0"
|
dart: ">=2.12.0-0.0 <3.0.0"
|
||||||
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
- (instancetype)initWithId:(NSString *)sid;
|
- (instancetype)initWithId:(NSString *)sid;
|
||||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder;
|
- (NSArray<NSNumber *> *)getShuffleIndices;
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
- (void)decodeShuffleOrder:(NSDictionary *)dict;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
|
|
||||||
@property (readonly, nonatomic) int count;
|
@property (readonly, nonatomic) int count;
|
||||||
|
|
||||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources;
|
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources shuffleOrder:(NSMutableArray<NSNumber *> *)shuffleOrder;
|
||||||
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
||||||
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||||
|
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -45,7 +45,7 @@ class AudioPlayer {
|
||||||
bool _disposed = false;
|
bool _disposed = false;
|
||||||
|
|
||||||
PlaybackEvent _playbackEvent;
|
PlaybackEvent _playbackEvent;
|
||||||
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>();
|
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
|
||||||
Future<Duration> _durationFuture;
|
Future<Duration> _durationFuture;
|
||||||
final _durationSubject = BehaviorSubject<Duration>();
|
final _durationSubject = BehaviorSubject<Duration>();
|
||||||
final _processingStateSubject = BehaviorSubject<ProcessingState>();
|
final _processingStateSubject = BehaviorSubject<ProcessingState>();
|
||||||
|
@ -56,7 +56,9 @@ class AudioPlayer {
|
||||||
final _icyMetadataSubject = BehaviorSubject<IcyMetadata>();
|
final _icyMetadataSubject = BehaviorSubject<IcyMetadata>();
|
||||||
final _playerStateSubject = BehaviorSubject<PlayerState>();
|
final _playerStateSubject = BehaviorSubject<PlayerState>();
|
||||||
final _sequenceSubject = BehaviorSubject<List<IndexedAudioSource>>();
|
final _sequenceSubject = BehaviorSubject<List<IndexedAudioSource>>();
|
||||||
final _currentIndexSubject = BehaviorSubject<int>();
|
final _shuffleIndicesSubject = BehaviorSubject<List<int>>();
|
||||||
|
final _shuffleIndicesInv = <int>[];
|
||||||
|
final _currentIndexSubject = BehaviorSubject<int>(sync: true);
|
||||||
final _sequenceStateSubject = BehaviorSubject<SequenceState>();
|
final _sequenceStateSubject = BehaviorSubject<SequenceState>();
|
||||||
final _loopModeSubject = BehaviorSubject<LoopMode>();
|
final _loopModeSubject = BehaviorSubject<LoopMode>();
|
||||||
final _shuffleModeEnabledSubject = BehaviorSubject<bool>();
|
final _shuffleModeEnabledSubject = BehaviorSubject<bool>();
|
||||||
|
@ -103,19 +105,29 @@ class AudioPlayer {
|
||||||
.map((event) => event.currentIndex)
|
.map((event) => event.currentIndex)
|
||||||
.distinct()
|
.distinct()
|
||||||
.handleError((err, stack) {/* noop */}));
|
.handleError((err, stack) {/* noop */}));
|
||||||
|
currentIndexStream.listen((index) => print('### index: $index'));
|
||||||
_androidAudioSessionIdSubject.addStream(playbackEventStream
|
_androidAudioSessionIdSubject.addStream(playbackEventStream
|
||||||
.map((event) => event.androidAudioSessionId)
|
.map((event) => event.androidAudioSessionId)
|
||||||
.distinct()
|
.distinct()
|
||||||
.handleError((err, stack) {/* noop */}));
|
.handleError((err, stack) {/* noop */}));
|
||||||
_sequenceStateSubject.addStream(
|
_sequenceStateSubject.addStream(Rx.combineLatest5<List<IndexedAudioSource>,
|
||||||
Rx.combineLatest2<List<IndexedAudioSource>, int, SequenceState>(
|
List<int>, int, bool, LoopMode, SequenceState>(
|
||||||
sequenceStream,
|
sequenceStream,
|
||||||
|
shuffleIndicesStream,
|
||||||
currentIndexStream,
|
currentIndexStream,
|
||||||
(sequence, currentIndex) {
|
shuffleModeEnabledStream,
|
||||||
|
loopModeStream,
|
||||||
|
(sequence, shuffleIndices, currentIndex, shuffleModeEnabled, loopMode) {
|
||||||
if (sequence == null) return null;
|
if (sequence == null) return null;
|
||||||
if (currentIndex == null) currentIndex = 0;
|
if (currentIndex == null) currentIndex = 0;
|
||||||
currentIndex = min(sequence.length - 1, max(0, currentIndex));
|
currentIndex = min(sequence.length - 1, max(0, currentIndex));
|
||||||
return SequenceState(sequence, currentIndex);
|
return SequenceState(
|
||||||
|
sequence,
|
||||||
|
currentIndex,
|
||||||
|
shuffleIndices,
|
||||||
|
shuffleModeEnabled,
|
||||||
|
loopMode,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
).distinct().handleError((err, stack) {/* noop */}));
|
).distinct().handleError((err, stack) {/* noop */}));
|
||||||
_playerStateSubject.addStream(
|
_playerStateSubject.addStream(
|
||||||
|
@ -125,6 +137,8 @@ class AudioPlayer {
|
||||||
(playing, event) => PlayerState(playing, event.processingState))
|
(playing, event) => PlayerState(playing, event.processingState))
|
||||||
.distinct()
|
.distinct()
|
||||||
.handleError((err, stack) {/* noop */}));
|
.handleError((err, stack) {/* noop */}));
|
||||||
|
_shuffleModeEnabledSubject.add(false);
|
||||||
|
_loopModeSubject.add(LoopMode.off);
|
||||||
_platform.then((platform) {
|
_platform.then((platform) {
|
||||||
platform.playbackEventMessageStream.listen((message) {
|
platform.playbackEventMessageStream.listen((message) {
|
||||||
final playbackEvent = PlaybackEvent(
|
final playbackEvent = PlaybackEvent(
|
||||||
|
@ -140,6 +154,8 @@ class AudioPlayer {
|
||||||
currentIndex: message.currentIndex,
|
currentIndex: message.currentIndex,
|
||||||
androidAudioSessionId: message.androidAudioSessionId,
|
androidAudioSessionId: message.androidAudioSessionId,
|
||||||
);
|
);
|
||||||
|
print(
|
||||||
|
"### received event with currentIndex: ${playbackEvent.currentIndex}");
|
||||||
_durationFuture = Future.value(playbackEvent.duration);
|
_durationFuture = Future.value(playbackEvent.duration);
|
||||||
if (playbackEvent.duration != _playbackEvent.duration) {
|
if (playbackEvent.duration != _playbackEvent.duration) {
|
||||||
_durationSubject.add(playbackEvent.duration);
|
_durationSubject.add(playbackEvent.duration);
|
||||||
|
@ -268,6 +284,16 @@ class AudioPlayer {
|
||||||
Stream<List<IndexedAudioSource>> get sequenceStream =>
|
Stream<List<IndexedAudioSource>> get sequenceStream =>
|
||||||
_sequenceSubject.stream;
|
_sequenceSubject.stream;
|
||||||
|
|
||||||
|
/// The current shuffled sequence of indexed audio sources.
|
||||||
|
List<int> get shuffleIndices => _shuffleIndicesSubject.value;
|
||||||
|
|
||||||
|
/// A stream broadcasting the current shuffled sequence of indexed audio
|
||||||
|
/// sources.
|
||||||
|
Stream<List<int>> get shuffleIndicesStream => _shuffleIndicesSubject.stream;
|
||||||
|
|
||||||
|
//List<IndexedAudioSource> get _effectiveSequence =>
|
||||||
|
// shuffleModeEnabled ? shuffleIndices : sequence;
|
||||||
|
|
||||||
/// The index of the current item.
|
/// The index of the current item.
|
||||||
int get currentIndex => _currentIndexSubject.value;
|
int get currentIndex => _currentIndexSubject.value;
|
||||||
|
|
||||||
|
@ -282,14 +308,71 @@ class AudioPlayer {
|
||||||
Stream<SequenceState> get sequenceStateStream => _sequenceStateSubject.stream;
|
Stream<SequenceState> get sequenceStateStream => _sequenceStateSubject.stream;
|
||||||
|
|
||||||
/// Whether there is another item after the current index.
|
/// Whether there is another item after the current index.
|
||||||
bool get hasNext =>
|
bool get hasNext => _nextIndex != null;
|
||||||
_audioSource != null &&
|
|
||||||
currentIndex != null &&
|
|
||||||
currentIndex + 1 < sequence.length;
|
|
||||||
|
|
||||||
/// Whether there is another item before the current index.
|
/// Whether there is another item before the current index.
|
||||||
bool get hasPrevious =>
|
bool get hasPrevious => _previousIndex != null;
|
||||||
_audioSource != null && currentIndex != null && currentIndex > 0;
|
|
||||||
|
int get _nextIndex => _getRelativeIndex(1);
|
||||||
|
int get _previousIndex => _getRelativeIndex(-1);
|
||||||
|
|
||||||
|
int _getRelativeIndex(int offset) {
|
||||||
|
print('### _getRelativeIndex');
|
||||||
|
print('audioSource = $_audioSource');
|
||||||
|
print('currentIndex = $currentIndex');
|
||||||
|
print('shuffleModeEnabled = $shuffleModeEnabled');
|
||||||
|
if (_audioSource == null ||
|
||||||
|
currentIndex == null ||
|
||||||
|
shuffleModeEnabled == null) return null;
|
||||||
|
if (loopMode == LoopMode.one) return currentIndex;
|
||||||
|
int result;
|
||||||
|
print('shuffleModeEnabled: $shuffleModeEnabled');
|
||||||
|
if (shuffleModeEnabled) {
|
||||||
|
print('shuffleIndices: $shuffleIndices');
|
||||||
|
print('shuffleIndicesInv: $_shuffleIndicesInv');
|
||||||
|
if (shuffleIndices == null) return null;
|
||||||
|
final shufflePos = _shuffleIndicesInv[currentIndex];
|
||||||
|
var newShufflePos = shufflePos + offset;
|
||||||
|
print(
|
||||||
|
'newshufflePos($newShufflePos) >= shuffleIndices.length(${shuffleIndices.length})');
|
||||||
|
if (newShufflePos >= shuffleIndices.length) {
|
||||||
|
if (loopMode == LoopMode.all) {
|
||||||
|
newShufflePos = 0;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newShufflePos < 0) {
|
||||||
|
if (loopMode == LoopMode.all) {
|
||||||
|
newShufflePos = shuffleIndices.length - 1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = shuffleIndices[newShufflePos];
|
||||||
|
} else {
|
||||||
|
print("sequence: $sequence");
|
||||||
|
if (sequence == null) return null;
|
||||||
|
result = currentIndex + offset;
|
||||||
|
print("result($result >= sequence.length(${sequence.length}))");
|
||||||
|
if (result >= sequence.length) {
|
||||||
|
if (loopMode == LoopMode.all) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result < 0) {
|
||||||
|
if (loopMode == LoopMode.all) {
|
||||||
|
result = sequence.length - 1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("returning $result");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// The current loop mode.
|
/// The current loop mode.
|
||||||
LoopMode get loopMode => _loopModeSubject.value;
|
LoopMode get loopMode => _loopModeSubject.value;
|
||||||
|
@ -452,6 +535,7 @@ class AudioPlayer {
|
||||||
if (_disposed) return null;
|
if (_disposed) return null;
|
||||||
try {
|
try {
|
||||||
_audioSource = source;
|
_audioSource = source;
|
||||||
|
print("### set _audioSource = $_audioSource");
|
||||||
_broadcastSequence();
|
_broadcastSequence();
|
||||||
final duration = await _load(source,
|
final duration = await _load(source,
|
||||||
initialPosition: initialPosition, initialIndex: initialIndex);
|
initialPosition: initialPosition, initialIndex: initialIndex);
|
||||||
|
@ -466,7 +550,30 @@ class AudioPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _broadcastSequence() {
|
void _broadcastSequence() {
|
||||||
|
print("### _broadcastSequence");
|
||||||
_sequenceSubject.add(_audioSource?.sequence);
|
_sequenceSubject.add(_audioSource?.sequence);
|
||||||
|
|
||||||
|
print('sequence: ${_audioSource?.sequence?.length}');
|
||||||
|
|
||||||
|
_updateShuffleIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateShuffleIndices() {
|
||||||
|
_shuffleIndicesSubject.add(_audioSource?.shuffleIndices);
|
||||||
|
print('shuffle indices: ${_audioSource?.shuffleIndices?.length}');
|
||||||
|
print('shuffleIndices: ${shuffleIndices?.length}');
|
||||||
|
final shuffleIndicesLength = shuffleIndices?.length ?? 0;
|
||||||
|
if (_shuffleIndicesInv.length > shuffleIndicesLength) {
|
||||||
|
_shuffleIndicesInv.removeRange(
|
||||||
|
shuffleIndicesLength, _shuffleIndicesInv.length);
|
||||||
|
} else if (_shuffleIndicesInv.length < shuffleIndicesLength) {
|
||||||
|
_shuffleIndicesInv.addAll(
|
||||||
|
List.filled(shuffleIndicesLength - _shuffleIndicesInv.length, 0));
|
||||||
|
}
|
||||||
|
for (var i = 0; i < shuffleIndicesLength; i++) {
|
||||||
|
_shuffleIndicesInv[shuffleIndices[i]] = i;
|
||||||
|
}
|
||||||
|
print('shuffleIndicesInv: ${_shuffleIndicesInv?.length}');
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerAudioSource(AudioSource source) {
|
_registerAudioSource(AudioSource source) {
|
||||||
|
@ -475,6 +582,7 @@ class AudioPlayer {
|
||||||
|
|
||||||
Future<Duration> _load(AudioSource source,
|
Future<Duration> _load(AudioSource source,
|
||||||
{Duration initialPosition, int initialIndex}) async {
|
{Duration initialPosition, int initialIndex}) async {
|
||||||
|
print("### _load with initialIndex=$initialIndex");
|
||||||
try {
|
try {
|
||||||
if (!kIsWeb && source._requiresHeaders) {
|
if (!kIsWeb && source._requiresHeaders) {
|
||||||
if (_proxy == null) {
|
if (_proxy == null) {
|
||||||
|
@ -483,6 +591,8 @@ class AudioPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await source._setup(this);
|
await source._setup(this);
|
||||||
|
source._shuffle(initialIndex: initialIndex ?? 0);
|
||||||
|
_updateShuffleIndices();
|
||||||
_durationFuture = (await _platform)
|
_durationFuture = (await _platform)
|
||||||
.load(LoadRequest(
|
.load(LoadRequest(
|
||||||
audioSourceMessage: source._toMessage(),
|
audioSourceMessage: source._toMessage(),
|
||||||
|
@ -620,6 +730,19 @@ class AudioPlayer {
|
||||||
enabled ? ShuffleModeMessage.all : ShuffleModeMessage.none));
|
enabled ? ShuffleModeMessage.all : ShuffleModeMessage.none));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively shuffles the children of the currently loaded [AudioSource].
|
||||||
|
Future<void> shuffle() async {
|
||||||
|
print(
|
||||||
|
"shuffle. _disposed: $_disposed, currentIndex: $currentIndex, _audioSource: $_audioSource");
|
||||||
|
if (_disposed) return;
|
||||||
|
if (_audioSource == null) return;
|
||||||
|
_audioSource._shuffle(initialIndex: currentIndex);
|
||||||
|
_updateShuffleIndices();
|
||||||
|
print("Shuffle: $shuffleIndices");
|
||||||
|
await (await _platform).setShuffleOrder(
|
||||||
|
SetShuffleOrderRequest(audioSourceMessage: _audioSource._toMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets automaticallyWaitsToMinimizeStalling for AVPlayer in iOS 10.0 or later, defaults to true.
|
/// Sets automaticallyWaitsToMinimizeStalling for AVPlayer in iOS 10.0 or later, defaults to true.
|
||||||
/// Has no effect on Android clients
|
/// Has no effect on Android clients
|
||||||
Future<void> setAutomaticallyWaitsToMinimizeStalling(
|
Future<void> setAutomaticallyWaitsToMinimizeStalling(
|
||||||
|
@ -657,7 +780,7 @@ class AudioPlayer {
|
||||||
Future<void> seekToNext() async {
|
Future<void> seekToNext() async {
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
if (hasNext) {
|
if (hasNext) {
|
||||||
await seek(Duration.zero, index: currentIndex + 1);
|
await seek(Duration.zero, index: _nextIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +788,7 @@ class AudioPlayer {
|
||||||
Future<void> seekToPrevious() async {
|
Future<void> seekToPrevious() async {
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
if (hasPrevious) {
|
if (hasPrevious) {
|
||||||
await seek(Duration.zero, index: currentIndex - 1);
|
await seek(Duration.zero, index: _previousIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,6 +817,7 @@ class AudioPlayer {
|
||||||
await (await _platform).dispose(DisposeRequest());
|
await (await _platform).dispose(DisposeRequest());
|
||||||
}
|
}
|
||||||
_audioSource = null;
|
_audioSource = null;
|
||||||
|
print("### set _audioSource = $_audioSource (dispose)");
|
||||||
_audioSources.values.forEach((s) => s._dispose());
|
_audioSources.values.forEach((s) => s._dispose());
|
||||||
_audioSources.clear();
|
_audioSources.clear();
|
||||||
_proxy?.stop();
|
_proxy?.stop();
|
||||||
|
@ -704,6 +828,7 @@ class AudioPlayer {
|
||||||
await _volumeSubject.close();
|
await _volumeSubject.close();
|
||||||
await _speedSubject.close();
|
await _speedSubject.close();
|
||||||
await _sequenceSubject.close();
|
await _sequenceSubject.close();
|
||||||
|
await _shuffleIndicesSubject.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,10 +1065,26 @@ class SequenceState {
|
||||||
/// The index of the current source in the sequence.
|
/// The index of the current source in the sequence.
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
|
|
||||||
SequenceState(this.sequence, this.currentIndex);
|
/// The current shuffle order
|
||||||
|
final List<int> shuffleIndices;
|
||||||
|
|
||||||
|
/// Whether shuffle mode is enabled.
|
||||||
|
final bool shuffleModeEnabled;
|
||||||
|
|
||||||
|
/// The current loop mode.
|
||||||
|
final LoopMode loopMode;
|
||||||
|
|
||||||
|
SequenceState(this.sequence, this.currentIndex, this.shuffleIndices,
|
||||||
|
this.shuffleModeEnabled, this.loopMode);
|
||||||
|
|
||||||
/// The current source in the sequence.
|
/// The current source in the sequence.
|
||||||
IndexedAudioSource get currentSource => sequence[currentIndex];
|
IndexedAudioSource get currentSource => sequence[currentIndex];
|
||||||
|
|
||||||
|
/// The effective sequence. This is equivalent to [sequence]. If
|
||||||
|
/// [shuffleModeEnabled] is true, this is modulated by [shuffleIndices].
|
||||||
|
List<IndexedAudioSource> get effectiveSequence => shuffleModeEnabled
|
||||||
|
? shuffleIndices.map((i) => sequence[i]).toList()
|
||||||
|
: sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A local proxy HTTP server for making remote GET requests with headers.
|
/// A local proxy HTTP server for making remote GET requests with headers.
|
||||||
|
@ -1105,6 +1246,8 @@ abstract class AudioSource {
|
||||||
player._registerAudioSource(this);
|
player._registerAudioSource(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _shuffle({int initialIndex});
|
||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void _dispose() {
|
void _dispose() {
|
||||||
_player = null;
|
_player = null;
|
||||||
|
@ -1116,6 +1259,8 @@ abstract class AudioSource {
|
||||||
|
|
||||||
List<IndexedAudioSource> get sequence;
|
List<IndexedAudioSource> get sequence;
|
||||||
|
|
||||||
|
List<int> get shuffleIndices;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => _id.hashCode;
|
int get hashCode => _id.hashCode;
|
||||||
|
|
||||||
|
@ -1129,8 +1274,14 @@ abstract class IndexedAudioSource extends AudioSource {
|
||||||
|
|
||||||
IndexedAudioSource(this.tag);
|
IndexedAudioSource(this.tag);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void _shuffle({int initialIndex}) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<IndexedAudioSource> get sequence => [this];
|
List<IndexedAudioSource> get sequence => [this];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> get shuffleIndices => [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An abstract class representing audio sources that are loaded from a URI.
|
/// An abstract class representing audio sources that are loaded from a URI.
|
||||||
|
@ -1258,11 +1409,14 @@ class HlsAudioSource extends UriAudioSource {
|
||||||
class ConcatenatingAudioSource extends AudioSource {
|
class ConcatenatingAudioSource extends AudioSource {
|
||||||
final List<AudioSource> children;
|
final List<AudioSource> children;
|
||||||
final bool useLazyPreparation;
|
final bool useLazyPreparation;
|
||||||
|
ShuffleOrder _shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingAudioSource({
|
ConcatenatingAudioSource({
|
||||||
@required this.children,
|
@required this.children,
|
||||||
this.useLazyPreparation = true,
|
this.useLazyPreparation = true,
|
||||||
});
|
ShuffleOrder shuffleOrder,
|
||||||
|
}) : _shuffleOrder = shuffleOrder ?? DefaultShuffleOrder()
|
||||||
|
..insert(0, children.length);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> _setup(AudioPlayer player) async {
|
Future<void> _setup(AudioPlayer player) async {
|
||||||
|
@ -1272,28 +1426,60 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void _shuffle({int initialIndex}) {
|
||||||
|
int localInitialIndex;
|
||||||
|
// si = index in [sequence]
|
||||||
|
// ci = index in [children] array.
|
||||||
|
for (var ci = 0, si = 0; ci < children.length; ci++) {
|
||||||
|
final child = children[ci];
|
||||||
|
final childLength = child.sequence.length;
|
||||||
|
final initialIndexWithinThisChild = initialIndex != null &&
|
||||||
|
initialIndex >= si &&
|
||||||
|
initialIndex < si + childLength;
|
||||||
|
if (initialIndexWithinThisChild) {
|
||||||
|
localInitialIndex = ci;
|
||||||
|
}
|
||||||
|
final childInitialIndex =
|
||||||
|
initialIndexWithinThisChild ? (initialIndex - si) : null;
|
||||||
|
child._shuffle(initialIndex: childInitialIndex);
|
||||||
|
si += childLength;
|
||||||
|
}
|
||||||
|
print(
|
||||||
|
"cat _shuffle (initialIndex=$initialIndex, localInitialIndex=$localInitialIndex)");
|
||||||
|
_shuffleOrder.shuffle(initialIndex: localInitialIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/// (Untested) Appends an [AudioSource].
|
/// (Untested) Appends an [AudioSource].
|
||||||
Future<void> add(AudioSource audioSource) async {
|
Future<void> add(AudioSource audioSource) async {
|
||||||
final index = children.length;
|
final index = children.length;
|
||||||
children.add(audioSource);
|
children.add(audioSource);
|
||||||
|
_shuffleOrder.insert(index, 1);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await audioSource._setup(_player);
|
await audioSource._setup(_player);
|
||||||
await (await _player._platform).concatenatingInsertAll(
|
await (await _player._platform).concatenatingInsertAll(
|
||||||
ConcatenatingInsertAllRequest(
|
ConcatenatingInsertAllRequest(
|
||||||
id: _id, index: index, children: [audioSource._toMessage()]));
|
id: _id,
|
||||||
|
index: index,
|
||||||
|
children: [audioSource._toMessage()],
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (Untested) Inserts an [AudioSource] at [index].
|
/// (Untested) Inserts an [AudioSource] at [index].
|
||||||
Future<void> insert(int index, AudioSource audioSource) async {
|
Future<void> insert(int index, AudioSource audioSource) async {
|
||||||
children.insert(index, audioSource);
|
children.insert(index, audioSource);
|
||||||
|
_shuffleOrder.insert(index, 1);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await audioSource._setup(_player);
|
await audioSource._setup(_player);
|
||||||
await (await _player._platform).concatenatingInsertAll(
|
await (await _player._platform).concatenatingInsertAll(
|
||||||
ConcatenatingInsertAllRequest(
|
ConcatenatingInsertAllRequest(
|
||||||
id: _id, index: index, children: [audioSource._toMessage()]));
|
id: _id,
|
||||||
|
index: index,
|
||||||
|
children: [audioSource._toMessage()],
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,6 +1487,7 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
Future<void> addAll(List<AudioSource> children) async {
|
Future<void> addAll(List<AudioSource> children) async {
|
||||||
int index = this.children.length;
|
int index = this.children.length;
|
||||||
this.children.addAll(children);
|
this.children.addAll(children);
|
||||||
|
_shuffleOrder.insert(index, children.length);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
for (var child in children) {
|
for (var child in children) {
|
||||||
|
@ -1310,13 +1497,15 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
ConcatenatingInsertAllRequest(
|
ConcatenatingInsertAllRequest(
|
||||||
id: _id,
|
id: _id,
|
||||||
index: index,
|
index: index,
|
||||||
children: children.map((child) => child._toMessage()).toList()));
|
children: children.map((child) => child._toMessage()).toList(),
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (Untested) Insert multiple [AudioSource]s at [index].
|
/// (Untested) Insert multiple [AudioSource]s at [index].
|
||||||
Future<void> insertAll(int index, List<AudioSource> children) async {
|
Future<void> insertAll(int index, List<AudioSource> children) async {
|
||||||
this.children.insertAll(index, children);
|
this.children.insertAll(index, children);
|
||||||
|
_shuffleOrder.insert(index, children.length);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
for (var child in children) {
|
for (var child in children) {
|
||||||
|
@ -1326,7 +1515,8 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
ConcatenatingInsertAllRequest(
|
ConcatenatingInsertAllRequest(
|
||||||
id: _id,
|
id: _id,
|
||||||
index: index,
|
index: index,
|
||||||
children: children.map((child) => child._toMessage()).toList()));
|
children: children.map((child) => child._toMessage()).toList(),
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1334,11 +1524,15 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
/// [ConcatenatingAudioSource] has already been loaded.
|
/// [ConcatenatingAudioSource] has already been loaded.
|
||||||
Future<void> removeAt(int index) async {
|
Future<void> removeAt(int index) async {
|
||||||
children.removeAt(index);
|
children.removeAt(index);
|
||||||
|
_shuffleOrder.removeRange(index, index + 1);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await (await _player._platform).concatenatingRemoveRange(
|
await (await _player._platform).concatenatingRemoveRange(
|
||||||
ConcatenatingRemoveRangeRequest(
|
ConcatenatingRemoveRangeRequest(
|
||||||
id: _id, startIndex: index, endIndex: index + 1));
|
id: _id,
|
||||||
|
startIndex: index,
|
||||||
|
endIndex: index + 1,
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,33 +1540,46 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
/// to [end] exclusive.
|
/// to [end] exclusive.
|
||||||
Future<void> removeRange(int start, int end) async {
|
Future<void> removeRange(int start, int end) async {
|
||||||
children.removeRange(start, end);
|
children.removeRange(start, end);
|
||||||
|
_shuffleOrder.removeRange(start, end);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await (await _player._platform).concatenatingRemoveRange(
|
await (await _player._platform).concatenatingRemoveRange(
|
||||||
ConcatenatingRemoveRangeRequest(
|
ConcatenatingRemoveRangeRequest(
|
||||||
id: _id, startIndex: start, endIndex: end));
|
id: _id,
|
||||||
|
startIndex: start,
|
||||||
|
endIndex: end,
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (Untested) Moves an [AudioSource] from [currentIndex] to [newIndex].
|
/// (Untested) Moves an [AudioSource] from [currentIndex] to [newIndex].
|
||||||
Future<void> move(int currentIndex, int newIndex) async {
|
Future<void> move(int currentIndex, int newIndex) async {
|
||||||
children.insert(newIndex, children.removeAt(currentIndex));
|
children.insert(newIndex, children.removeAt(currentIndex));
|
||||||
|
_shuffleOrder.removeRange(currentIndex, currentIndex + 1);
|
||||||
|
_shuffleOrder.insert(newIndex, 1);
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await (await _player._platform).concatenatingMove(
|
await (await _player._platform).concatenatingMove(
|
||||||
ConcatenatingMoveRequest(
|
ConcatenatingMoveRequest(
|
||||||
id: _id, currentIndex: currentIndex, newIndex: newIndex));
|
id: _id,
|
||||||
|
currentIndex: currentIndex,
|
||||||
|
newIndex: newIndex,
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (Untested) Removes all [AudioSources].
|
/// (Untested) Removes all [AudioSources].
|
||||||
Future<void> clear() async {
|
Future<void> clear() async {
|
||||||
children.clear();
|
children.clear();
|
||||||
|
_shuffleOrder.clear();
|
||||||
if (_player != null) {
|
if (_player != null) {
|
||||||
_player._broadcastSequence();
|
_player._broadcastSequence();
|
||||||
await (await _player._platform).concatenatingRemoveRange(
|
await (await _player._platform).concatenatingRemoveRange(
|
||||||
ConcatenatingRemoveRangeRequest(
|
ConcatenatingRemoveRangeRequest(
|
||||||
id: _id, startIndex: 0, endIndex: children.length));
|
id: _id,
|
||||||
|
startIndex: 0,
|
||||||
|
endIndex: children.length,
|
||||||
|
shuffleOrder: _shuffleOrder.indices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,6 +1592,25 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
List<IndexedAudioSource> get sequence =>
|
List<IndexedAudioSource> get sequence =>
|
||||||
children.expand((s) => s.sequence).toList();
|
children.expand((s) => s.sequence).toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> get shuffleIndices {
|
||||||
|
var offset = 0;
|
||||||
|
final childIndicesList = <List<int>>[];
|
||||||
|
for (var child in children) {
|
||||||
|
final childIndices = child.shuffleIndices.map((i) => i + offset).toList();
|
||||||
|
childIndicesList.add(childIndices);
|
||||||
|
offset += childIndices.length;
|
||||||
|
}
|
||||||
|
final indices = <int>[];
|
||||||
|
for (var index in _shuffleOrder.indices) {
|
||||||
|
final childIndices = childIndicesList[index];
|
||||||
|
//print("child indices: $childIndices");
|
||||||
|
indices.addAll(childIndices);
|
||||||
|
}
|
||||||
|
print("### returning: $indices");
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get _requiresHeaders =>
|
bool get _requiresHeaders =>
|
||||||
children.any((source) => source._requiresHeaders);
|
children.any((source) => source._requiresHeaders);
|
||||||
|
@ -1393,7 +1619,8 @@ class ConcatenatingAudioSource extends AudioSource {
|
||||||
AudioSourceMessage _toMessage() => ConcatenatingAudioSourceMessage(
|
AudioSourceMessage _toMessage() => ConcatenatingAudioSourceMessage(
|
||||||
id: _id,
|
id: _id,
|
||||||
children: children.map((child) => child._toMessage()).toList(),
|
children: children.map((child) => child._toMessage()).toList(),
|
||||||
useLazyPreparation: useLazyPreparation);
|
useLazyPreparation: useLazyPreparation,
|
||||||
|
shuffleOrder: _shuffleOrder.indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An [AudioSource] that clips the audio of a [UriAudioSource] between a
|
/// An [AudioSource] that clips the audio of a [UriAudioSource] between a
|
||||||
|
@ -1449,10 +1676,16 @@ class LoopingAudioSource extends AudioSource {
|
||||||
await child._setup(player);
|
await child._setup(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void _shuffle({int initialIndex}) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<IndexedAudioSource> get sequence =>
|
List<IndexedAudioSource> get sequence =>
|
||||||
List.generate(count, (i) => child).expand((s) => s.sequence).toList();
|
List.generate(count, (i) => child).expand((s) => s.sequence).toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> get shuffleIndices => List.generate(count, (i) => i);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get _requiresHeaders => child._requiresHeaders;
|
bool get _requiresHeaders => child._requiresHeaders;
|
||||||
|
|
||||||
|
@ -1461,4 +1694,90 @@ class LoopingAudioSource extends AudioSource {
|
||||||
id: _id, child: child._toMessage(), count: count);
|
id: _id, child: child._toMessage(), count: count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines the algorithm for shuffling the order of a
|
||||||
|
/// [ConcatenatingAudioSource]. See [DefaultShuffleOrder] for a default
|
||||||
|
/// implementation.
|
||||||
|
abstract class ShuffleOrder {
|
||||||
|
/// The shuffled list of indices of [AudioSource]s to play. For example,
|
||||||
|
/// [2,0,1] specifies to play the 3rd, then the 1st, then the 2nd item.
|
||||||
|
List<int> get indices;
|
||||||
|
|
||||||
|
/// Shuffles the [indices]. If [initialIndex] is provided, the [indices]
|
||||||
|
/// should be shuffled so that [initialIndex] appears in `indices[0]`.
|
||||||
|
void shuffle({int initialIndex});
|
||||||
|
|
||||||
|
/// Inserts [count] new consecutive indices starting from [index] into
|
||||||
|
/// [indices], at random positions.
|
||||||
|
void insert(int index, int count);
|
||||||
|
|
||||||
|
/// Removes the indices that are `>= start` and `< end`.
|
||||||
|
void removeRange(int start, int end);
|
||||||
|
|
||||||
|
/// Removes all indices.
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A default implementation of [ShuffleOrder].
|
||||||
|
class DefaultShuffleOrder extends ShuffleOrder {
|
||||||
|
final _random;
|
||||||
|
@override
|
||||||
|
final indices = <int>[];
|
||||||
|
|
||||||
|
DefaultShuffleOrder({Random random}) : _random = random ?? Random();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void shuffle({int initialIndex}) {
|
||||||
|
assert(initialIndex == null || indices.contains(initialIndex));
|
||||||
|
print(
|
||||||
|
"### order shuffle, initialIndex: $initialIndex (existing: $indices)");
|
||||||
|
if (indices.length <= 1) return;
|
||||||
|
indices.shuffle(_random);
|
||||||
|
print("now $indices");
|
||||||
|
if (initialIndex == null) return;
|
||||||
|
|
||||||
|
final initialPos = 0;
|
||||||
|
final swapPos = indices.indexOf(initialIndex);
|
||||||
|
// Swap the indices at initialPos and swapPos.
|
||||||
|
final swapIndex = indices[initialPos];
|
||||||
|
indices[initialPos] = initialIndex;
|
||||||
|
indices[swapPos] = swapIndex;
|
||||||
|
print("final $indices");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void insert(int index, int count) {
|
||||||
|
// Offset indices after insertion point.
|
||||||
|
for (var i = 0; i < indices.length; i++) {
|
||||||
|
if (indices[i] >= index) {
|
||||||
|
indices[i] += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert new indices at random positions after currentIndex.
|
||||||
|
final newIndices = List.generate(count, (i) => index + i);
|
||||||
|
for (var newIndex in newIndices) {
|
||||||
|
final insertionIndex = _random.nextInt(indices.length + 1);
|
||||||
|
indices.insert(insertionIndex, newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeRange(int start, int end) {
|
||||||
|
final count = end - start;
|
||||||
|
// Remove old indices.
|
||||||
|
final oldIndices = List.generate(count, (i) => start + i).toSet();
|
||||||
|
indices.removeWhere(oldIndices.contains);
|
||||||
|
// Offset indices after deletion point.
|
||||||
|
for (var i = 0; i < indices.length; i++) {
|
||||||
|
if (indices[i] >= end) {
|
||||||
|
indices[i] -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
indices.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum LoopMode { off, one, all }
|
enum LoopMode { off, one, all }
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
- (instancetype)initWithId:(NSString *)sid;
|
- (instancetype)initWithId:(NSString *)sid;
|
||||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||||
- (NSArray<NSNumber *> *)getShuffleOrder;
|
- (NSArray<NSNumber *> *)getShuffleIndices;
|
||||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
- (void)decodeShuffleOrder:(NSDictionary *)dict;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
|
|
||||||
@property (readonly, nonatomic) int count;
|
@property (readonly, nonatomic) int count;
|
||||||
|
|
||||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources;
|
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources shuffleOrder:(NSMutableArray<NSNumber *> *)shuffleOrder;
|
||||||
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
||||||
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||||
|
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -28,7 +28,7 @@ packages:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0-nullsafety.1"
|
version: "2.5.0-nullsafety.3"
|
||||||
audio_session:
|
audio_session:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -42,7 +42,7 @@ packages:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.1"
|
version: "2.1.0-nullsafety.3"
|
||||||
build:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -70,14 +70,14 @@ packages:
|
||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.3"
|
version: "1.1.0-nullsafety.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -91,7 +91,7 @@ packages:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.1"
|
version: "1.1.0-nullsafety.3"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -105,7 +105,7 @@ packages:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0-nullsafety.3"
|
version: "1.15.0-nullsafety.5"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -133,7 +133,7 @@ packages:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -190,20 +190,20 @@ packages:
|
||||||
name: js
|
name: js
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.2"
|
version: "0.6.3-nullsafety.3"
|
||||||
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: "1.1.1"
|
version: "1.1.1"
|
||||||
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.1.1"
|
version: "0.1.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
@ -218,14 +218,14 @@ packages:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10-nullsafety.1"
|
version: "0.12.10-nullsafety.3"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.3"
|
version: "1.3.0-nullsafety.6"
|
||||||
mockito:
|
mockito:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -260,7 +260,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.1"
|
version: "1.8.0-nullsafety.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -363,49 +363,49 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.2"
|
version: "1.8.0-nullsafety.4"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0-nullsafety.1"
|
version: "1.10.0-nullsafety.6"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.1"
|
version: "2.1.0-nullsafety.3"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.1"
|
version: "1.1.0-nullsafety.3"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.1"
|
version: "1.2.0-nullsafety.3"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.19-nullsafety.2"
|
version: "0.2.19-nullsafety.6"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.3"
|
version: "1.3.0-nullsafety.5"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -419,7 +419,7 @@ packages:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.3"
|
version: "2.1.0-nullsafety.5"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -449,5 +449,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.10.0 <2.11.0"
|
dart: ">=2.12.0-0.0 <3.0.0"
|
||||||
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
||||||
|
|
|
@ -8,8 +8,13 @@ environment:
|
||||||
flutter: ">=1.12.13+hotfix.5"
|
flutter: ">=1.12.13+hotfix.5"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
just_audio_platform_interface: ^1.1.1
|
# TODO: change this back when the new platform interface is published.
|
||||||
just_audio_web: ^0.1.1
|
# just_audio_platform_interface: ^1.1.1
|
||||||
|
just_audio_platform_interface:
|
||||||
|
path: ../just_audio_platform_interface
|
||||||
|
# just_audio_web: ^0.1.1
|
||||||
|
just_audio_web:
|
||||||
|
path: ../just_audio_web
|
||||||
audio_session: ^0.0.9
|
audio_session: ^0.0.9
|
||||||
rxdart: ^0.24.1
|
rxdart: ^0.24.1
|
||||||
path: ^1.6.4
|
path: ^1.6.4
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -44,6 +45,12 @@ void runTests() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkIndices(List<int> indices, int length) {
|
||||||
|
expect(indices.length, length);
|
||||||
|
final sorted = List.of(indices)..sort();
|
||||||
|
expect(sorted, equals(List.generate(indices.length, (i) => i)));
|
||||||
|
}
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
audioSessionChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
audioSessionChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||||
return null;
|
return null;
|
||||||
|
@ -381,6 +388,95 @@ void runTests() {
|
||||||
expect(AudioSource.uri(Uri.parse('https://a.a/a#.mpd')) is DashAudioSource,
|
expect(AudioSource.uri(Uri.parse('https://a.a/a#.mpd')) is DashAudioSource,
|
||||||
equals(true));
|
equals(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('shuffle order', () async {
|
||||||
|
final shuffleOrder1 = DefaultShuffleOrder(random: Random(1001));
|
||||||
|
checkIndices(shuffleOrder1.indices, 0);
|
||||||
|
//expect(shuffleOrder1.indices, equals([]));
|
||||||
|
shuffleOrder1.insert(0, 5);
|
||||||
|
//expect(shuffleOrder1.indices, equals([3, 0, 2, 4, 1]));
|
||||||
|
checkIndices(shuffleOrder1.indices, 5);
|
||||||
|
shuffleOrder1.insert(3, 2);
|
||||||
|
checkIndices(shuffleOrder1.indices, 7);
|
||||||
|
shuffleOrder1.insert(0, 2);
|
||||||
|
checkIndices(shuffleOrder1.indices, 9);
|
||||||
|
shuffleOrder1.insert(9, 2);
|
||||||
|
checkIndices(shuffleOrder1.indices, 11);
|
||||||
|
|
||||||
|
final indices1 = List.of(shuffleOrder1.indices);
|
||||||
|
shuffleOrder1.shuffle();
|
||||||
|
expect(shuffleOrder1.indices, isNot(indices1));
|
||||||
|
checkIndices(shuffleOrder1.indices, 11);
|
||||||
|
final indices2 = List.of(shuffleOrder1.indices);
|
||||||
|
shuffleOrder1.shuffle(initialIndex: 5);
|
||||||
|
expect(shuffleOrder1.indices[0], equals(5));
|
||||||
|
expect(shuffleOrder1.indices, isNot(indices2));
|
||||||
|
checkIndices(shuffleOrder1.indices, 11);
|
||||||
|
|
||||||
|
shuffleOrder1.removeRange(4, 6);
|
||||||
|
checkIndices(shuffleOrder1.indices, 9);
|
||||||
|
shuffleOrder1.removeRange(0, 2);
|
||||||
|
checkIndices(shuffleOrder1.indices, 7);
|
||||||
|
shuffleOrder1.removeRange(5, 7);
|
||||||
|
checkIndices(shuffleOrder1.indices, 5);
|
||||||
|
shuffleOrder1.removeRange(0, 5);
|
||||||
|
checkIndices(shuffleOrder1.indices, 0);
|
||||||
|
|
||||||
|
shuffleOrder1.insert(0, 5);
|
||||||
|
checkIndices(shuffleOrder1.indices, 5);
|
||||||
|
shuffleOrder1.clear();
|
||||||
|
checkIndices(shuffleOrder1.indices, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shuffle', () async {
|
||||||
|
AudioSource createSource() => ConcatenatingAudioSource(
|
||||||
|
shuffleOrder: DefaultShuffleOrder(random: Random(1001)),
|
||||||
|
children: [
|
||||||
|
LoopingAudioSource(
|
||||||
|
count: 2,
|
||||||
|
child: ClippingAudioSource(
|
||||||
|
start: Duration(seconds: 60),
|
||||||
|
end: Duration(seconds: 65),
|
||||||
|
child: AudioSource.uri(Uri.parse("https://foo.foo/foo.mp3")),
|
||||||
|
tag: 'a',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AudioSource.uri(
|
||||||
|
Uri.parse("https://bar.bar/bar.mp3"),
|
||||||
|
tag: 'b',
|
||||||
|
),
|
||||||
|
AudioSource.uri(
|
||||||
|
Uri.parse("https://baz.baz/baz.mp3"),
|
||||||
|
tag: 'c',
|
||||||
|
),
|
||||||
|
ClippingAudioSource(
|
||||||
|
child: AudioSource.uri(
|
||||||
|
Uri.parse("https://baz.baz/baz.mp3"),
|
||||||
|
tag: 'd',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final source1 = createSource();
|
||||||
|
//expect(source1.shuffleIndices, [4, 0, 1, 3, 2]);
|
||||||
|
checkIndices(source1.shuffleIndices, 5);
|
||||||
|
expect(source1.shuffleIndices.skipWhile((i) => i != 0).skip(1).first,
|
||||||
|
equals(1));
|
||||||
|
final player1 = AudioPlayer();
|
||||||
|
await player1.load(source1);
|
||||||
|
checkIndices(player1.shuffleIndices, 5);
|
||||||
|
expect(player1.shuffleIndices.first, equals(0));
|
||||||
|
await player1.seek(Duration.zero, index: 3);
|
||||||
|
await player1.shuffle();
|
||||||
|
checkIndices(player1.shuffleIndices, 5);
|
||||||
|
expect(player1.shuffleIndices.first, equals(3));
|
||||||
|
|
||||||
|
final source2 = createSource();
|
||||||
|
final player2 = AudioPlayer();
|
||||||
|
await player2.load(source2, initialIndex: 3);
|
||||||
|
checkIndices(player2.shuffleIndices, 5);
|
||||||
|
expect(player2.shuffleIndices.first, equals(3));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockJustAudio extends Mock
|
class MockJustAudio extends Mock
|
||||||
|
@ -543,6 +639,12 @@ class MockAudioPlayer implements AudioPlayerPlatform {
|
||||||
return SetShuffleModeResponse();
|
return SetShuffleModeResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||||
|
SetShuffleOrderRequest request) async {
|
||||||
|
return SetShuffleOrderResponse();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request) async {
|
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request) async {
|
||||||
_speed = request.speed;
|
_speed = request.speed;
|
||||||
|
|
|
@ -103,6 +103,12 @@ abstract class AudioPlayerPlatform {
|
||||||
throw UnimplementedError("setShuffleMode() has not been implemented.");
|
throw UnimplementedError("setShuffleMode() has not been implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the shuffle order.
|
||||||
|
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||||
|
SetShuffleOrderRequest request) {
|
||||||
|
throw UnimplementedError("setShuffleOrder() has not been implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
/// On iOS and macOS, sets the automaticallyWaitsToMinimizeStalling option,
|
/// On iOS and macOS, sets the automaticallyWaitsToMinimizeStalling option,
|
||||||
/// and does nothing on other platforms.
|
/// and does nothing on other platforms.
|
||||||
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
||||||
|
@ -431,6 +437,25 @@ class SetShuffleModeResponse {
|
||||||
/// The shuffle mode communicated to the platform implementation.
|
/// The shuffle mode communicated to the platform implementation.
|
||||||
enum ShuffleModeMessage { none, all }
|
enum ShuffleModeMessage { none, all }
|
||||||
|
|
||||||
|
/// Information communicated to the platform implementation when setting the
|
||||||
|
/// shuffle order.
|
||||||
|
class SetShuffleOrderRequest {
|
||||||
|
final AudioSourceMessage audioSourceMessage;
|
||||||
|
|
||||||
|
SetShuffleOrderRequest({@required this.audioSourceMessage});
|
||||||
|
|
||||||
|
Map<dynamic, dynamic> toMap() => {
|
||||||
|
'audioSource': audioSourceMessage.toMap(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information returned by the platform implementation after setting the
|
||||||
|
/// shuffle order.
|
||||||
|
class SetShuffleOrderResponse {
|
||||||
|
static SetShuffleOrderResponse fromMap(Map<dynamic, dynamic> map) =>
|
||||||
|
SetShuffleOrderResponse();
|
||||||
|
}
|
||||||
|
|
||||||
/// Information communicated to the platform implementation when setting the
|
/// Information communicated to the platform implementation when setting the
|
||||||
/// automaticallyWaitsToMinimizeStalling option.
|
/// automaticallyWaitsToMinimizeStalling option.
|
||||||
class SetAutomaticallyWaitsToMinimizeStallingRequest {
|
class SetAutomaticallyWaitsToMinimizeStallingRequest {
|
||||||
|
@ -515,17 +540,20 @@ class ConcatenatingInsertAllRequest {
|
||||||
final String id;
|
final String id;
|
||||||
final int index;
|
final int index;
|
||||||
final List<AudioSourceMessage> children;
|
final List<AudioSourceMessage> children;
|
||||||
|
final List<int> shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingInsertAllRequest({
|
ConcatenatingInsertAllRequest({
|
||||||
@required this.id,
|
@required this.id,
|
||||||
@required this.index,
|
@required this.index,
|
||||||
@required this.children,
|
@required this.children,
|
||||||
|
@required this.shuffleOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<dynamic, dynamic> toMap() => {
|
Map<dynamic, dynamic> toMap() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'index': index,
|
'index': index,
|
||||||
'children': children.map((child) => child.toMap()).toList(),
|
'children': children.map((child) => child.toMap()).toList(),
|
||||||
|
'shuffleOrder': shuffleOrder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,17 +570,20 @@ class ConcatenatingRemoveRangeRequest {
|
||||||
final String id;
|
final String id;
|
||||||
final int startIndex;
|
final int startIndex;
|
||||||
final int endIndex;
|
final int endIndex;
|
||||||
|
final List<int> shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingRemoveRangeRequest({
|
ConcatenatingRemoveRangeRequest({
|
||||||
@required this.id,
|
@required this.id,
|
||||||
@required this.startIndex,
|
@required this.startIndex,
|
||||||
@required this.endIndex,
|
@required this.endIndex,
|
||||||
|
@required this.shuffleOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<dynamic, dynamic> toMap() => {
|
Map<dynamic, dynamic> toMap() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'startIndex': startIndex,
|
'startIndex': startIndex,
|
||||||
'endIndex': endIndex,
|
'endIndex': endIndex,
|
||||||
|
'shuffleOrder': shuffleOrder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,17 +600,20 @@ class ConcatenatingMoveRequest {
|
||||||
final String id;
|
final String id;
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
final int newIndex;
|
final int newIndex;
|
||||||
|
final List<int> shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingMoveRequest({
|
ConcatenatingMoveRequest({
|
||||||
@required this.id,
|
@required this.id,
|
||||||
@required this.currentIndex,
|
@required this.currentIndex,
|
||||||
@required this.newIndex,
|
@required this.newIndex,
|
||||||
|
@required this.shuffleOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<dynamic, dynamic> toMap() => {
|
Map<dynamic, dynamic> toMap() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'currentIndex': currentIndex,
|
'currentIndex': currentIndex,
|
||||||
'newIndex': newIndex,
|
'newIndex': newIndex,
|
||||||
|
'shuffleOrder': shuffleOrder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,11 +712,13 @@ class HlsAudioSourceMessage extends UriAudioSourceMessage {
|
||||||
class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
|
class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
|
||||||
final List<AudioSourceMessage> children;
|
final List<AudioSourceMessage> children;
|
||||||
final bool useLazyPreparation;
|
final bool useLazyPreparation;
|
||||||
|
final List<int> shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingAudioSourceMessage({
|
ConcatenatingAudioSourceMessage({
|
||||||
@required String id,
|
@required String id,
|
||||||
@required this.children,
|
@required this.children,
|
||||||
@required this.useLazyPreparation,
|
@required this.useLazyPreparation,
|
||||||
|
@required this.shuffleOrder,
|
||||||
}) : super(id: id);
|
}) : super(id: id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -691,6 +727,7 @@ class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
|
||||||
'id': id,
|
'id': id,
|
||||||
'children': children.map((child) => child.toMap()).toList(),
|
'children': children.map((child) => child.toMap()).toList(),
|
||||||
'useLazyPreparation': useLazyPreparation,
|
'useLazyPreparation': useLazyPreparation,
|
||||||
|
'shuffleOrder': shuffleOrder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,13 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform {
|
||||||
await _channel.invokeMethod('setShuffleMode', request?.toMap()));
|
await _channel.invokeMethod('setShuffleMode', request?.toMap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||||
|
SetShuffleOrderRequest request) async {
|
||||||
|
return SetShuffleOrderResponse.fromMap(
|
||||||
|
await _channel.invokeMethod('setShuffleOrder', request?.toMap()));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
||||||
setAutomaticallyWaitsToMinimizeStalling(
|
setAutomaticallyWaitsToMinimizeStalling(
|
||||||
|
|
|
@ -112,7 +112,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
final sequence = _audioSourcePlayer.sequence;
|
final sequence = _audioSourcePlayer.sequence;
|
||||||
List<int> order = List<int>(sequence.length);
|
List<int> order = List<int>(sequence.length);
|
||||||
if (_shuffleModeEnabled) {
|
if (_shuffleModeEnabled) {
|
||||||
order = _audioSourcePlayer.shuffleOrder;
|
order = _audioSourcePlayer.shuffleIndices;
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < order.length; i++) {
|
for (var i = 0; i < order.length; i++) {
|
||||||
order[i] = i;
|
order[i] = i;
|
||||||
|
@ -181,9 +181,6 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
_currentAudioSourcePlayer?.pause();
|
_currentAudioSourcePlayer?.pause();
|
||||||
_audioSourcePlayer = getAudioSource(request.audioSourceMessage);
|
_audioSourcePlayer = getAudioSource(request.audioSourceMessage);
|
||||||
_index = request.initialIndex ?? 0;
|
_index = request.initialIndex ?? 0;
|
||||||
if (_shuffleModeEnabled) {
|
|
||||||
_audioSourcePlayer?.shuffle(0, _index);
|
|
||||||
}
|
|
||||||
final duration = await _currentAudioSourcePlayer.load();
|
final duration = await _currentAudioSourcePlayer.load();
|
||||||
if (request.initialPosition != null) {
|
if (request.initialPosition != null) {
|
||||||
await _currentAudioSourcePlayer
|
await _currentAudioSourcePlayer
|
||||||
|
@ -259,12 +256,30 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
Future<SetShuffleModeResponse> setShuffleMode(
|
Future<SetShuffleModeResponse> setShuffleMode(
|
||||||
SetShuffleModeRequest request) async {
|
SetShuffleModeRequest request) async {
|
||||||
_shuffleModeEnabled = request.shuffleMode == ShuffleModeMessage.all;
|
_shuffleModeEnabled = request.shuffleMode == ShuffleModeMessage.all;
|
||||||
if (_shuffleModeEnabled) {
|
|
||||||
_audioSourcePlayer?.shuffle(0, _index);
|
|
||||||
}
|
|
||||||
return SetShuffleModeResponse();
|
return SetShuffleModeResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||||
|
SetShuffleOrderRequest request) async {
|
||||||
|
void internalSetShuffleOrder(AudioSourceMessage sourceMessage) {
|
||||||
|
final audioSourcePlayer = _audioSourcePlayers[sourceMessage.id];
|
||||||
|
if (audioSourcePlayer == null) return;
|
||||||
|
if (sourceMessage is ConcatenatingAudioSourceMessage &&
|
||||||
|
audioSourcePlayer is ConcatenatingAudioSourcePlayer) {
|
||||||
|
audioSourcePlayer.setShuffleOrder(sourceMessage.shuffleOrder);
|
||||||
|
for (var childMessage in sourceMessage.children) {
|
||||||
|
internalSetShuffleOrder(childMessage);
|
||||||
|
}
|
||||||
|
} else if (sourceMessage is LoopingAudioSourceMessage) {
|
||||||
|
internalSetShuffleOrder(sourceMessage.child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internalSetShuffleOrder(request.audioSourceMessage);
|
||||||
|
return SetShuffleOrderResponse();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SeekResponse> seek(SeekRequest request) async {
|
Future<SeekResponse> seek(SeekRequest request) async {
|
||||||
await _seek(request.position.inMilliseconds, request.index);
|
await _seek(request.position.inMilliseconds, request.index);
|
||||||
|
@ -294,6 +309,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
ConcatenatingInsertAllRequest request) async {
|
ConcatenatingInsertAllRequest request) async {
|
||||||
_concatenating(request.id)
|
_concatenating(request.id)
|
||||||
.insertAll(request.index, getAudioSources(request.children));
|
.insertAll(request.index, getAudioSources(request.children));
|
||||||
|
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||||
if (request.index <= _index) {
|
if (request.index <= _index) {
|
||||||
_index += request.children.length;
|
_index += request.children.length;
|
||||||
}
|
}
|
||||||
|
@ -309,6 +325,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
}
|
}
|
||||||
_concatenating(request.id)
|
_concatenating(request.id)
|
||||||
.removeRange(request.startIndex, request.endIndex);
|
.removeRange(request.startIndex, request.endIndex);
|
||||||
|
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||||
if (_index >= request.startIndex && _index < request.endIndex) {
|
if (_index >= request.startIndex && _index < request.endIndex) {
|
||||||
// Skip backward if there's nothing after this
|
// Skip backward if there's nothing after this
|
||||||
if (request.startIndex >= _audioSourcePlayer.sequence.length) {
|
if (request.startIndex >= _audioSourcePlayer.sequence.length) {
|
||||||
|
@ -332,6 +349,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
Future<ConcatenatingMoveResponse> concatenatingMove(
|
Future<ConcatenatingMoveResponse> concatenatingMove(
|
||||||
ConcatenatingMoveRequest request) async {
|
ConcatenatingMoveRequest request) async {
|
||||||
_concatenating(request.id).move(request.currentIndex, request.newIndex);
|
_concatenating(request.id).move(request.currentIndex, request.newIndex);
|
||||||
|
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||||
if (request.currentIndex == _index) {
|
if (request.currentIndex == _index) {
|
||||||
_index = request.newIndex;
|
_index = request.newIndex;
|
||||||
} else if (request.currentIndex < _index && request.newIndex >= _index) {
|
} else if (request.currentIndex < _index && request.newIndex >= _index) {
|
||||||
|
@ -401,7 +419,8 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
||||||
this,
|
this,
|
||||||
audioSourceMessage.id,
|
audioSourceMessage.id,
|
||||||
getAudioSources(audioSourceMessage.children),
|
getAudioSources(audioSourceMessage.children),
|
||||||
audioSourceMessage.useLazyPreparation);
|
audioSourceMessage.useLazyPreparation,
|
||||||
|
audioSourceMessage.shuffleOrder);
|
||||||
} else if (audioSourceMessage is ClippingAudioSourceMessage) {
|
} else if (audioSourceMessage is ClippingAudioSourceMessage) {
|
||||||
return ClippingAudioSourcePlayer(
|
return ClippingAudioSourcePlayer(
|
||||||
this,
|
this,
|
||||||
|
@ -426,9 +445,7 @@ abstract class AudioSourcePlayer {
|
||||||
|
|
||||||
List<IndexedAudioSourcePlayer> get sequence;
|
List<IndexedAudioSourcePlayer> get sequence;
|
||||||
|
|
||||||
List<int> get shuffleOrder;
|
List<int> get shuffleIndices;
|
||||||
|
|
||||||
int shuffle(int treeIndex, int currentIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class IndexedAudioSourcePlayer extends AudioSourcePlayer {
|
abstract class IndexedAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
|
@ -455,9 +472,6 @@ abstract class IndexedAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
|
|
||||||
AudioElement get _audioElement => html5AudioPlayer._audioElement;
|
AudioElement get _audioElement => html5AudioPlayer._audioElement;
|
||||||
|
|
||||||
@override
|
|
||||||
int shuffle(int treeIndex, int currentIndex) => treeIndex + 1;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "${this.runtimeType}";
|
String toString() => "${this.runtimeType}";
|
||||||
}
|
}
|
||||||
|
@ -477,7 +491,7 @@ abstract class UriAudioSourcePlayer extends IndexedAudioSourcePlayer {
|
||||||
List<IndexedAudioSourcePlayer> get sequence => [this];
|
List<IndexedAudioSourcePlayer> get sequence => [this];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> get shuffleOrder => [0];
|
List<int> get shuffleIndices => [0];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Duration> load() async {
|
Future<Duration> load() async {
|
||||||
|
@ -566,33 +580,13 @@ class HlsAudioSourcePlayer extends UriAudioSourcePlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
static List<int> generateShuffleOrder(int length, [int firstIndex]) {
|
|
||||||
final shuffleOrder = List<int>(length);
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
final j = _random.nextInt(i + 1);
|
|
||||||
shuffleOrder[i] = shuffleOrder[j];
|
|
||||||
shuffleOrder[j] = i;
|
|
||||||
}
|
|
||||||
if (firstIndex != null) {
|
|
||||||
for (var i = 1; i < length; i++) {
|
|
||||||
if (shuffleOrder[i] == firstIndex) {
|
|
||||||
final v = shuffleOrder[0];
|
|
||||||
shuffleOrder[0] = shuffleOrder[i];
|
|
||||||
shuffleOrder[i] = v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return shuffleOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<AudioSourcePlayer> audioSourcePlayers;
|
final List<AudioSourcePlayer> audioSourcePlayers;
|
||||||
final bool useLazyPreparation;
|
final bool useLazyPreparation;
|
||||||
List<int> _shuffleOrder;
|
List<int> _shuffleOrder;
|
||||||
|
|
||||||
ConcatenatingAudioSourcePlayer(Html5AudioPlayer html5AudioPlayer, String id,
|
ConcatenatingAudioSourcePlayer(Html5AudioPlayer html5AudioPlayer, String id,
|
||||||
this.audioSourcePlayers, this.useLazyPreparation)
|
this.audioSourcePlayers, this.useLazyPreparation, List<int> shuffleOrder)
|
||||||
: _shuffleOrder = generateShuffleOrder(audioSourcePlayers.length),
|
: _shuffleOrder = shuffleOrder,
|
||||||
super(html5AudioPlayer, id);
|
super(html5AudioPlayer, id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -600,14 +594,14 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
audioSourcePlayers.expand((p) => p.sequence).toList();
|
audioSourcePlayers.expand((p) => p.sequence).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> get shuffleOrder {
|
List<int> get shuffleIndices {
|
||||||
final order = <int>[];
|
final order = <int>[];
|
||||||
var offset = order.length;
|
var offset = order.length;
|
||||||
final childOrders = <List<int>>[];
|
final childOrders = <List<int>>[];
|
||||||
for (var audioSourcePlayer in audioSourcePlayers) {
|
for (var audioSourcePlayer in audioSourcePlayers) {
|
||||||
final childShuffleOrder = audioSourcePlayer.shuffleOrder;
|
final childShuffleIndices = audioSourcePlayer.shuffleIndices;
|
||||||
childOrders.add(childShuffleOrder.map((i) => i + offset).toList());
|
childOrders.add(childShuffleIndices.map((i) => i + offset).toList());
|
||||||
offset += childShuffleOrder.length;
|
offset += childShuffleIndices.length;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < childOrders.length; i++) {
|
for (var i = 0; i < childOrders.length; i++) {
|
||||||
order.addAll(childOrders[_shuffleOrder[i]]);
|
order.addAll(childOrders[_shuffleOrder[i]]);
|
||||||
|
@ -615,21 +609,8 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void setShuffleOrder(List<int> shuffleOrder) {
|
||||||
int shuffle(int treeIndex, int currentIndex) {
|
_shuffleOrder = shuffleOrder;
|
||||||
int currentChildIndex;
|
|
||||||
for (var i = 0; i < audioSourcePlayers.length; i++) {
|
|
||||||
final indexBefore = treeIndex;
|
|
||||||
final child = audioSourcePlayers[i];
|
|
||||||
treeIndex = child.shuffle(treeIndex, currentIndex);
|
|
||||||
if (currentIndex >= indexBefore && currentIndex < treeIndex) {
|
|
||||||
currentChildIndex = i;
|
|
||||||
} else {}
|
|
||||||
}
|
|
||||||
// Shuffle so that the current child is first in the shuffle order
|
|
||||||
_shuffleOrder =
|
|
||||||
generateShuffleOrder(audioSourcePlayers.length, currentChildIndex);
|
|
||||||
return treeIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insertAll(int index, List<AudioSourcePlayer> players) {
|
insertAll(int index, List<AudioSourcePlayer> players) {
|
||||||
|
@ -639,8 +620,6 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
_shuffleOrder[i] += players.length;
|
_shuffleOrder[i] += players.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_shuffleOrder.addAll(
|
|
||||||
List.generate(players.length, (i) => index + i).toList()..shuffle());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRange(int start, int end) {
|
removeRange(int start, int end) {
|
||||||
|
@ -650,7 +629,6 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
_shuffleOrder[i] -= (end - start);
|
_shuffleOrder[i] -= (end - start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_shuffleOrder.removeWhere((i) => i >= start && i < end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
move(int currentIndex, int newIndex) {
|
move(int currentIndex, int newIndex) {
|
||||||
|
@ -675,7 +653,7 @@ class ClippingAudioSourcePlayer extends IndexedAudioSourcePlayer {
|
||||||
List<IndexedAudioSourcePlayer> get sequence => [this];
|
List<IndexedAudioSourcePlayer> get sequence => [this];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> get shuffleOrder => [0];
|
List<int> get shuffleIndices => [0];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Duration> load() async {
|
Future<Duration> load() async {
|
||||||
|
@ -799,22 +777,14 @@ class LoopingAudioSourcePlayer extends AudioSourcePlayer {
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> get shuffleOrder {
|
List<int> get shuffleIndices {
|
||||||
final order = <int>[];
|
final order = <int>[];
|
||||||
var offset = order.length;
|
var offset = order.length;
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
final childShuffleOrder = audioSourcePlayer.shuffleOrder;
|
final childShuffleOrder = audioSourcePlayer.shuffleIndices;
|
||||||
order.addAll(childShuffleOrder.map((i) => i + offset).toList());
|
order.addAll(childShuffleOrder.map((i) => i + offset).toList());
|
||||||
offset += childShuffleOrder.length;
|
offset += childShuffleOrder.length;
|
||||||
}
|
}
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
int shuffle(int treeIndex, int currentIndex) {
|
|
||||||
for (var i = 0; i < count; i++) {
|
|
||||||
treeIndex = audioSourcePlayer.shuffle(treeIndex, currentIndex);
|
|
||||||
}
|
|
||||||
return treeIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ 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: "1.1.1"
|
version: "1.1.1"
|
||||||
meta:
|
meta:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
|
|
|
@ -11,7 +11,10 @@ flutter:
|
||||||
fileName: just_audio_web.dart
|
fileName: just_audio_web.dart
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
just_audio_platform_interface: ^1.1.1
|
# TODO: change this back when the new platform interface is published.
|
||||||
|
# just_audio_platform_interface: ^1.1.1
|
||||||
|
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