import 'package:just_audio/just_audio.dart' as ja; import 'package:rxdart/rxdart.dart'; import '../../domain/entities/loop_mode.dart'; import '../../domain/entities/queue_item.dart'; import '../../domain/entities/shuffle_mode.dart'; import '../models/loop_mode_model.dart'; import '../models/playback_event_model.dart'; import '../models/queue_item_model.dart'; import '../models/song_model.dart'; import 'audio_player_contract.dart'; import 'queue_generator.dart'; class AudioPlayerImpl implements AudioPlayer { AudioPlayerImpl(this._audioPlayer, this._queueGenerator) { _audioPlayer.currentIndexStream.listen((event) { print('currentIndex: $event'); _currentIndexSubject.add(event); if (_queueSubject.value != null) { _currentSongSubject.add(_queueSubject.value[event].song); } }); _audioPlayer.playingStream.listen((event) => _playingSubject.add(event)); _audioPlayer.playbackEventStream.listen((event) { _playbackEventSubject.add(PlaybackEventModel.fromJAPlaybackEvent(event)); }); _audioPlayer.positionStream.listen((event) { _positionSubject.add(event); }); _audioPlayer.loopModeStream.listen((event) { _loopModeSubject.add(event.toEntity()); }); _queueSubject.listen((event) { if (_currentIndexSubject.value != null) { _currentSongSubject.add(event[_currentIndexSubject.value].song); } }); } final ja.AudioPlayer _audioPlayer; ja.ConcatenatingAudioSource _audioSource; final QueueGenerator _queueGenerator; List _inputQueue; List _queue; final BehaviorSubject _currentIndexSubject = BehaviorSubject(); final BehaviorSubject _currentSongSubject = BehaviorSubject(); final BehaviorSubject _playbackEventSubject = BehaviorSubject(); final BehaviorSubject _playingSubject = BehaviorSubject(); final BehaviorSubject _positionSubject = BehaviorSubject(); final BehaviorSubject> _queueSubject = BehaviorSubject(); final BehaviorSubject _shuffleModeSubject = BehaviorSubject.seeded(ShuffleMode.none); final BehaviorSubject _loopModeSubject = BehaviorSubject(); @override ValueStream get currentIndexStream => _currentIndexSubject.stream; @override ValueStream get currentSongStream => _currentSongSubject.stream; @override ValueStream get playbackEventStream => _playbackEventSubject.stream; @override ValueStream get positionStream => _positionSubject.stream; @override ValueStream get playingStream => _playingSubject.stream; @override ValueStream> get queueStream => _queueSubject.stream; @override ValueStream get shuffleModeStream => _shuffleModeSubject.stream; @override ValueStream get loopModeStream => _loopModeSubject.stream; @override Future dispose() async { await _currentIndexSubject.close(); await _currentSongSubject.close(); await _playbackEventSubject.close(); await _positionSubject.close(); await _queueSubject.close(); await _shuffleModeSubject.close(); await _loopModeSubject.close(); await _audioPlayer.dispose(); } @override Future loadQueue({List queue, int startIndex = 0}) async { if (queue == null || queue.isEmpty ) { return; } if (startIndex >= queue.length) { print('$startIndex >= ${queue.length}'); return; } _audioSource = _queueGenerator.songModelsToAudioSource([queue[startIndex].song]); await _audioPlayer.setAudioSource(_audioSource); // await _audioPlayer.load(); final songModelQueue = queue.map((e) => e.song).toList(); _queueSubject.add(queue); final completeAudioSource = _queueGenerator.songModelsToAudioSource(songModelQueue); _audioSource.insertAll(0, completeAudioSource.children.sublist(0, startIndex)); _audioSource.addAll( completeAudioSource.children.sublist(startIndex + 1, completeAudioSource.length), ); } @override Future pause() async { await _audioPlayer.pause(); } @override Future play() async { await _audioPlayer.play(); } @override Future playSongList(List songs, int startIndex) async { _inputQueue = songs; final firstSong = songs[startIndex]; _queueSubject.add([QueueItemModel(firstSong, originalIndex: startIndex)]); _audioSource = _queueGenerator.songModelsToAudioSource([firstSong]); await _audioPlayer.setAudioSource(_audioSource); _audioPlayer.play(); _queue = await _queueGenerator.generateQueue(_shuffleModeSubject.value, songs, startIndex); final songModelQueue = _queue.map((e) => e.song).toList(); _queueSubject.add(_queue); final int splitIndex = _shuffleModeSubject.value == ShuffleMode.none ? startIndex : 0; final newQueue = _queueGenerator.songModelsToAudioSource(songModelQueue); await _audioSource.insertAll(0, newQueue.children.sublist(0, splitIndex)); _audioSource.addAll(newQueue.children.sublist(splitIndex + 1, newQueue.length)); } @override Future seekToNext() async { await _audioPlayer.seekToNext(); } @override Future seekToPrevious() async { await _audioPlayer.seekToPrevious(); } @override Future setIndex(int index) async { await _audioPlayer.seek(const Duration(seconds: 0), index: index); } @override Future stop() async { _audioPlayer.stop(); } @override Future addToQueue(SongModel song) async { await _audioSource.add(ja.AudioSource.uri(Uri.file(song.path))); _queue.add(QueueItemModel(song, originalIndex: -1, type: QueueItemType.added)); _queueSubject.add(_queue); } @override Future moveQueueItem(int oldIndex, int newIndex) async { final QueueItemModel queueItem = _queue.removeAt(oldIndex); final index = newIndex < oldIndex ? newIndex : newIndex - 1; _queue.insert(index, queueItem); _queueSubject.add(_queue); await _audioSource.move(oldIndex, index); } @override Future removeQueueIndex(int index) async { _queue.removeAt(index); _queueSubject.add(_queue); await _audioSource.removeAt(index); } @override Future setShuffleMode(ShuffleMode shuffleMode, bool updateQueue) async { if (shuffleMode == null) return; _shuffleModeSubject.add(shuffleMode); if (updateQueue) { final QueueItem currentQueueItem = _queue[_currentIndexSubject.value]; final int index = currentQueueItem.originalIndex; _queue = await _queueGenerator.generateQueue(shuffleMode, _inputQueue, index); // TODO: maybe refactor _queue to a subject and listen for changes final songModelQueue = _queue.map((e) => e.song).toList(); _queueSubject.add(_queue); final newQueue = _queueGenerator.songModelsToAudioSource(songModelQueue); _updateQueue(newQueue, currentQueueItem); } } @override Future setLoopMode(LoopMode loopMode) async { if (loopMode == null) return; await _audioPlayer.setLoopMode(loopMode.toJA()); } Future _updateQueue(ja.ConcatenatingAudioSource newQueue, QueueItem currentQueueItem) async { final int index = currentQueueItem.originalIndex; _audioSource.removeRange(0, _currentIndexSubject.value); _audioSource.removeRange(1, _audioSource.length); if (_shuffleModeSubject.value == ShuffleMode.none) { switch (currentQueueItem.type) { case QueueItemType.added: case QueueItemType.standard: await _audioSource.insertAll(0, newQueue.children.sublist(0, index)); _audioSource.addAll(newQueue.children.sublist(index + 1)); break; case QueueItemType.predecessor: await _audioSource.insertAll(0, newQueue.children.sublist(0, index)); _audioSource.addAll(newQueue.children.sublist(index)); break; case QueueItemType.successor: await _audioSource.insertAll(0, newQueue.children.sublist(0, index + 1)); _audioSource.addAll(newQueue.children.sublist(index + 1)); break; } _currentIndexSubject.add(index); } else { _audioSource.addAll(newQueue.children.sublist(1)); } } }