I am building an app that uses DartMeltySoundFont as a synthesizer and raw_sounds to play the sounds.
I init the synthesizer like this:
Synthesizer synthesizer = Synthesizer.loadByteData(
bytes,
SynthesizerSettings(
sampleRate: 44100,
blockSize: 980,
maximumPolyphony: 64,
enableReverbAndChorus: false,
));
Then I add a block with some MIDI instructions and the sound plays.
But I don't know how long it plays.
How can I calculate how long it plays in seconds given the variables sampleRate, blockSize and amount of blocks added?
Full example app:
import 'dart:typed_data'; // for Uint8List
import 'package:flutter/material.dart';
import 'package:raw_sound/raw_sound_player.dart';
import 'package:dart_melty_soundfont/dart_melty_soundfont.dart';
import 'package:flutter/services.dart' show rootBundle;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// Player instance to play raw PCM (16-bit integer) audio data
final _playerPCMF32 = RawSoundPlayer();
bool _playing = false;
@override
void initState() {
super.initState();
// release any initialized player instances
_playerPCMF32
.initialize(
sampleRate: 44100,
pcmType: RawSoundPCMType.PCMF32,
)
.then((value) {
setState(() {
// Trigger rebuild to update UI
});
});
}
@override
void dispose() {
// Finally release any initialized player instances
_playerPCMF32.release();
super.dispose();
}
Widget build(BuildContext context) {
debugPrint('_playerPCMF32 is inited? ${_playerPCMF32.isInited}');
if (!_playerPCMF32.isInited) {
return Container();
}
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.grey,
),
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Raw Sound Plugin Example App'),
),
body: Column(
children: [
Card(
child: Row(
children: [
IconButton(
icon: Icon(_playerPCMF32.isPlaying ? Icons.stop : Icons.play_arrow),
onPressed: () {
_playerPCMF32.isPlaying ? _pausePCMF32() : _playPCMF32();
},
),
Text('Test PCMF32'),
],
),
),
],
),
),
);
}
Future<void> _playPCMF32() async {
if (_playerPCMF32.isPlaying) {
return;
}
await _playerPCMF32.play();
setState(() {
_playing = true;
});
// Continuously feed the player until the playback is paused/stopped
//final dataBlock = _genPCMF32DataBlock(nPeriods: 20);
final dataBlock = await _getMoreSound();
print('new sound read');
while (_playerPCMF32.isPlaying) {
await _playerPCMF32.feed(dataBlock);
}
}
Future<void> _pausePCMF32() async {
await _playerPCMF32.stop();
setState(() {
_playing = false;
});
}
_getMoreSound() async {
int sampleRate = 44100;
int blockSize = 980;
int blockCount = sampleRate ~/ blockSize; // =45
// Create the synthesizer.
ByteData bytes = await rootBundle.load('assets/Mace-Drums.sf2');
Synthesizer synthesizer = Synthesizer.loadByteData(
bytes,
SynthesizerSettings(
sampleRate: sampleRate,
blockSize: blockSize,
maximumPolyphony: 64,
enableReverbAndChorus: false,
));
// Define the melody.
// A single row indicates the start timing, end timing, and pitch.
var data = [
//[0, 15, 0],
[15, 30, 36],
[30, 45, 36],
];
// The output buffer.
List<double> left = List<double>.filled(sampleRate, 0);
List<double> right = List<double>.filled(sampleRate, 0);
final dataBlock = <int>[];
for (var t = 0; t < blockCount; t++) {
// Process the melody.
for (var row in data) {
if (t == row[0]) synthesizer.noteOn(channel: 9, key: row[2], velocity: 100);
if (t == row[1]) synthesizer.noteOff(channel: 9, key: row[2]);
}
// Render the block.
int start = blockSize * t;
var blockLeft = left.sublist(start, start + blockSize);
var blockRight = right.sublist(start, start + blockSize);
synthesizer.render(blockLeft, blockRight);
final dataBlockPerPeriod = ByteData(blockLeft.length << 2 /* one float32 is made of 4 bytes */);
for (int i = 0; i < blockLeft.length; i++) {
// amplitude is in the range of -1.0 to 1.0
final value = blockLeft[i];
dataBlockPerPeriod.setFloat32(i << 2, value, Endian.host /* native endianness */);
}
// Repeat dataBlockPerPeriod nPeriods times
dataBlock.addAll(dataBlockPerPeriod.buffer.asUint8List());
debugPrint('dataBlock nBytes: ${dataBlock.length}');
}
return Uint8List.fromList(dataBlock);
}
}