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 "JustAudioPlugin.h"
|
||||||
#import "AudioPlayer.h"
|
#import "AudioPlayer.h"
|
||||||
#import "AudioPlayer.h"
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
|
||||||
@implementation JustAudioPlugin {
|
@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
|
- Flutter
|
||||||
- path_provider (0.0.1):
|
- path_provider (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- path_provider_linux (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- path_provider_macos (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- just_audio (from `.symlinks/plugins/just_audio/ios`)
|
- just_audio (from `.symlinks/plugins/just_audio/ios`)
|
||||||
- path_provider (from `.symlinks/plugins/path_provider/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:
|
EXTERNAL SOURCES:
|
||||||
Flutter:
|
Flutter:
|
||||||
|
@ -17,12 +23,18 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/just_audio/ios"
|
:path: ".symlinks/plugins/just_audio/ios"
|
||||||
path_provider:
|
path_provider:
|
||||||
:path: ".symlinks/plugins/path_provider/ios"
|
: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:
|
SPEC CHECKSUMS:
|
||||||
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
|
||||||
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
|
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
|
||||||
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
|
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||||
|
path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4
|
||||||
|
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
|
||||||
|
|
||||||
PODFILE CHECKSUM: f32fb4e7c14f8b3ca19a369d7be425dd9241af27
|
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:just_audio/just_audio.dart';
|
||||||
import 'package:rxdart/rxdart.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(
|
AudioSource.uri(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
"https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
|
"https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
|
||||||
|
@ -296,7 +318,8 @@ class _SeekBarState extends State<SeekBar> {
|
||||||
return Slider(
|
return Slider(
|
||||||
min: 0.0,
|
min: 0.0,
|
||||||
max: widget.duration.inMilliseconds.toDouble(),
|
max: widget.duration.inMilliseconds.toDouble(),
|
||||||
value: _dragValue ?? widget.position.inMilliseconds.toDouble(),
|
value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(),
|
||||||
|
widget.duration.inMilliseconds.toDouble()),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_dragValue = value;
|
_dragValue = value;
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
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:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -22,13 +36,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.3"
|
||||||
clock:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: clock
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -57,13 +64,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.1.3"
|
||||||
fake_async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fake_async
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -86,6 +86,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.12"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -120,7 +127,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.6.4"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -149,6 +156,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -170,6 +184,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.13"
|
version: "3.0.13"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -223,7 +244,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.16"
|
version: "0.2.15"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -252,6 +273,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.6.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.7.0 <3.0.0"
|
dart: ">=2.6.0 <3.0.0"
|
||||||
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
flutter: ">=1.12.13+hotfix.5 <2.0.0"
|
||||||
|
|
|
@ -14,3 +14,9 @@ enum PlaybackState {
|
||||||
connecting,
|
connecting,
|
||||||
completed
|
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,6 +136,8 @@ class AudioPlayer {
|
||||||
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
_eventChannelStream = EventChannel('com.ryanheise.just_audio.events.$_id')
|
||||||
.receiveBroadcastStream()
|
.receiveBroadcastStream()
|
||||||
.map((data) {
|
.map((data) {
|
||||||
|
try {
|
||||||
|
//print("received raw event: $data");
|
||||||
final duration = (data['duration'] ?? -1) < 0
|
final duration = (data['duration'] ?? -1) < 0
|
||||||
? null
|
? null
|
||||||
: Duration(milliseconds: data['duration']);
|
: Duration(milliseconds: data['duration']);
|
||||||
|
@ -154,7 +156,13 @@ class AudioPlayer {
|
||||||
: IcyMetadata.fromJson(data['icyMetadata']),
|
: IcyMetadata.fromJson(data['icyMetadata']),
|
||||||
currentIndex: data['currentIndex'],
|
currentIndex: data['currentIndex'],
|
||||||
);
|
);
|
||||||
|
//print("created event object with state: ${_audioPlaybackEvent.state}");
|
||||||
return _audioPlaybackEvent;
|
return _audioPlaybackEvent;
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
print("Error parsing event: $e");
|
||||||
|
print("$stacktrace");
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
_eventChannelStreamSubscription = _eventChannelStream.listen(
|
_eventChannelStreamSubscription = _eventChannelStream.listen(
|
||||||
_playbackEventSubject.add,
|
_playbackEventSubject.add,
|
||||||
|
|
|
@ -77,6 +77,8 @@ abstract class JustAudioPlayer {
|
||||||
return await setLoopMode(args[0]);
|
return await setLoopMode(args[0]);
|
||||||
case 'setShuffleModeEnabled':
|
case 'setShuffleModeEnabled':
|
||||||
return await setShuffleModeEnabled(args[0]);
|
return await setShuffleModeEnabled(args[0]);
|
||||||
|
case 'setAutomaticallyWaitsToMinimizeStalling':
|
||||||
|
return null;
|
||||||
case 'seek':
|
case 'seek':
|
||||||
return await seek(args[0], args[1]);
|
return await seek(args[0], args[1]);
|
||||||
case 'dispose':
|
case 'dispose':
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
@interface AudioPlayer : NSObject<FlutterStreamHandler>
|
@interface AudioPlayer : NSObject<FlutterStreamHandler>
|
||||||
|
|
||||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam;
|
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar playerId:(NSString*)idParam configuredSession:(BOOL)configuredSession;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -14,3 +14,9 @@ enum PlaybackState {
|
||||||
connecting,
|
connecting,
|
||||||
completed
|
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