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 MediaSource mediaSource;
|
||||
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 Runnable bufferWatcher = new Runnable() {
|
||||
@Override
|
||||
|
@ -321,6 +319,10 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
setShuffleModeEnabled((Integer) request.get("shuffleMode") == 1);
|
||||
result.success(new HashMap<String, Object>());
|
||||
break;
|
||||
case "setShuffleOrder":
|
||||
setShuffleOrder(request.get("audioSource"));
|
||||
result.success(new HashMap<String, Object>());
|
||||
break;
|
||||
case "setAutomaticallyWaitsToMinimizeStalling":
|
||||
result.success(new HashMap<String, Object>());
|
||||
break;
|
||||
|
@ -332,14 +334,20 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
case "concatenatingInsertAll":
|
||||
concatenating(request.get("id"))
|
||||
.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;
|
||||
case "concatenatingRemoveRange":
|
||||
concatenating(request.get("id"))
|
||||
.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;
|
||||
case "concatenatingMove":
|
||||
concatenating(request.get("id"))
|
||||
.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;
|
||||
case "setAndroidAudioAttributes":
|
||||
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
|
||||
// the first position. Traverse the tree incrementing index at each
|
||||
// node.
|
||||
private int setShuffleOrder(MediaSource mediaSource, int index) {
|
||||
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;
|
||||
private ShuffleOrder decodeShuffleOrder(List<Integer> indexList) {
|
||||
int[] shuffleIndices = new int[indexList.size()];
|
||||
for (int i = 0; i < shuffleIndices.length; i++) {
|
||||
shuffleIndices[i] = indexList.get(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) {
|
||||
|
@ -423,6 +404,26 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
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) {
|
||||
Map<?, ?> map = (Map<?, ?>)json;
|
||||
String id = (String)map.get("id");
|
||||
|
@ -455,7 +456,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
return new ConcatenatingMediaSource(
|
||||
false, // isAtomic
|
||||
(Boolean)map.get("useLazyPreparation"),
|
||||
new DefaultShuffleOrder(mediaSources.length),
|
||||
decodeShuffleOrder((List<Integer>)map.get("shuffleOrder")),
|
||||
mediaSources);
|
||||
case "clipping":
|
||||
Long start = getLong(map.get("start"));
|
||||
|
@ -466,11 +467,7 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
case "looping":
|
||||
Integer count = (Integer)map.get("count");
|
||||
MediaSource looperChild = getAudioSource(map.get("child"));
|
||||
LoopingMediaSource looper = new LoopingMediaSource(looperChild, count);
|
||||
// TODO: store both in a single map
|
||||
loopingChildren.put(looper, looperChild);
|
||||
loopingCounts.put(looper, count);
|
||||
return looper;
|
||||
return new LoopingMediaSource(looperChild, count);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown AudioSource type: " + map.get("type"));
|
||||
}
|
||||
|
@ -520,9 +517,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
errorCount = 0;
|
||||
prepareResult = result;
|
||||
transition(ProcessingState.loading);
|
||||
if (player.getShuffleModeEnabled()) {
|
||||
setShuffleOrder(mediaSource, 0);
|
||||
}
|
||||
this.mediaSource = mediaSource;
|
||||
player.prepare(mediaSource);
|
||||
}
|
||||
|
@ -669,9 +663,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
}
|
||||
|
||||
public void setShuffleModeEnabled(final boolean enabled) {
|
||||
if (enabled) {
|
||||
setShuffleOrder(mediaSource, 0);
|
||||
}
|
||||
player.setShuffleModeEnabled(enabled);
|
||||
}
|
||||
|
||||
|
@ -694,7 +685,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud
|
|||
}
|
||||
mediaSources.clear();
|
||||
mediaSource = null;
|
||||
loopingChildren.clear();
|
||||
if (player != null) {
|
||||
player.release();
|
||||
player = null;
|
||||
|
|
|
@ -106,6 +106,9 @@
|
|||
} else if ([@"setShuffleMode" isEqualToString:call.method]) {
|
||||
[self setShuffleModeEnabled:(BOOL)([request[@"shuffleMode"] intValue] == 1)];
|
||||
result(@{});
|
||||
} else if ([@"setShuffleOrder" isEqualToString:call.method]) {
|
||||
[self setShuffleOrder:(NSDictionary *)request[@"audioSources"]];
|
||||
result(@{});
|
||||
} else if ([@"setAutomaticallyWaitsToMinimizeStalling" isEqualToString:call.method]) {
|
||||
[self setAutomaticallyWaitsToMinimizeStalling:(BOOL)[request[@"enabled"] boolValue]];
|
||||
result(@{});
|
||||
|
@ -115,13 +118,13 @@
|
|||
result(@{});
|
||||
}];
|
||||
} 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(@{});
|
||||
} 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(@{});
|
||||
} 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(@{});
|
||||
} else if ([@"setAndroidAudioAttributes" isEqualToString:call.method]) {
|
||||
result(@{});
|
||||
|
@ -144,22 +147,7 @@
|
|||
}
|
||||
|
||||
// Untested
|
||||
- (void)concatenatingAdd:(NSString *)catId source:(NSDictionary *)source {
|
||||
[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 {
|
||||
- (void)concatenatingInsertAll:(NSString *)catId index:(int)index sources:(NSArray *)sources shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||
// Find all duplicates of the identified ConcatenatingAudioSource.
|
||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||
[_audioSource findById:catId matches:matches];
|
||||
|
@ -172,6 +160,7 @@
|
|||
AudioSource *audioSource = audioSources[j];
|
||||
[catSource insertSource:audioSource atIndex:(idx + j)];
|
||||
}
|
||||
[catSource setShuffleOrder:shuffleOrder];
|
||||
}
|
||||
// Index the new audio sources.
|
||||
_indexedAudioSources = [[NSMutableArray alloc] init];
|
||||
|
@ -200,12 +189,7 @@
|
|||
}
|
||||
|
||||
// Untested
|
||||
- (void)concatenatingRemoveAt:(NSString *)catId index:(int)index {
|
||||
[self concatenatingRemoveRange:catId start:index end:(index + 1)];
|
||||
}
|
||||
|
||||
// Untested
|
||||
- (void)concatenatingRemoveRange:(NSString *)catId start:(int)start end:(int)end {
|
||||
- (void)concatenatingRemoveRange:(NSString *)catId start:(int)start end:(int)end shuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||
// Find all duplicates of the identified ConcatenatingAudioSource.
|
||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||
[_audioSource findById:catId matches:matches];
|
||||
|
@ -214,6 +198,7 @@
|
|||
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
||||
int endIndex = end >= 0 ? end : catSource.count;
|
||||
[catSource removeSourcesFromIndex:start toIndex:endIndex];
|
||||
[catSource setShuffleOrder:shuffleOrder];
|
||||
}
|
||||
// Re-index the remaining audio sources.
|
||||
NSArray<IndexedAudioSource *> *oldIndexedAudioSources = _indexedAudioSources;
|
||||
|
@ -239,7 +224,7 @@
|
|||
}
|
||||
|
||||
// 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.
|
||||
NSMutableArray *matches = [[NSMutableArray alloc] init];
|
||||
[_audioSource findById:catId matches:matches];
|
||||
|
@ -247,6 +232,7 @@
|
|||
for (int i = 0; i < matches.count; i++) {
|
||||
ConcatenatingAudioSource *catSource = (ConcatenatingAudioSource *)matches[i];
|
||||
[catSource moveSourceFromIndex:currentIndex toIndex:newIndex];
|
||||
[catSource setShuffleOrder:shuffleOrder];
|
||||
}
|
||||
// Re-index the audio sources.
|
||||
_indexedAudioSources = [[NSMutableArray alloc] init];
|
||||
|
@ -256,11 +242,6 @@
|
|||
[self broadcastPlaybackEvent];
|
||||
}
|
||||
|
||||
// Untested
|
||||
- (void)concatenatingClear:(NSString *)catId {
|
||||
[self concatenatingRemoveRange:catId start:0 end:-1];
|
||||
}
|
||||
|
||||
- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
|
||||
_eventSink = eventSink;
|
||||
return nil;
|
||||
|
@ -408,7 +389,8 @@
|
|||
return [[UriAudioSource alloc] initWithId:data[@"id"] uri:data[@"uri"]];
|
||||
} else if ([@"concatenating" isEqualToString:type]) {
|
||||
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]) {
|
||||
return [[ClippingAudioSource alloc] initWithId:data[@"id"]
|
||||
audioSource:(UriAudioSource *)[self decodeAudioSource:data[@"child"]]
|
||||
|
@ -590,15 +572,12 @@
|
|||
}
|
||||
|
||||
- (void)updateOrder {
|
||||
if (_shuffleModeEnabled) {
|
||||
[_audioSource shuffle:0 currentIndex: _index];
|
||||
}
|
||||
_orderInv = [NSMutableArray arrayWithCapacity:[_indexedAudioSources count]];
|
||||
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
||||
[_orderInv addObject:@(0)];
|
||||
}
|
||||
if (_shuffleModeEnabled) {
|
||||
_order = [_audioSource getShuffleOrder];
|
||||
_order = [_audioSource getShuffleIndices];
|
||||
} else {
|
||||
NSMutableArray *order = [[NSMutableArray alloc] init];
|
||||
for (int i = 0; i < [_indexedAudioSources count]; i++) {
|
||||
|
@ -1031,6 +1010,10 @@
|
|||
[self enqueueFrom:_index];
|
||||
}
|
||||
|
||||
- (void)setShuffleOrder:(NSDictionary *)dict {
|
||||
[_audioSource decodeShuffleOrder:dict];
|
||||
}
|
||||
|
||||
- (void)dumpQueue {
|
||||
for (int i = 0; i < _player.items.count; i++) {
|
||||
IndexedPlayerItem *playerItem = (IndexedPlayerItem *)_player.items[i];
|
||||
|
|
|
@ -26,12 +26,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
return 0;
|
||||
- (void)decodeShuffleOrder:(NSDictionary *)dict {
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
return _audioSource.playerItem;
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||
return @[@(0)];
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
|
||||
@implementation ConcatenatingAudioSource {
|
||||
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];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_audioSources = audioSources;
|
||||
_shuffleOrder = shuffleOrder;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -50,19 +51,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
||||
int offset = (int)[order count];
|
||||
NSMutableArray<NSArray<NSNumber *> *> *childOrders = [NSMutableArray new]; // array of array of ints
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
AudioSource *audioSource = _audioSources[i];
|
||||
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleOrder];
|
||||
NSArray<NSNumber *> *childShuffleIndices = [audioSource getShuffleIndices];
|
||||
NSMutableArray<NSNumber *> *offsetChildShuffleOrder = [NSMutableArray new];
|
||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
||||
[offsetChildShuffleOrder addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
||||
for (int j = 0; j < [childShuffleIndices count]; j++) {
|
||||
[offsetChildShuffleOrder addObject:@([childShuffleIndices[j] integerValue] + offset)];
|
||||
}
|
||||
[childOrders addObject:offsetChildShuffleOrder];
|
||||
offset += [childShuffleOrder count];
|
||||
offset += [childShuffleIndices count];
|
||||
}
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
[order addObjectsFromArray:childOrders[[_shuffleOrder[i] integerValue]]];
|
||||
|
@ -70,40 +71,22 @@
|
|||
return order;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
int currentChildIndex = -1;
|
||||
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder {
|
||||
_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++) {
|
||||
int indexBefore = treeIndex;
|
||||
AudioSource *child = _audioSources[i];
|
||||
treeIndex = [child shuffle:treeIndex currentIndex:currentIndex];
|
||||
if (currentIndex >= indexBefore && currentIndex < treeIndex) {
|
||||
currentChildIndex = i;
|
||||
} else {}
|
||||
NSDictionary *dictChild = (NSDictionary *)dictChildren[i];
|
||||
[child decodesetShuffleOrder:dictChild];
|
||||
}
|
||||
// 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
|
||||
|
|
|
@ -42,10 +42,6 @@
|
|||
return treeIndex + 1;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
return treeIndex + 1;
|
||||
}
|
||||
|
||||
- (void)attach:(AVQueuePlayer *)player {
|
||||
_isAttached = YES;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||
NSMutableArray<NSNumber *> *order = [NSMutableArray new];
|
||||
int offset = (int)[order count];
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
AudioSource *audioSource = _audioSources[i];
|
||||
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleOrder];
|
||||
NSArray<NSNumber *> *childShuffleOrder = [audioSource getShuffleIndices];
|
||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
||||
[order addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
||||
}
|
||||
|
@ -42,12 +42,12 @@
|
|||
return order;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
// TODO: This should probably shuffle the same way on all duplicates.
|
||||
- (void)decodeShuffleOrder:(NSDictionary *)dict {
|
||||
NSDictionary *dictChild = (NSDictionary *)dict[@"child"];
|
||||
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
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
return _playerItem;
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder {
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices {
|
||||
return @[@(0)];
|
||||
}
|
||||
|
||||
|
|
|
@ -179,8 +179,12 @@ class _MyAppState extends State<MyApp> {
|
|||
icon: shuffleModeEnabled
|
||||
? Icon(Icons.shuffle, color: Colors.orange)
|
||||
: Icon(Icons.shuffle, color: Colors.grey),
|
||||
onPressed: () {
|
||||
_player.setShuffleModeEnabled(!shuffleModeEnabled);
|
||||
onPressed: () async {
|
||||
final enable = !shuffleModeEnabled;
|
||||
if (enable) {
|
||||
await _player.shuffle();
|
||||
}
|
||||
await _player.setShuffleModeEnabled(enable);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ packages:
|
|||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.5.0-nullsafety.1"
|
||||
version: "2.5.0-nullsafety.3"
|
||||
audio_session:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -21,35 +21,35 @@ packages:
|
|||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.3"
|
||||
version: "1.1.0-nullsafety.5"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.3"
|
||||
version: "1.15.0-nullsafety.5"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -77,7 +77,7 @@ packages:
|
|||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -114,6 +114,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -124,16 +131,16 @@ packages:
|
|||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
path: "../../just_audio_platform_interface"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.1.1"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
path: "../../just_audio_web"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
|
@ -141,21 +148,21 @@ packages:
|
|||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.10-nullsafety.1"
|
||||
version: "0.12.10-nullsafety.3"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.6"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.1"
|
||||
version: "1.8.0-nullsafety.3"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -230,49 +237,49 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.2"
|
||||
version: "1.8.0-nullsafety.4"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety.1"
|
||||
version: "1.10.0-nullsafety.6"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.19-nullsafety.2"
|
||||
version: "0.2.19-nullsafety.6"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.5"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -286,7 +293,7 @@ packages:
|
|||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.3"
|
||||
version: "2.1.0-nullsafety.5"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -302,5 +309,5 @@ packages:
|
|||
source: hosted
|
||||
version: "0.1.2"
|
||||
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"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- (instancetype)initWithId:(NSString *)sid;
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder;
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices;
|
||||
- (void)decodeShuffleOrder:(NSDictionary *)dict;
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
|
||||
@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)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder;
|
||||
|
||||
@end
|
||||
|
|
|
@ -45,7 +45,7 @@ class AudioPlayer {
|
|||
bool _disposed = false;
|
||||
|
||||
PlaybackEvent _playbackEvent;
|
||||
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>();
|
||||
final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
|
||||
Future<Duration> _durationFuture;
|
||||
final _durationSubject = BehaviorSubject<Duration>();
|
||||
final _processingStateSubject = BehaviorSubject<ProcessingState>();
|
||||
|
@ -56,7 +56,9 @@ class AudioPlayer {
|
|||
final _icyMetadataSubject = BehaviorSubject<IcyMetadata>();
|
||||
final _playerStateSubject = BehaviorSubject<PlayerState>();
|
||||
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 _loopModeSubject = BehaviorSubject<LoopMode>();
|
||||
final _shuffleModeEnabledSubject = BehaviorSubject<bool>();
|
||||
|
@ -103,19 +105,29 @@ class AudioPlayer {
|
|||
.map((event) => event.currentIndex)
|
||||
.distinct()
|
||||
.handleError((err, stack) {/* noop */}));
|
||||
currentIndexStream.listen((index) => print('### index: $index'));
|
||||
_androidAudioSessionIdSubject.addStream(playbackEventStream
|
||||
.map((event) => event.androidAudioSessionId)
|
||||
.distinct()
|
||||
.handleError((err, stack) {/* noop */}));
|
||||
_sequenceStateSubject.addStream(
|
||||
Rx.combineLatest2<List<IndexedAudioSource>, int, SequenceState>(
|
||||
_sequenceStateSubject.addStream(Rx.combineLatest5<List<IndexedAudioSource>,
|
||||
List<int>, int, bool, LoopMode, SequenceState>(
|
||||
sequenceStream,
|
||||
shuffleIndicesStream,
|
||||
currentIndexStream,
|
||||
(sequence, currentIndex) {
|
||||
shuffleModeEnabledStream,
|
||||
loopModeStream,
|
||||
(sequence, shuffleIndices, currentIndex, shuffleModeEnabled, loopMode) {
|
||||
if (sequence == null) return null;
|
||||
if (currentIndex == null) currentIndex = 0;
|
||||
currentIndex = min(sequence.length - 1, max(0, currentIndex));
|
||||
return SequenceState(sequence, currentIndex);
|
||||
return SequenceState(
|
||||
sequence,
|
||||
currentIndex,
|
||||
shuffleIndices,
|
||||
shuffleModeEnabled,
|
||||
loopMode,
|
||||
);
|
||||
},
|
||||
).distinct().handleError((err, stack) {/* noop */}));
|
||||
_playerStateSubject.addStream(
|
||||
|
@ -125,6 +137,8 @@ class AudioPlayer {
|
|||
(playing, event) => PlayerState(playing, event.processingState))
|
||||
.distinct()
|
||||
.handleError((err, stack) {/* noop */}));
|
||||
_shuffleModeEnabledSubject.add(false);
|
||||
_loopModeSubject.add(LoopMode.off);
|
||||
_platform.then((platform) {
|
||||
platform.playbackEventMessageStream.listen((message) {
|
||||
final playbackEvent = PlaybackEvent(
|
||||
|
@ -140,6 +154,8 @@ class AudioPlayer {
|
|||
currentIndex: message.currentIndex,
|
||||
androidAudioSessionId: message.androidAudioSessionId,
|
||||
);
|
||||
print(
|
||||
"### received event with currentIndex: ${playbackEvent.currentIndex}");
|
||||
_durationFuture = Future.value(playbackEvent.duration);
|
||||
if (playbackEvent.duration != _playbackEvent.duration) {
|
||||
_durationSubject.add(playbackEvent.duration);
|
||||
|
@ -268,6 +284,16 @@ class AudioPlayer {
|
|||
Stream<List<IndexedAudioSource>> get sequenceStream =>
|
||||
_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.
|
||||
int get currentIndex => _currentIndexSubject.value;
|
||||
|
||||
|
@ -282,14 +308,71 @@ class AudioPlayer {
|
|||
Stream<SequenceState> get sequenceStateStream => _sequenceStateSubject.stream;
|
||||
|
||||
/// Whether there is another item after the current index.
|
||||
bool get hasNext =>
|
||||
_audioSource != null &&
|
||||
currentIndex != null &&
|
||||
currentIndex + 1 < sequence.length;
|
||||
bool get hasNext => _nextIndex != null;
|
||||
|
||||
/// Whether there is another item before the current index.
|
||||
bool get hasPrevious =>
|
||||
_audioSource != null && currentIndex != null && currentIndex > 0;
|
||||
bool get hasPrevious => _previousIndex != null;
|
||||
|
||||
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.
|
||||
LoopMode get loopMode => _loopModeSubject.value;
|
||||
|
@ -452,6 +535,7 @@ class AudioPlayer {
|
|||
if (_disposed) return null;
|
||||
try {
|
||||
_audioSource = source;
|
||||
print("### set _audioSource = $_audioSource");
|
||||
_broadcastSequence();
|
||||
final duration = await _load(source,
|
||||
initialPosition: initialPosition, initialIndex: initialIndex);
|
||||
|
@ -466,7 +550,30 @@ class AudioPlayer {
|
|||
}
|
||||
|
||||
void _broadcastSequence() {
|
||||
print("### _broadcastSequence");
|
||||
_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) {
|
||||
|
@ -475,6 +582,7 @@ class AudioPlayer {
|
|||
|
||||
Future<Duration> _load(AudioSource source,
|
||||
{Duration initialPosition, int initialIndex}) async {
|
||||
print("### _load with initialIndex=$initialIndex");
|
||||
try {
|
||||
if (!kIsWeb && source._requiresHeaders) {
|
||||
if (_proxy == null) {
|
||||
|
@ -483,6 +591,8 @@ class AudioPlayer {
|
|||
}
|
||||
}
|
||||
await source._setup(this);
|
||||
source._shuffle(initialIndex: initialIndex ?? 0);
|
||||
_updateShuffleIndices();
|
||||
_durationFuture = (await _platform)
|
||||
.load(LoadRequest(
|
||||
audioSourceMessage: source._toMessage(),
|
||||
|
@ -620,6 +730,19 @@ class AudioPlayer {
|
|||
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.
|
||||
/// Has no effect on Android clients
|
||||
Future<void> setAutomaticallyWaitsToMinimizeStalling(
|
||||
|
@ -657,7 +780,7 @@ class AudioPlayer {
|
|||
Future<void> seekToNext() async {
|
||||
if (_disposed) return;
|
||||
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 {
|
||||
if (_disposed) return;
|
||||
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());
|
||||
}
|
||||
_audioSource = null;
|
||||
print("### set _audioSource = $_audioSource (dispose)");
|
||||
_audioSources.values.forEach((s) => s._dispose());
|
||||
_audioSources.clear();
|
||||
_proxy?.stop();
|
||||
|
@ -704,6 +828,7 @@ class AudioPlayer {
|
|||
await _volumeSubject.close();
|
||||
await _speedSubject.close();
|
||||
await _sequenceSubject.close();
|
||||
await _shuffleIndicesSubject.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -940,10 +1065,26 @@ class SequenceState {
|
|||
/// The index of the current source in the sequence.
|
||||
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.
|
||||
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.
|
||||
|
@ -1105,6 +1246,8 @@ abstract class AudioSource {
|
|||
player._registerAudioSource(this);
|
||||
}
|
||||
|
||||
void _shuffle({int initialIndex});
|
||||
|
||||
@mustCallSuper
|
||||
void _dispose() {
|
||||
_player = null;
|
||||
|
@ -1116,6 +1259,8 @@ abstract class AudioSource {
|
|||
|
||||
List<IndexedAudioSource> get sequence;
|
||||
|
||||
List<int> get shuffleIndices;
|
||||
|
||||
@override
|
||||
int get hashCode => _id.hashCode;
|
||||
|
||||
|
@ -1129,8 +1274,14 @@ abstract class IndexedAudioSource extends AudioSource {
|
|||
|
||||
IndexedAudioSource(this.tag);
|
||||
|
||||
@override
|
||||
void _shuffle({int initialIndex}) {}
|
||||
|
||||
@override
|
||||
List<IndexedAudioSource> get sequence => [this];
|
||||
|
||||
@override
|
||||
List<int> get shuffleIndices => [0];
|
||||
}
|
||||
|
||||
/// An abstract class representing audio sources that are loaded from a URI.
|
||||
|
@ -1258,11 +1409,14 @@ class HlsAudioSource extends UriAudioSource {
|
|||
class ConcatenatingAudioSource extends AudioSource {
|
||||
final List<AudioSource> children;
|
||||
final bool useLazyPreparation;
|
||||
ShuffleOrder _shuffleOrder;
|
||||
|
||||
ConcatenatingAudioSource({
|
||||
@required this.children,
|
||||
this.useLazyPreparation = true,
|
||||
});
|
||||
ShuffleOrder shuffleOrder,
|
||||
}) : _shuffleOrder = shuffleOrder ?? DefaultShuffleOrder()
|
||||
..insert(0, children.length);
|
||||
|
||||
@override
|
||||
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].
|
||||
Future<void> add(AudioSource audioSource) async {
|
||||
final index = children.length;
|
||||
children.add(audioSource);
|
||||
_shuffleOrder.insert(index, 1);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await audioSource._setup(_player);
|
||||
await (await _player._platform).concatenatingInsertAll(
|
||||
ConcatenatingInsertAllRequest(
|
||||
id: _id, index: index, children: [audioSource._toMessage()]));
|
||||
id: _id,
|
||||
index: index,
|
||||
children: [audioSource._toMessage()],
|
||||
shuffleOrder: _shuffleOrder.indices));
|
||||
}
|
||||
}
|
||||
|
||||
/// (Untested) Inserts an [AudioSource] at [index].
|
||||
Future<void> insert(int index, AudioSource audioSource) async {
|
||||
children.insert(index, audioSource);
|
||||
_shuffleOrder.insert(index, 1);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await audioSource._setup(_player);
|
||||
await (await _player._platform).concatenatingInsertAll(
|
||||
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 {
|
||||
int index = this.children.length;
|
||||
this.children.addAll(children);
|
||||
_shuffleOrder.insert(index, children.length);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
for (var child in children) {
|
||||
|
@ -1310,13 +1497,15 @@ class ConcatenatingAudioSource extends AudioSource {
|
|||
ConcatenatingInsertAllRequest(
|
||||
id: _id,
|
||||
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].
|
||||
Future<void> insertAll(int index, List<AudioSource> children) async {
|
||||
this.children.insertAll(index, children);
|
||||
_shuffleOrder.insert(index, children.length);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
for (var child in children) {
|
||||
|
@ -1326,7 +1515,8 @@ class ConcatenatingAudioSource extends AudioSource {
|
|||
ConcatenatingInsertAllRequest(
|
||||
id: _id,
|
||||
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.
|
||||
Future<void> removeAt(int index) async {
|
||||
children.removeAt(index);
|
||||
_shuffleOrder.removeRange(index, index + 1);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await (await _player._platform).concatenatingRemoveRange(
|
||||
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.
|
||||
Future<void> removeRange(int start, int end) async {
|
||||
children.removeRange(start, end);
|
||||
_shuffleOrder.removeRange(start, end);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await (await _player._platform).concatenatingRemoveRange(
|
||||
ConcatenatingRemoveRangeRequest(
|
||||
id: _id, startIndex: start, endIndex: end));
|
||||
id: _id,
|
||||
startIndex: start,
|
||||
endIndex: end,
|
||||
shuffleOrder: _shuffleOrder.indices));
|
||||
}
|
||||
}
|
||||
|
||||
/// (Untested) Moves an [AudioSource] from [currentIndex] to [newIndex].
|
||||
Future<void> move(int currentIndex, int newIndex) async {
|
||||
children.insert(newIndex, children.removeAt(currentIndex));
|
||||
_shuffleOrder.removeRange(currentIndex, currentIndex + 1);
|
||||
_shuffleOrder.insert(newIndex, 1);
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await (await _player._platform).concatenatingMove(
|
||||
ConcatenatingMoveRequest(
|
||||
id: _id, currentIndex: currentIndex, newIndex: newIndex));
|
||||
id: _id,
|
||||
currentIndex: currentIndex,
|
||||
newIndex: newIndex,
|
||||
shuffleOrder: _shuffleOrder.indices));
|
||||
}
|
||||
}
|
||||
|
||||
/// (Untested) Removes all [AudioSources].
|
||||
Future<void> clear() async {
|
||||
children.clear();
|
||||
_shuffleOrder.clear();
|
||||
if (_player != null) {
|
||||
_player._broadcastSequence();
|
||||
await (await _player._platform).concatenatingRemoveRange(
|
||||
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 =>
|
||||
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
|
||||
bool get _requiresHeaders =>
|
||||
children.any((source) => source._requiresHeaders);
|
||||
|
@ -1393,7 +1619,8 @@ class ConcatenatingAudioSource extends AudioSource {
|
|||
AudioSourceMessage _toMessage() => ConcatenatingAudioSourceMessage(
|
||||
id: _id,
|
||||
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
|
||||
|
@ -1449,10 +1676,16 @@ class LoopingAudioSource extends AudioSource {
|
|||
await child._setup(player);
|
||||
}
|
||||
|
||||
@override
|
||||
void _shuffle({int initialIndex}) {}
|
||||
|
||||
@override
|
||||
List<IndexedAudioSource> get sequence =>
|
||||
List.generate(count, (i) => child).expand((s) => s.sequence).toList();
|
||||
|
||||
@override
|
||||
List<int> get shuffleIndices => List.generate(count, (i) => i);
|
||||
|
||||
@override
|
||||
bool get _requiresHeaders => child._requiresHeaders;
|
||||
|
||||
|
@ -1461,4 +1694,90 @@ class LoopingAudioSource extends AudioSource {
|
|||
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 }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- (instancetype)initWithId:(NSString *)sid;
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||
- (NSArray<NSNumber *> *)getShuffleOrder;
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
||||
- (NSArray<NSNumber *> *)getShuffleIndices;
|
||||
- (void)decodeShuffleOrder:(NSDictionary *)dict;
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
|
||||
@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)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||
- (void)setShuffleOrder:(NSArray<NSNumber *> *)shuffleOrder;
|
||||
|
||||
@end
|
||||
|
|
|
@ -28,7 +28,7 @@ packages:
|
|||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.5.0-nullsafety.1"
|
||||
version: "2.5.0-nullsafety.3"
|
||||
audio_session:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -42,7 +42,7 @@ packages:
|
|||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -70,14 +70,14 @@ packages:
|
|||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.3"
|
||||
version: "1.1.0-nullsafety.5"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -91,7 +91,7 @@ packages:
|
|||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -105,7 +105,7 @@ packages:
|
|||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.3"
|
||||
version: "1.15.0-nullsafety.5"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -133,7 +133,7 @@ packages:
|
|||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -190,20 +190,20 @@ packages:
|
|||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.2"
|
||||
version: "0.6.3-nullsafety.3"
|
||||
just_audio_platform_interface:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
path: "../just_audio_platform_interface"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.1.1"
|
||||
just_audio_web:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
path: "../just_audio_web"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
|
@ -218,14 +218,14 @@ packages:
|
|||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.10-nullsafety.1"
|
||||
version: "0.12.10-nullsafety.3"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.6"
|
||||
mockito:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -260,7 +260,7 @@ packages:
|
|||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.1"
|
||||
version: "1.8.0-nullsafety.3"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -363,49 +363,49 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.2"
|
||||
version: "1.8.0-nullsafety.4"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety.1"
|
||||
version: "1.10.0-nullsafety.6"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.19-nullsafety.2"
|
||||
version: "0.2.19-nullsafety.6"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.5"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -419,7 +419,7 @@ packages:
|
|||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.3"
|
||||
version: "2.1.0-nullsafety.5"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -449,5 +449,5 @@ packages:
|
|||
source: hosted
|
||||
version: "2.2.1"
|
||||
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"
|
||||
|
|
|
@ -8,8 +8,13 @@ environment:
|
|||
flutter: ">=1.12.13+hotfix.5"
|
||||
|
||||
dependencies:
|
||||
just_audio_platform_interface: ^1.1.1
|
||||
just_audio_web: ^0.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
|
||||
# just_audio_web: ^0.1.1
|
||||
just_audio_web:
|
||||
path: ../just_audio_web
|
||||
audio_session: ^0.0.9
|
||||
rxdart: ^0.24.1
|
||||
path: ^1.6.4
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
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(() {
|
||||
audioSessionChannel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
return null;
|
||||
|
@ -381,6 +388,95 @@ void runTests() {
|
|||
expect(AudioSource.uri(Uri.parse('https://a.a/a#.mpd')) is DashAudioSource,
|
||||
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
|
||||
|
@ -543,6 +639,12 @@ class MockAudioPlayer implements AudioPlayerPlatform {
|
|||
return SetShuffleModeResponse();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||
SetShuffleOrderRequest request) async {
|
||||
return SetShuffleOrderResponse();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request) async {
|
||||
_speed = request.speed;
|
||||
|
|
|
@ -103,6 +103,12 @@ abstract class AudioPlayerPlatform {
|
|||
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,
|
||||
/// and does nothing on other platforms.
|
||||
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
||||
|
@ -431,6 +437,25 @@ class SetShuffleModeResponse {
|
|||
/// The shuffle mode communicated to the platform implementation.
|
||||
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
|
||||
/// automaticallyWaitsToMinimizeStalling option.
|
||||
class SetAutomaticallyWaitsToMinimizeStallingRequest {
|
||||
|
@ -515,17 +540,20 @@ class ConcatenatingInsertAllRequest {
|
|||
final String id;
|
||||
final int index;
|
||||
final List<AudioSourceMessage> children;
|
||||
final List<int> shuffleOrder;
|
||||
|
||||
ConcatenatingInsertAllRequest({
|
||||
@required this.id,
|
||||
@required this.index,
|
||||
@required this.children,
|
||||
@required this.shuffleOrder,
|
||||
});
|
||||
|
||||
Map<dynamic, dynamic> toMap() => {
|
||||
'id': id,
|
||||
'index': index,
|
||||
'children': children.map((child) => child.toMap()).toList(),
|
||||
'shuffleOrder': shuffleOrder,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -542,17 +570,20 @@ class ConcatenatingRemoveRangeRequest {
|
|||
final String id;
|
||||
final int startIndex;
|
||||
final int endIndex;
|
||||
final List<int> shuffleOrder;
|
||||
|
||||
ConcatenatingRemoveRangeRequest({
|
||||
@required this.id,
|
||||
@required this.startIndex,
|
||||
@required this.endIndex,
|
||||
@required this.shuffleOrder,
|
||||
});
|
||||
|
||||
Map<dynamic, dynamic> toMap() => {
|
||||
'id': id,
|
||||
'startIndex': startIndex,
|
||||
'endIndex': endIndex,
|
||||
'shuffleOrder': shuffleOrder,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -569,17 +600,20 @@ class ConcatenatingMoveRequest {
|
|||
final String id;
|
||||
final int currentIndex;
|
||||
final int newIndex;
|
||||
final List<int> shuffleOrder;
|
||||
|
||||
ConcatenatingMoveRequest({
|
||||
@required this.id,
|
||||
@required this.currentIndex,
|
||||
@required this.newIndex,
|
||||
@required this.shuffleOrder,
|
||||
});
|
||||
|
||||
Map<dynamic, dynamic> toMap() => {
|
||||
'id': id,
|
||||
'currentIndex': currentIndex,
|
||||
'newIndex': newIndex,
|
||||
'shuffleOrder': shuffleOrder,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -678,11 +712,13 @@ class HlsAudioSourceMessage extends UriAudioSourceMessage {
|
|||
class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
|
||||
final List<AudioSourceMessage> children;
|
||||
final bool useLazyPreparation;
|
||||
final List<int> shuffleOrder;
|
||||
|
||||
ConcatenatingAudioSourceMessage({
|
||||
@required String id,
|
||||
@required this.children,
|
||||
@required this.useLazyPreparation,
|
||||
@required this.shuffleOrder,
|
||||
}) : super(id: id);
|
||||
|
||||
@override
|
||||
|
@ -691,6 +727,7 @@ class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
|
|||
'id': id,
|
||||
'children': children.map((child) => child.toMap()).toList(),
|
||||
'useLazyPreparation': useLazyPreparation,
|
||||
'shuffleOrder': shuffleOrder,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,13 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform {
|
|||
await _channel.invokeMethod('setShuffleMode', request?.toMap()));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SetShuffleOrderResponse> setShuffleOrder(
|
||||
SetShuffleOrderRequest request) async {
|
||||
return SetShuffleOrderResponse.fromMap(
|
||||
await _channel.invokeMethod('setShuffleOrder', request?.toMap()));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
|
||||
setAutomaticallyWaitsToMinimizeStalling(
|
||||
|
|
|
@ -112,7 +112,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
final sequence = _audioSourcePlayer.sequence;
|
||||
List<int> order = List<int>(sequence.length);
|
||||
if (_shuffleModeEnabled) {
|
||||
order = _audioSourcePlayer.shuffleOrder;
|
||||
order = _audioSourcePlayer.shuffleIndices;
|
||||
} else {
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
order[i] = i;
|
||||
|
@ -181,9 +181,6 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
_currentAudioSourcePlayer?.pause();
|
||||
_audioSourcePlayer = getAudioSource(request.audioSourceMessage);
|
||||
_index = request.initialIndex ?? 0;
|
||||
if (_shuffleModeEnabled) {
|
||||
_audioSourcePlayer?.shuffle(0, _index);
|
||||
}
|
||||
final duration = await _currentAudioSourcePlayer.load();
|
||||
if (request.initialPosition != null) {
|
||||
await _currentAudioSourcePlayer
|
||||
|
@ -259,12 +256,30 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
Future<SetShuffleModeResponse> setShuffleMode(
|
||||
SetShuffleModeRequest request) async {
|
||||
_shuffleModeEnabled = request.shuffleMode == ShuffleModeMessage.all;
|
||||
if (_shuffleModeEnabled) {
|
||||
_audioSourcePlayer?.shuffle(0, _index);
|
||||
}
|
||||
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
|
||||
Future<SeekResponse> seek(SeekRequest request) async {
|
||||
await _seek(request.position.inMilliseconds, request.index);
|
||||
|
@ -294,6 +309,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
ConcatenatingInsertAllRequest request) async {
|
||||
_concatenating(request.id)
|
||||
.insertAll(request.index, getAudioSources(request.children));
|
||||
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||
if (request.index <= _index) {
|
||||
_index += request.children.length;
|
||||
}
|
||||
|
@ -309,6 +325,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
}
|
||||
_concatenating(request.id)
|
||||
.removeRange(request.startIndex, request.endIndex);
|
||||
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||
if (_index >= request.startIndex && _index < request.endIndex) {
|
||||
// Skip backward if there's nothing after this
|
||||
if (request.startIndex >= _audioSourcePlayer.sequence.length) {
|
||||
|
@ -332,6 +349,7 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
Future<ConcatenatingMoveResponse> concatenatingMove(
|
||||
ConcatenatingMoveRequest request) async {
|
||||
_concatenating(request.id).move(request.currentIndex, request.newIndex);
|
||||
_concatenating(request.id).setShuffleOrder(request.shuffleOrder);
|
||||
if (request.currentIndex == _index) {
|
||||
_index = request.newIndex;
|
||||
} else if (request.currentIndex < _index && request.newIndex >= _index) {
|
||||
|
@ -401,7 +419,8 @@ class Html5AudioPlayer extends JustAudioPlayer {
|
|||
this,
|
||||
audioSourceMessage.id,
|
||||
getAudioSources(audioSourceMessage.children),
|
||||
audioSourceMessage.useLazyPreparation);
|
||||
audioSourceMessage.useLazyPreparation,
|
||||
audioSourceMessage.shuffleOrder);
|
||||
} else if (audioSourceMessage is ClippingAudioSourceMessage) {
|
||||
return ClippingAudioSourcePlayer(
|
||||
this,
|
||||
|
@ -426,9 +445,7 @@ abstract class AudioSourcePlayer {
|
|||
|
||||
List<IndexedAudioSourcePlayer> get sequence;
|
||||
|
||||
List<int> get shuffleOrder;
|
||||
|
||||
int shuffle(int treeIndex, int currentIndex);
|
||||
List<int> get shuffleIndices;
|
||||
}
|
||||
|
||||
abstract class IndexedAudioSourcePlayer extends AudioSourcePlayer {
|
||||
|
@ -455,9 +472,6 @@ abstract class IndexedAudioSourcePlayer extends AudioSourcePlayer {
|
|||
|
||||
AudioElement get _audioElement => html5AudioPlayer._audioElement;
|
||||
|
||||
@override
|
||||
int shuffle(int treeIndex, int currentIndex) => treeIndex + 1;
|
||||
|
||||
@override
|
||||
String toString() => "${this.runtimeType}";
|
||||
}
|
||||
|
@ -477,7 +491,7 @@ abstract class UriAudioSourcePlayer extends IndexedAudioSourcePlayer {
|
|||
List<IndexedAudioSourcePlayer> get sequence => [this];
|
||||
|
||||
@override
|
||||
List<int> get shuffleOrder => [0];
|
||||
List<int> get shuffleIndices => [0];
|
||||
|
||||
@override
|
||||
Future<Duration> load() async {
|
||||
|
@ -566,33 +580,13 @@ class HlsAudioSourcePlayer extends UriAudioSourcePlayer {
|
|||
}
|
||||
|
||||
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 bool useLazyPreparation;
|
||||
List<int> _shuffleOrder;
|
||||
|
||||
ConcatenatingAudioSourcePlayer(Html5AudioPlayer html5AudioPlayer, String id,
|
||||
this.audioSourcePlayers, this.useLazyPreparation)
|
||||
: _shuffleOrder = generateShuffleOrder(audioSourcePlayers.length),
|
||||
this.audioSourcePlayers, this.useLazyPreparation, List<int> shuffleOrder)
|
||||
: _shuffleOrder = shuffleOrder,
|
||||
super(html5AudioPlayer, id);
|
||||
|
||||
@override
|
||||
|
@ -600,14 +594,14 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
|||
audioSourcePlayers.expand((p) => p.sequence).toList();
|
||||
|
||||
@override
|
||||
List<int> get shuffleOrder {
|
||||
List<int> get shuffleIndices {
|
||||
final order = <int>[];
|
||||
var offset = order.length;
|
||||
final childOrders = <List<int>>[];
|
||||
for (var audioSourcePlayer in audioSourcePlayers) {
|
||||
final childShuffleOrder = audioSourcePlayer.shuffleOrder;
|
||||
childOrders.add(childShuffleOrder.map((i) => i + offset).toList());
|
||||
offset += childShuffleOrder.length;
|
||||
final childShuffleIndices = audioSourcePlayer.shuffleIndices;
|
||||
childOrders.add(childShuffleIndices.map((i) => i + offset).toList());
|
||||
offset += childShuffleIndices.length;
|
||||
}
|
||||
for (var i = 0; i < childOrders.length; i++) {
|
||||
order.addAll(childOrders[_shuffleOrder[i]]);
|
||||
|
@ -615,21 +609,8 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
|||
return order;
|
||||
}
|
||||
|
||||
@override
|
||||
int shuffle(int treeIndex, int currentIndex) {
|
||||
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;
|
||||
void setShuffleOrder(List<int> shuffleOrder) {
|
||||
_shuffleOrder = shuffleOrder;
|
||||
}
|
||||
|
||||
insertAll(int index, List<AudioSourcePlayer> players) {
|
||||
|
@ -639,8 +620,6 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
|||
_shuffleOrder[i] += players.length;
|
||||
}
|
||||
}
|
||||
_shuffleOrder.addAll(
|
||||
List.generate(players.length, (i) => index + i).toList()..shuffle());
|
||||
}
|
||||
|
||||
removeRange(int start, int end) {
|
||||
|
@ -650,7 +629,6 @@ class ConcatenatingAudioSourcePlayer extends AudioSourcePlayer {
|
|||
_shuffleOrder[i] -= (end - start);
|
||||
}
|
||||
}
|
||||
_shuffleOrder.removeWhere((i) => i >= start && i < end);
|
||||
}
|
||||
|
||||
move(int currentIndex, int newIndex) {
|
||||
|
@ -675,7 +653,7 @@ class ClippingAudioSourcePlayer extends IndexedAudioSourcePlayer {
|
|||
List<IndexedAudioSourcePlayer> get sequence => [this];
|
||||
|
||||
@override
|
||||
List<int> get shuffleOrder => [0];
|
||||
List<int> get shuffleIndices => [0];
|
||||
|
||||
@override
|
||||
Future<Duration> load() async {
|
||||
|
@ -799,22 +777,14 @@ class LoopingAudioSourcePlayer extends AudioSourcePlayer {
|
|||
.toList();
|
||||
|
||||
@override
|
||||
List<int> get shuffleOrder {
|
||||
List<int> get shuffleIndices {
|
||||
final order = <int>[];
|
||||
var offset = order.length;
|
||||
for (var i = 0; i < count; i++) {
|
||||
final childShuffleOrder = audioSourcePlayer.shuffleOrder;
|
||||
final childShuffleOrder = audioSourcePlayer.shuffleIndices;
|
||||
order.addAll(childShuffleOrder.map((i) => i + offset).toList());
|
||||
offset += childShuffleOrder.length;
|
||||
}
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
path: "../just_audio_platform_interface"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.1.1"
|
||||
meta:
|
||||
dependency: "direct main"
|
||||
|
|
|
@ -11,7 +11,10 @@ flutter:
|
|||
fileName: just_audio_web.dart
|
||||
|
||||
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:
|
||||
sdk: flutter
|
||||
flutter_web_plugins:
|
||||
|
|
Loading…
Reference in New Issue