From fbc7ed5202f7d0e752766deefb7ecfb0d38c3130 Mon Sep 17 00:00:00 2001 From: Jalal Date: Thu, 25 Apr 2024 23:49:12 +0300 Subject: [PATCH 1/3] basic audio playing --- lib/components/audio_player.dart | 97 ++++++++++++- lib/main.dart | 3 +- lib/pages/notation.dart | 14 +- lib/pages/transcribe.dart | 229 ++++++++----------------------- 4 files changed, 158 insertions(+), 185 deletions(-) diff --git a/lib/components/audio_player.dart b/lib/components/audio_player.dart index 96a6eb542..3dfa74fbd 100644 --- a/lib/components/audio_player.dart +++ b/lib/components/audio_player.dart @@ -2,7 +2,14 @@ import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; class AudioPlayerButton extends StatefulWidget { - const AudioPlayerButton({super.key}); + final String audioUrl; + final AudioPlayer audioPlayer; + + const AudioPlayerButton({ + super.key, + required this.audioUrl, + required this.audioPlayer, + }); @override State createState() => _AudioPlayerButtonState(); @@ -12,23 +19,51 @@ class _AudioPlayerButtonState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; bool _isPlaying = false; + bool _firstClick = true; + late AudioPlayer _audioPlayer; + final _key = UniqueKey(); @override void initState() { super.initState(); + _audioPlayer = widget.audioPlayer; _animationController = AnimationController( - vsync: this, duration: const Duration(milliseconds: 500)); + vsync: this, + duration: const Duration(milliseconds: 100), + ); + } + + Future playAudioFromUrl(String url) async { + await _audioPlayer.play(UrlSource(url)); } @override Widget build(BuildContext context) { return TextButton( + key: _key, onPressed: () { setState(() { - if (_isPlaying) { - _animationController.reverse(); - } else { + if (_firstClick) { + print(">> first click"); + print(">> ${_audioPlayer.state}"); + playAudioFromUrl(widget.audioUrl); + print(">> ${_audioPlayer.state}"); _animationController.forward(); + _firstClick = false; + } else { + if (_isPlaying) { + print(">> pausing"); + _animationController.reverse(); + print(">> ${_audioPlayer.state}"); + _audioPlayer.pause(); + print(">> ${_audioPlayer.state}"); + } else { + print(">> resuming"); + _animationController.forward(); + print(">> ${_audioPlayer.state}"); + _audioPlayer.resume(); + print(">> ${_audioPlayer.state}"); + } } _isPlaying = !_isPlaying; }); @@ -40,3 +75,55 @@ class _AudioPlayerButtonState extends State ); } } + +// ============================================================================= + +class NewAudioPlayerButton extends StatefulWidget { + final Function changePlayerState; + final String audioLink; + + const NewAudioPlayerButton({ + super.key, + required this.changePlayerState, + required this.audioLink, + }); + + @override + State createState() => _NewAudioPlayerButtonState(); +} + +class _NewAudioPlayerButtonState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + bool _isPlaying = false; + final UniqueKey _key = UniqueKey(); + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 100), + ); + } + + @override + Widget build(BuildContext context) { + return TextButton( + key: _key, + onPressed: () { + if (_isPlaying) { + _animationController.reverse(); + } else { + _animationController.forward(); + } + _isPlaying = !_isPlaying; + widget.changePlayerState(_key, widget.audioLink); + }, + child: AnimatedIcon( + icon: AnimatedIcons.play_pause, + progress: _animationController, + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index fb683839b..8a0f8cc34 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -63,7 +63,8 @@ class _MyHomePageState extends State { ), floatingActionButton: FloatingActionButton( onPressed: () { - showDialog(context: context, builder: (_) => const NewTransDialogue()); + showDialog( + context: context, builder: (_) => const NewTransDialogue()); }, tooltip: 'Increment', child: const Icon(Icons.add), diff --git a/lib/pages/notation.dart b/lib/pages/notation.dart index 04ff9a8d4..464622921 100644 --- a/lib/pages/notation.dart +++ b/lib/pages/notation.dart @@ -1,3 +1,4 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_pdfview/flutter_pdfview.dart'; import 'package:franz/components/audio_player.dart'; @@ -22,7 +23,8 @@ class _SheetMusicViewerScreenState extends State { int currentPage = 0; bool isReady = false; String errorMessage = ''; - String _localFilePath =""; + String _localFilePath = ""; + AudioPlayer _audioPlayer = AudioPlayer(); @override void initState() { @@ -51,7 +53,11 @@ class _SheetMusicViewerScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(widget.link), - AudioPlayerButton(), + AudioPlayerButton( + audioPlayer: _audioPlayer, + audioUrl: + "https://filesamples.com/samples/audio/mp3/sample2.mp3", + ), ], ), Text(errorMessage), @@ -59,8 +65,8 @@ class _SheetMusicViewerScreenState extends State { child: _localFilePath == "" ? const Center(child: CircularProgressIndicator()) : PDFView( - filePath: _localFilePath, - ), + filePath: _localFilePath, + ), ), ], ), diff --git a/lib/pages/transcribe.dart b/lib/pages/transcribe.dart index a46c3219d..773b0672f 100644 --- a/lib/pages/transcribe.dart +++ b/lib/pages/transcribe.dart @@ -1,193 +1,59 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:franz/components/audio_player.dart'; import 'package:franz/pages/notation.dart'; class TranscribeScreen extends StatefulWidget { - const TranscribeScreen({super.key}); + const TranscribeScreen({ + super.key, + }); @override State createState() => _TranscribeScreenState(); } class _TranscribeScreenState extends State { + final AudioPlayer _audioPlayer = AudioPlayer(); + UniqueKey? _currentPlaying = null; + List> info = [ { "title": "weak and powerless", "date": "today", - "link": "https://arxiv.org/pdf/2111.03017v4.pdf" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "assets/1205.2618.pdf" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" + "transcriptionLink": "https://arxiv.org/pdf/2111.03017v4.pdf", + "audioLink": "https://filesamples.com/samples/audio/mp3/sample3.mp3" }, { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" - }, - { - "title": "weak and powerless", - "date": "today", - "link": "https://www.jalal.com" + "title": "nookie", + "date": "yesterday", + "transcriptionLink": "https://arxiv.org/pdf/2111.03017v4.pdf", + "audioLink": "https://filesamples.com/samples/audio/mp3/sample2.mp3" }, ]; + void changePlayer(UniqueKey key, String audioUrl) { + print(">> current id playing: $_currentPlaying"); + print(">> requesting id: $key"); + print(">> requesting url: $audioUrl"); + print(">> current player state: ${_audioPlayer.state.toString()}"); + print("\n"); + setState(() async { + if (key == _currentPlaying) { + if (_audioPlayer.state == PlayerState.playing) { + await _audioPlayer.pause(); + } else if (_audioPlayer.state == PlayerState.paused) { + await _audioPlayer.resume(); + } + } else { + await _audioPlayer.stop(); + await _audioPlayer.setSourceUrl(audioUrl); + await _audioPlayer.setVolume(1); + await _audioPlayer.resume(); + _currentPlaying = key; + } + }); + } + @override Widget build(BuildContext context) { return Padding( @@ -214,7 +80,10 @@ class _TranscribeScreenState extends State { return TransriptionRow( title: info[index]["title"], date: info[index]["date"], - link: info[index]["link"], + transcriptionLink: info[index]["transcriptionLink"], + audioLink: info[index]["audioLink"], + audioPlayer: _audioPlayer, + changePlayerState: changePlayer, ); }, ), @@ -228,17 +97,24 @@ class _TranscribeScreenState extends State { class TransriptionRow extends StatelessWidget { final String title; final String date; - final String link; + final String transcriptionLink; + final String audioLink; + final AudioPlayer audioPlayer; + final Function changePlayerState; const TransriptionRow({ super.key, required this.title, required this.date, - required this.link, + required this.transcriptionLink, + required this.audioLink, + required this.audioPlayer, + required this.changePlayerState, }); @override Widget build(BuildContext context) { + // TODO: make this flex 4 3 1 1 return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -251,14 +127,17 @@ class TransriptionRow extends StatelessWidget { context, MaterialPageRoute( builder: (context) => SheetMusicViewerScreen( - link: link, + link: transcriptionLink, title: title, ), ), ); }, ), - AudioPlayerButton(), + NewAudioPlayerButton( + changePlayerState: changePlayerState, + audioLink: audioLink, + ), ], ); } From 425ffa7f915a080ac3a22bc8eba7e0492c597600 Mon Sep 17 00:00:00 2001 From: Jalal Date: Fri, 26 Apr 2024 00:27:01 +0300 Subject: [PATCH 2/3] play one song at a time with animations at transcribe --- lib/components/audio_player.dart | 15 +++++++- lib/pages/transcribe.dart | 61 ++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/lib/components/audio_player.dart b/lib/components/audio_player.dart index 3dfa74fbd..09a1c09b1 100644 --- a/lib/components/audio_player.dart +++ b/lib/components/audio_player.dart @@ -81,11 +81,13 @@ class _AudioPlayerButtonState extends State class NewAudioPlayerButton extends StatefulWidget { final Function changePlayerState; final String audioLink; + final UniqueKey? playingKey; const NewAudioPlayerButton({ super.key, required this.changePlayerState, required this.audioLink, + required this.playingKey, }); @override @@ -95,7 +97,7 @@ class NewAudioPlayerButton extends StatefulWidget { class _NewAudioPlayerButtonState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; - bool _isPlaying = false; + late bool _isPlaying; final UniqueKey _key = UniqueKey(); @override @@ -105,10 +107,21 @@ class _NewAudioPlayerButtonState extends State vsync: this, duration: const Duration(milliseconds: 100), ); + _isPlaying = widget.playingKey == _key; } @override Widget build(BuildContext context) { + print( + ">>this is button $_key \ncurrent playing is ${widget.playingKey}\n ${widget.playingKey == _key}\ncurrent animation status: ${_animationController.status.toString()}"); + _isPlaying = widget.playingKey == _key; + // needs to be paused but is play -> take to pause + if (_isPlaying && _animationController.status != AnimationStatus.forward) { + _animationController.forward(); + } else if (!_isPlaying && + _animationController.status != AnimationStatus.reverse) { + _animationController.reverse(); + } return TextButton( key: _key, onPressed: () { diff --git a/lib/pages/transcribe.dart b/lib/pages/transcribe.dart index 773b0672f..e4ac90679 100644 --- a/lib/pages/transcribe.dart +++ b/lib/pages/transcribe.dart @@ -14,7 +14,7 @@ class TranscribeScreen extends StatefulWidget { class _TranscribeScreenState extends State { final AudioPlayer _audioPlayer = AudioPlayer(); - UniqueKey? _currentPlaying = null; + UniqueKey? _currentPlaying; List> info = [ { @@ -31,26 +31,55 @@ class _TranscribeScreenState extends State { }, ]; + // void changePlayer(UniqueKey key, String audioUrl) { + // print(">> current id playing: $_currentPlaying"); + // print(">> requesting id: $key"); + // print(">> requesting url: $audioUrl"); + // print(">> current player state: ${_audioPlayer.state.toString()}"); + // print("\n"); + // setState(() async { + // if (key == _currentPlaying) { + // if (_audioPlayer.state == PlayerState.playing) { + // await _audioPlayer.pause(); + // } else if (_audioPlayer.state == PlayerState.paused) { + // await _audioPlayer.resume(); + // } + // } else { + // await _audioPlayer.stop(); + // await _audioPlayer.setSourceUrl(audioUrl); + // await _audioPlayer.setVolume(1); + // await _audioPlayer.resume(); + // _currentPlaying = key; + // } + // }); + // } + void changePlayer(UniqueKey key, String audioUrl) { print(">> current id playing: $_currentPlaying"); print(">> requesting id: $key"); print(">> requesting url: $audioUrl"); print(">> current player state: ${_audioPlayer.state.toString()}"); print("\n"); - setState(() async { - if (key == _currentPlaying) { - if (_audioPlayer.state == PlayerState.playing) { - await _audioPlayer.pause(); - } else if (_audioPlayer.state == PlayerState.paused) { - await _audioPlayer.resume(); - } - } else { - await _audioPlayer.stop(); - await _audioPlayer.setSourceUrl(audioUrl); - await _audioPlayer.setVolume(1); - await _audioPlayer.resume(); - _currentPlaying = key; + + if (key == _currentPlaying) { + if (_audioPlayer.state == PlayerState.playing) { + _audioPlayer.pause(); + } else if (_audioPlayer.state == PlayerState.paused) { + _audioPlayer.resume(); } + } else { + _handleNewAudioSource(key, audioUrl); + } + } + + Future _handleNewAudioSource(UniqueKey key, String audioUrl) async { + await _audioPlayer.stop(); + await _audioPlayer.setSourceUrl(audioUrl); + await _audioPlayer.setVolume(1); + await _audioPlayer.resume(); + + setState(() { + _currentPlaying = key; }); } @@ -84,6 +113,7 @@ class _TranscribeScreenState extends State { audioLink: info[index]["audioLink"], audioPlayer: _audioPlayer, changePlayerState: changePlayer, + currentPlayingKey: _currentPlaying, ); }, ), @@ -101,6 +131,7 @@ class TransriptionRow extends StatelessWidget { final String audioLink; final AudioPlayer audioPlayer; final Function changePlayerState; + final UniqueKey? currentPlayingKey; const TransriptionRow({ super.key, @@ -110,6 +141,7 @@ class TransriptionRow extends StatelessWidget { required this.audioLink, required this.audioPlayer, required this.changePlayerState, + required this.currentPlayingKey, }); @override @@ -137,6 +169,7 @@ class TransriptionRow extends StatelessWidget { NewAudioPlayerButton( changePlayerState: changePlayerState, audioLink: audioLink, + playingKey: currentPlayingKey, ), ], ); From 99a9d845496044631a1cc5f3d6b154817781d8ad Mon Sep 17 00:00:00 2001 From: Jalal Date: Fri, 26 Apr 2024 00:30:06 +0300 Subject: [PATCH 3/3] UI fix for transcription history rows --- lib/pages/transcribe.dart | 42 ++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/pages/transcribe.dart b/lib/pages/transcribe.dart index e4ac90679..e8ee4e846 100644 --- a/lib/pages/transcribe.dart +++ b/lib/pages/transcribe.dart @@ -150,26 +150,32 @@ class TransriptionRow extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(title), - Text(date), - TextButton( - child: const Icon(Icons.file_copy), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SheetMusicViewerScreen( - link: transcriptionLink, - title: title, + Expanded(flex: 5, child: Text(title),), + Expanded(flex: 3, child: Text(date),), + Expanded( + flex: 1, + child: TextButton( + child: const Icon(Icons.file_copy), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SheetMusicViewerScreen( + link: transcriptionLink, + title: title, + ), ), - ), - ); - }, + ); + }, + ), ), - NewAudioPlayerButton( - changePlayerState: changePlayerState, - audioLink: audioLink, - playingKey: currentPlayingKey, + Expanded( + flex: 1, + child: NewAudioPlayerButton( + changePlayerState: changePlayerState, + audioLink: audioLink, + playingKey: currentPlayingKey, + ), ), ], );