Playlists, looping, shuffling for iOS
This commit is contained in:
parent
c0c5d0c2bf
commit
a63ef2ba39
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
|||
#import "AudioSource.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation AudioSource {
|
||||
NSString *_sourceId;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid {
|
||||
self = [super init];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_sourceId = sid;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)sourceId {
|
||||
return _sourceId;
|
||||
}
|
||||
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches {
|
||||
if ([_sourceId isEqualToString:sourceId]) {
|
||||
[matches addObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)getShuffleOrder {
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,67 @@
|
|||
#import "AudioSource.h"
|
||||
#import "ClippingAudioSource.h"
|
||||
#import "IndexedPlayerItem.h"
|
||||
#import "UriAudioSource.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation ClippingAudioSource {
|
||||
UriAudioSource *_audioSource;
|
||||
CMTime _start;
|
||||
CMTime _end;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSource:(UriAudioSource *)audioSource start:(NSNumber *)start end:(NSNumber *)end {
|
||||
self = [super initWithId:sid];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_audioSource = audioSource;
|
||||
_start = start == [NSNull null] ? kCMTimeZero : CMTimeMake([start intValue], 1000);
|
||||
_end = end == [NSNull null] ? kCMTimeInvalid : CMTimeMake([end intValue], 1000);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches {
|
||||
[super findById:sourceId matches:matches];
|
||||
[_audioSource findById:sourceId matches:matches];
|
||||
}
|
||||
|
||||
- (void)attach:(AVQueuePlayer *)player {
|
||||
[super attach:player];
|
||||
_audioSource.playerItem.forwardPlaybackEndTime = _end;
|
||||
// XXX: Not needed since currentItem observer handles it?
|
||||
[self seek:kCMTimeZero];
|
||||
}
|
||||
|
||||
- (IndexedPlayerItem *)playerItem {
|
||||
return _audioSource.playerItem;
|
||||
}
|
||||
|
||||
- (NSArray *)getShuffleOrder {
|
||||
return @[@(0)];
|
||||
}
|
||||
|
||||
- (void)play:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)pause:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)stop:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)seek:(CMTime)position completionHandler:(void (^)(BOOL))completionHandler {
|
||||
CMTime absPosition = CMTimeAdd(_start, position);
|
||||
[_audioSource.playerItem seekToTime:absPosition toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (CMTime)duration {
|
||||
return CMTimeSubtract(CMTIME_IS_INVALID(_end) ? self.playerItem.duration : _end, _start);
|
||||
}
|
||||
|
||||
- (void)setDuration:(CMTime)duration {
|
||||
}
|
||||
|
||||
- (CMTime)position {
|
||||
return CMTimeSubtract(self.playerItem.currentTime, _start);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,109 @@
|
|||
#import "AudioSource.h"
|
||||
#import "ConcatenatingAudioSource.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <stdlib.h>
|
||||
|
||||
@implementation ConcatenatingAudioSource {
|
||||
NSMutableArray<AudioSource *> *_audioSources;
|
||||
NSMutableArray<NSNumber *> *_shuffleOrder;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources {
|
||||
self = [super initWithId:sid];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_audioSources = audioSources;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)count {
|
||||
return _audioSources.count;
|
||||
}
|
||||
|
||||
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index {
|
||||
[_audioSources insertObject:audioSource atIndex:index];
|
||||
}
|
||||
|
||||
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end {
|
||||
if (end == -1) end = _audioSources.count;
|
||||
for (int i = start; i < end; i++) {
|
||||
[_audioSources removeObjectAtIndex:start];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex {
|
||||
AudioSource *source = _audioSources[currentIndex];
|
||||
[_audioSources removeObjectAtIndex:currentIndex];
|
||||
[_audioSources insertObject:source atIndex:newIndex];
|
||||
}
|
||||
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex {
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
treeIndex = [_audioSources[i] buildSequence:sequence treeIndex:treeIndex];
|
||||
}
|
||||
return treeIndex;
|
||||
}
|
||||
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches {
|
||||
[super findById:sourceId matches:matches];
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
[_audioSources[i] findById:sourceId matches:matches];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)getShuffleOrder {
|
||||
NSMutableArray *order = [NSMutableArray new];
|
||||
int offset = [order count];
|
||||
NSMutableArray *childOrders = [NSMutableArray new]; // array of array of ints
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
AudioSource *audioSource = _audioSources[i];
|
||||
NSArray *childShuffleOrder = [audioSource getShuffleOrder];
|
||||
NSMutableArray *offsetChildShuffleOrder = [NSMutableArray new];
|
||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
||||
[offsetChildShuffleOrder addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
||||
}
|
||||
[childOrders addObject:offsetChildShuffleOrder];
|
||||
offset += [childShuffleOrder count];
|
||||
}
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
[order addObjectsFromArray:childOrders[[_shuffleOrder[i] integerValue]]];
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
int currentChildIndex = -1;
|
||||
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 {}
|
||||
}
|
||||
// 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", [_audioSources count], [_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
|
|
@ -0,0 +1,64 @@
|
|||
#import "IndexedAudioSource.h"
|
||||
#import "IndexedPlayerItem.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation IndexedAudioSource {
|
||||
BOOL _isAttached;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid {
|
||||
self = [super init];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_isAttached = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IndexedPlayerItem *)playerItem {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isAttached {
|
||||
return _isAttached;
|
||||
}
|
||||
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex {
|
||||
[sequence addObject:self];
|
||||
return treeIndex + 1;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
return treeIndex + 1;
|
||||
}
|
||||
|
||||
- (void)attach:(AVQueuePlayer *)player {
|
||||
_isAttached = YES;
|
||||
}
|
||||
|
||||
- (void)play:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)pause:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)stop:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)seek:(CMTime)position {
|
||||
[self seek:position completionHandler:nil];
|
||||
}
|
||||
|
||||
- (void)seek:(CMTime)position completionHandler:(void (^)(BOOL))completionHandler {
|
||||
}
|
||||
|
||||
- (CMTime)duration {
|
||||
return kCMTimeInvalid;
|
||||
}
|
||||
|
||||
- (void)setDuration:(CMTime)duration {
|
||||
}
|
||||
|
||||
- (CMTime)position {
|
||||
return kCMTimeInvalid;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,16 @@
|
|||
#import "IndexedPlayerItem.h"
|
||||
#import "IndexedAudioSource.h"
|
||||
|
||||
@implementation IndexedPlayerItem {
|
||||
IndexedAudioSource *_audioSource;
|
||||
}
|
||||
|
||||
-(void)setAudioSource:(IndexedAudioSource *)audioSource {
|
||||
_audioSource = audioSource;
|
||||
}
|
||||
|
||||
-(IndexedAudioSource *)audioSource {
|
||||
return _audioSource;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +1,5 @@
|
|||
#import "JustAudioPlugin.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation JustAudioPlugin {
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#import "AudioSource.h"
|
||||
#import "LoopingAudioSource.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation LoopingAudioSource {
|
||||
// An array of duplicates
|
||||
NSArray<AudioSource *> *_audioSources; // <AudioSource *>
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSArray<AudioSource *> *)audioSources {
|
||||
self = [super initWithId:sid];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_audioSources = audioSources;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex {
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
treeIndex = [_audioSources[i] buildSequence:sequence treeIndex:treeIndex];
|
||||
}
|
||||
return treeIndex;
|
||||
}
|
||||
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches {
|
||||
[super findById:sourceId matches:matches];
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
[_audioSources[i] findById:sourceId matches:matches];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)getShuffleOrder {
|
||||
NSMutableArray *order = [NSMutableArray new];
|
||||
int offset = [order count];
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
AudioSource *audioSource = _audioSources[i];
|
||||
NSArray *childShuffleOrder = [audioSource getShuffleOrder];
|
||||
for (int j = 0; j < [childShuffleOrder count]; j++) {
|
||||
[order addObject:@([childShuffleOrder[j] integerValue] + offset)];
|
||||
}
|
||||
offset += [childShuffleOrder count];
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex {
|
||||
// TODO: This should probably shuffle the same way on all duplicates.
|
||||
for (int i = 0; i < [_audioSources count]; i++) {
|
||||
treeIndex = [_audioSources[i] shuffle:treeIndex currentIndex:currentIndex];
|
||||
}
|
||||
return treeIndex;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,66 @@
|
|||
#import "UriAudioSource.h"
|
||||
#import "IndexedAudioSource.h"
|
||||
#import "IndexedPlayerItem.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation UriAudioSource {
|
||||
NSString *_uri;
|
||||
IndexedPlayerItem *_playerItem;
|
||||
/* CMTime _duration; */
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid uri:(NSString *)uri {
|
||||
self = [super initWithId:sid];
|
||||
NSAssert(self, @"super init cannot be nil");
|
||||
_uri = uri;
|
||||
if ([_uri hasPrefix:@"file://"]) {
|
||||
_playerItem = [[IndexedPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:[_uri substringFromIndex:7]]];
|
||||
} else {
|
||||
_playerItem = [[IndexedPlayerItem alloc] initWithURL:[NSURL URLWithString:_uri]];
|
||||
}
|
||||
if (@available(macOS 10.13, iOS 11.0, *)) {
|
||||
// This does the best at reducing distortion on voice with speeds below 1.0
|
||||
_playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmTimeDomain;
|
||||
}
|
||||
/* NSKeyValueObservingOptions options = */
|
||||
/* NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew; */
|
||||
/* [_playerItem addObserver:self */
|
||||
/* forKeyPath:@"duration" */
|
||||
/* options:options */
|
||||
/* context:nil]; */
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IndexedPlayerItem *)playerItem {
|
||||
return _playerItem;
|
||||
}
|
||||
|
||||
- (NSArray *)getShuffleOrder {
|
||||
return @[@(0)];
|
||||
}
|
||||
|
||||
- (void)play:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)pause:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)stop:(AVQueuePlayer *)player {
|
||||
}
|
||||
|
||||
- (void)seek:(CMTime)position completionHandler:(void (^)(BOOL))completionHandler {
|
||||
[_playerItem seekToTime:position toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (CMTime)duration {
|
||||
return _playerItem.duration;
|
||||
}
|
||||
|
||||
- (void)setDuration:(CMTime)duration {
|
||||
}
|
||||
|
||||
- (CMTime)position {
|
||||
return _playerItem.currentTime;
|
||||
}
|
||||
|
||||
@end
|
|
@ -4,11 +4,17 @@ PODS:
|
|||
- Flutter
|
||||
- path_provider (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_linux (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_macos (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- just_audio (from `.symlinks/plugins/just_audio/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`)
|
||||
- path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
|
@ -17,12 +23,18 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/just_audio/ios"
|
||||
path_provider:
|
||||
:path: ".symlinks/plugins/path_provider/ios"
|
||||
path_provider_linux:
|
||||
:path: ".symlinks/plugins/path_provider_linux/ios"
|
||||
path_provider_macos:
|
||||
:path: ".symlinks/plugins/path_provider_macos/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
||||
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
|
||||
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4
|
||||
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
|
||||
|
||||
PODFILE CHECKSUM: f32fb4e7c14f8b3ca19a369d7be425dd9241af27
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
COCOAPODS: 1.9.3
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
|
@ -28,6 +29,27 @@ class _MyAppState extends State<MyApp> {
|
|||
),
|
||||
),
|
||||
),
|
||||
//LoopingAudioSource(
|
||||
// count: 2,
|
||||
// audioSource: AudioSource.uri(
|
||||
// Uri.parse(
|
||||
// "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
|
||||
// tag: AudioMetadata(
|
||||
// album: "Science Friday",
|
||||
// title: "A Salute To Head-Scratching Science (full)",
|
||||
// ),
|
||||
// ),
|
||||
//),
|
||||
//ClippingAudioSource(
|
||||
// start: Duration(seconds: 60),
|
||||
// end: Duration(seconds: 65),
|
||||
// audioSource: AudioSource.uri(Uri.parse(
|
||||
// "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3")),
|
||||
// tag: AudioMetadata(
|
||||
// album: "Science Friday",
|
||||
// title: "A Salute To Head-Scratching Science (5 seconds)",
|
||||
// ),
|
||||
//),
|
||||
AudioSource.uri(
|
||||
Uri.parse(
|
||||
"https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
|
||||
|
@ -296,7 +318,8 @@ class _SeekBarState extends State<SeekBar> {
|
|||
return Slider(
|
||||
min: 0.0,
|
||||
max: widget.duration.inMilliseconds.toDouble(),
|
||||
value: _dragValue ?? widget.position.inMilliseconds.toDouble(),
|
||||
value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(),
|
||||
widget.duration.inMilliseconds.toDouble()),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_dragValue = value;
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.13"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -22,13 +36,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -57,13 +64,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -86,6 +86,13 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.12"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -120,7 +127,7 @@ packages:
|
|||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.6.4"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -149,6 +156,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -170,6 +184,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.13"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -223,7 +244,7 @@ packages:
|
|||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.16"
|
||||
version: "0.2.15"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -252,6 +273,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
sdks:
|
||||
dart: ">=2.7.0 <3.0.0"
|
||||
dart: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
||||
|
|
|
@ -14,3 +14,9 @@ enum PlaybackState {
|
|||
connecting,
|
||||
completed
|
||||
};
|
||||
|
||||
enum LoopMode {
|
||||
loopOff,
|
||||
loopOne,
|
||||
loopAll
|
||||
};
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface AudioSource : NSObject
|
||||
|
||||
@property (readonly, nonatomic) NSString* sourceId;
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid;
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||
- (NSArray *)getShuffleOrder;
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/AudioSource.m
|
|
@ -0,0 +1,9 @@
|
|||
#import "AudioSource.h"
|
||||
#import "UriAudioSource.h"
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface ClippingAudioSource : IndexedAudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSource:(UriAudioSource *)audioSource start:(NSNumber *)start end:(NSNumber *)end;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/ClippingAudioSource.m
|
|
@ -0,0 +1,13 @@
|
|||
#import "AudioSource.h"
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface ConcatenatingAudioSource : AudioSource
|
||||
|
||||
@property (readonly, nonatomic) int count;
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources;
|
||||
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
||||
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/ConcatenatingAudioSource.m
|
|
@ -0,0 +1,20 @@
|
|||
#import "AudioSource.h"
|
||||
#import "IndexedPlayerItem.h"
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface IndexedAudioSource : AudioSource
|
||||
|
||||
@property (readonly, nonatomic) IndexedPlayerItem *playerItem;
|
||||
@property (readwrite, nonatomic) CMTime duration;
|
||||
@property (readonly, nonatomic) CMTime position;
|
||||
@property (readonly, nonatomic) BOOL isAttached;
|
||||
|
||||
- (void)attach:(AVQueuePlayer *)player;
|
||||
- (void)play:(AVQueuePlayer *)player;
|
||||
- (void)pause:(AVQueuePlayer *)player;
|
||||
- (void)stop:(AVQueuePlayer *)player;
|
||||
- (void)seek:(CMTime)position;
|
||||
- (void)seek:(CMTime)position completionHandler:(void (^)(BOOL))completionHandler;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/IndexedAudioSource.m
|
|
@ -0,0 +1,9 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@class IndexedAudioSource;
|
||||
|
||||
@interface IndexedPlayerItem : AVPlayerItem
|
||||
|
||||
@property (readwrite, nonatomic) IndexedAudioSource *audioSource;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/IndexedPlayerItem.m
|
|
@ -0,0 +1,8 @@
|
|||
#import "AudioSource.h"
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface LoopingAudioSource : AudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSArray<AudioSource *> *)audioSources;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/LoopingAudioSource.m
|
|
@ -0,0 +1,8 @@
|
|||
#import "IndexedAudioSource.h"
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface UriAudioSource : IndexedAudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid uri:(NSString *)uri;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/UriAudioSource.m
|
|
@ -136,25 +136,33 @@ class AudioPlayer {
|
|||
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
||||
.receiveBroadcastStream()
|
||||
.map((data) {
|
||||
final duration = (data['duration'] ?? -1) < 0
|
||||
? null
|
||||
: Duration(milliseconds: data['duration']);
|
||||
_durationFuture = Future.value(duration);
|
||||
_durationSubject.add(duration);
|
||||
_audioPlaybackEvent = AudioPlaybackEvent(
|
||||
state: AudioPlaybackState.values[data['state']],
|
||||
buffering: data['buffering'],
|
||||
updatePosition: Duration(milliseconds: data['updatePosition']),
|
||||
updateTime: Duration(milliseconds: data['updateTime']),
|
||||
bufferedPosition: Duration(milliseconds: data['bufferedPosition']),
|
||||
speed: _speed,
|
||||
duration: duration,
|
||||
icyMetadata: data['icyMetadata'] == null
|
||||
try {
|
||||
//print("received raw event: $data");
|
||||
final duration = (data['duration'] ?? -1) < 0
|
||||
? null
|
||||
: IcyMetadata.fromJson(data['icyMetadata']),
|
||||
currentIndex: data['currentIndex'],
|
||||
);
|
||||
return _audioPlaybackEvent;
|
||||
: Duration(milliseconds: data['duration']);
|
||||
_durationFuture = Future.value(duration);
|
||||
_durationSubject.add(duration);
|
||||
_audioPlaybackEvent = AudioPlaybackEvent(
|
||||
state: AudioPlaybackState.values[data['state']],
|
||||
buffering: data['buffering'],
|
||||
updatePosition: Duration(milliseconds: data['updatePosition']),
|
||||
updateTime: Duration(milliseconds: data['updateTime']),
|
||||
bufferedPosition: Duration(milliseconds: data['bufferedPosition']),
|
||||
speed: _speed,
|
||||
duration: duration,
|
||||
icyMetadata: data['icyMetadata'] == null
|
||||
? null
|
||||
: IcyMetadata.fromJson(data['icyMetadata']),
|
||||
currentIndex: data['currentIndex'],
|
||||
);
|
||||
//print("created event object with state: ${_audioPlaybackEvent.state}");
|
||||
return _audioPlaybackEvent;
|
||||
} catch (e, stacktrace) {
|
||||
print("Error parsing event: $e");
|
||||
print("$stacktrace");
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
_eventChannelStreamSubscription = _eventChannelStream.listen(
|
||||
_playbackEventSubject.add,
|
||||
|
|
|
@ -77,6 +77,8 @@ abstract class JustAudioPlayer {
|
|||
return await setLoopMode(args[0]);
|
||||
case 'setShuffleModeEnabled':
|
||||
return await setShuffleModeEnabled(args[0]);
|
||||
case 'setAutomaticallyWaitsToMinimizeStalling':
|
||||
return null;
|
||||
case 'seek':
|
||||
return await seek(args[0], args[1]);
|
||||
case 'dispose':
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
@interface AudioPlayer : NSObject<FlutterStreamHandler>
|
||||
|
||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam;
|
||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam configuredSession:(BOOL)configuredSession;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -14,3 +14,9 @@ enum PlaybackState {
|
|||
connecting,
|
||||
completed
|
||||
};
|
||||
|
||||
enum LoopMode {
|
||||
loopOff,
|
||||
loopOne,
|
||||
loopAll
|
||||
};
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
|
||||
@interface AudioSource : NSObject
|
||||
|
||||
@property (readonly, nonatomic) NSString* sourceId;
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid;
|
||||
- (int)buildSequence:(NSMutableArray *)sequence treeIndex:(int)treeIndex;
|
||||
- (void)findById:(NSString *)sourceId matches:(NSMutableArray<AudioSource *> *)matches;
|
||||
- (NSArray *)getShuffleOrder;
|
||||
- (int)shuffle:(int)treeIndex currentIndex:(int)currentIndex;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/AudioSource.m
|
|
@ -0,0 +1,9 @@
|
|||
#import "AudioSource.h"
|
||||
#import "UriAudioSource.h"
|
||||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
|
||||
@interface ClippingAudioSource : IndexedAudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSource:(UriAudioSource *)audioSource start:(NSNumber *)start end:(NSNumber *)end;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/ClippingAudioSource.m
|
|
@ -0,0 +1,13 @@
|
|||
#import "AudioSource.h"
|
||||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
|
||||
@interface ConcatenatingAudioSource : AudioSource
|
||||
|
||||
@property (readonly, nonatomic) int count;
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSMutableArray<AudioSource *> *)audioSources;
|
||||
- (void)insertSource:(AudioSource *)audioSource atIndex:(int)index;
|
||||
- (void)removeSourcesFromIndex:(int)start toIndex:(int)end;
|
||||
- (void)moveSourceFromIndex:(int)currentIndex toIndex:(int)newIndex;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/ConcatenatingAudioSource.m
|
|
@ -0,0 +1,20 @@
|
|||
#import "AudioSource.h"
|
||||
#import "IndexedPlayerItem.h"
|
||||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface IndexedAudioSource : AudioSource
|
||||
|
||||
@property (readonly, nonatomic) IndexedPlayerItem *playerItem;
|
||||
@property (readwrite, nonatomic) CMTime duration;
|
||||
@property (readonly, nonatomic) CMTime position;
|
||||
@property (readonly, nonatomic) BOOL isAttached;
|
||||
|
||||
- (void)attach:(AVQueuePlayer *)player;
|
||||
- (void)play:(AVQueuePlayer *)player;
|
||||
- (void)pause:(AVQueuePlayer *)player;
|
||||
- (void)stop:(AVQueuePlayer *)player;
|
||||
- (void)seek:(CMTime)position;
|
||||
- (void)seek:(CMTime)position completionHandler:(void (^)(BOOL))completionHandler;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/IndexedAudioSource.m
|
|
@ -0,0 +1,9 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@class IndexedAudioSource;
|
||||
|
||||
@interface IndexedPlayerItem : AVPlayerItem
|
||||
|
||||
@property (readwrite, nonatomic) IndexedAudioSource *audioSource;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/IndexedPlayerItem.m
|
|
@ -0,0 +1,8 @@
|
|||
#import "AudioSource.h"
|
||||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
|
||||
@interface LoopingAudioSource : AudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid audioSources:(NSArray<AudioSource *> *)audioSources;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/LoopingAudioSource.m
|
|
@ -0,0 +1,8 @@
|
|||
#import "IndexedAudioSource.h"
|
||||
#import <FlutterMacOS/FlutterMacOS.h>
|
||||
|
||||
@interface UriAudioSource : IndexedAudioSource
|
||||
|
||||
- (instancetype)initWithId:(NSString *)sid uri:(NSString *)uri;
|
||||
|
||||
@end
|
|
@ -0,0 +1 @@
|
|||
../../darwin/Classes/UriAudioSource.m
|
Loading…
Reference in New Issue