Created
November 20, 2021 15:24
-
-
Save rutvik110/a8cc0f2d4f97bff6e16ae18c55250136 to your computer and use it in GitHub Desktop.
Audio Track Visualizer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
import 'dart:developer'; | |
import 'dart:math' as math; | |
import 'dart:ui'; | |
import 'package:audioplayers/audioplayers.dart'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:flutter/widgets.dart'; | |
List<double> loadparseJson(String jsonBody) { | |
final data = jsonDecode(jsonBody); | |
final List<int> points = List.castFrom<dynamic, int>(data['data']); | |
List<int> filteredData = []; | |
const int samples = 2123; | |
final double blockSize = points.length / samples; | |
log(points.length.toString()); | |
log(blockSize.toString()); | |
for (int i = 0; i < samples; i++) { | |
final double blockStart = | |
blockSize * i; // the location of the first sample in the block | |
int sum = 0; | |
for (int j = 0; j < blockSize; j++) { | |
sum = sum + | |
points[(blockStart + j) | |
.toInt()]; // find the sum of all the samples in the block | |
} | |
filteredData.add((sum / blockSize) | |
.round() // take the average of the block and add it to the filtered data | |
.toInt()); // divide the sum by the block size to get the average | |
} | |
final maxNum = filteredData.reduce(math.max); | |
log(maxNum.toString()); | |
final double multiplier = math.pow(maxNum, -1).toDouble(); | |
log(multiplier.toString()); | |
filteredData.add(0); | |
return filteredData.map<double>((e) => (e * multiplier)).toList(); | |
} | |
class AudioVisualizer extends StatefulWidget { | |
const AudioVisualizer({Key? key}) : super(key: key); | |
@override | |
State<AudioVisualizer> createState() => _AudioVisualizerState(); | |
} | |
class _AudioVisualizerState extends State<AudioVisualizer> { | |
late List<double> data; | |
late AudioCache audioPlayer; | |
double sliderValue = 0; | |
int maxDuration = 1; | |
int xAudio = 0; | |
Future<void> parseData() async { | |
final jsonString = await rootBundle.loadString('assets/audio_data.json'); | |
final dataPoints = await compute(loadparseJson, jsonString); | |
await audioPlayer.load('/audio.mp3'); | |
await audioPlayer.play('/audio.mp3'); | |
maxDuration = await audioPlayer.fixedPlayer!.getDuration(); | |
setState(() { | |
data = dataPoints; | |
}); | |
} | |
@override | |
void initState() { | |
// TODO: implement initState | |
super.initState(); | |
audioPlayer = AudioCache( | |
fixedPlayer: AudioPlayer(), | |
); | |
data = []; | |
parseData(); | |
audioPlayer.fixedPlayer!.onAudioPositionChanged.listen((Duration p) { | |
setState(() { | |
sliderValue = | |
double.parse((p.inMilliseconds / maxDuration).toStringAsFixed(20)); | |
xAudio = ((data.length - 1) * sliderValue).toInt(); | |
log(xAudio.toString()); | |
}); | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
backgroundColor: Colors.black, | |
appBar: AppBar( | |
backgroundColor: Colors.red, | |
title: const Text('Audio Visualizer'), | |
), | |
body: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
crossAxisAlignment: CrossAxisAlignment.center, | |
children: [ | |
Stack( | |
children: [ | |
CustomPaint( | |
size: const Size(400, 100), | |
painter: | |
AudioVisualizerPainter(samples: data, sliderValue: xAudio), | |
), | |
CustomPaint( | |
size: const Size(400, 100), | |
painter: ActiveTrackPainter(samples: data, sliderValue: xAudio), | |
), | |
], | |
), | |
Slider( | |
value: sliderValue.toDouble(), | |
min: 0, | |
activeColor: Colors.red, | |
max: 1, | |
onChanged: (val) { | |
setState(() { | |
sliderValue = val; | |
audioPlayer.fixedPlayer!.seek(Duration( | |
milliseconds: (maxDuration * sliderValue).toInt())); | |
}); | |
}, | |
), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
RaisedButton( | |
child: Text('Play'), | |
onPressed: () async { | |
await audioPlayer.play("/audio.mp3"); | |
}, | |
), | |
RaisedButton( | |
child: Text('Pause'), | |
onPressed: () { | |
audioPlayer.fixedPlayer!.pause(); | |
}, | |
), | |
RaisedButton( | |
child: Text('Stop'), | |
onPressed: () { | |
audioPlayer.fixedPlayer!.stop(); | |
}, | |
), | |
], | |
), | |
], | |
), | |
); | |
} | |
} | |
class ActiveTrackPainter extends CustomPainter { | |
ActiveTrackPainter({ | |
required this.samples, | |
required this.sliderValue, | |
}); | |
final List<double> samples; | |
final int sliderValue; | |
@override | |
void paint(Canvas canvas, Size size) { | |
final activeTrackPaint = Paint() | |
..style = PaintingStyle.fill | |
..strokeWidth = 10.0 | |
..color = Colors.red.withOpacity(0.5); | |
final activePaint = Paint() | |
..style = PaintingStyle.fill | |
..strokeWidth = 5.0 | |
..color = Colors.red; | |
final path = Path(); | |
final double ax = (size.width / samples.length) * sliderValue; | |
final double ay = samples[sliderValue] * size.height; | |
path.moveTo(0, size.height); | |
path.lineTo(0, size.height); | |
path.lineTo(0, -size.height); | |
path.lineTo(ax, -size.height); | |
path.lineTo(ax, size.height); | |
path.close(); | |
canvas.drawPath(path, activeTrackPaint); | |
// canvas.drawCircle(Offset(ax, ay), 10, activePaint); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
return true; | |
} | |
} | |
class AudioVisualizerPainter extends CustomPainter { | |
AudioVisualizerPainter({ | |
required this.samples, | |
required this.sliderValue, | |
}); | |
final List<double> samples; | |
final int sliderValue; | |
@override | |
void paint(Canvas canvas, Size size) { | |
final paint = Paint() | |
..style = PaintingStyle.stroke | |
..strokeWidth = 1.0 | |
..shader = LinearGradient( | |
begin: Alignment.centerLeft, | |
end: Alignment.centerRight, | |
transform: GradientRotation(math.pi / 2), | |
colors: [ | |
Colors.blueAccent[700]!, | |
Colors.red, | |
], | |
stops: [ | |
0.3, | |
0.5, | |
]).createShader( | |
Rect.fromLTWH(0, 0, size.width, size.height), | |
); | |
List<Offset> offsets = []; | |
for (var i = 0; i < samples.length; i++) { | |
final double width = size.width / samples.length; | |
final double x = width * i; | |
final double y = samples[i] * size.height; | |
offsets.add(Offset(x, y)); | |
} | |
canvas.drawPoints(PointMode.polygon, offsets, paint); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
// TODO: implement shouldRepaint | |
return samples.isEmpty; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment