mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-13 06:30:18 +02:00
feat(audio): add dither and resampler options for lossless conversion
Let users choose FFmpeg dithering when reducing bit depth and SoXr or SWR resampling when changing sample rate across single-track and batch lossless conversion flows.
This commit is contained in:
@@ -16,6 +16,7 @@ class BatchConvertSheet extends StatefulWidget {
|
||||
String format,
|
||||
String bitrate,
|
||||
LosslessConversionQuality losslessQuality,
|
||||
LosslessConversionProcessing losslessProcessing,
|
||||
)
|
||||
onConvert;
|
||||
|
||||
@@ -42,6 +43,8 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
late String _selectedBitrate;
|
||||
int? _selectedMaxBitDepth;
|
||||
int? _selectedMaxSampleRate;
|
||||
String _selectedDither = 'none';
|
||||
String _selectedResampler = 'swr';
|
||||
|
||||
String _defaultBitrateForFormat(String format) {
|
||||
if (format == 'Opus') return '128k';
|
||||
@@ -132,6 +135,8 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
} else {
|
||||
_selectedMaxBitDepth = null;
|
||||
_selectedMaxSampleRate = null;
|
||||
_selectedDither = 'none';
|
||||
_selectedResampler = 'swr';
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -183,8 +188,10 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
originalLabel: labels.original,
|
||||
),
|
||||
selected: _selectedMaxBitDepth == null,
|
||||
onTap: () =>
|
||||
setState(() => _selectedMaxBitDepth = null),
|
||||
onTap: () => setState(() {
|
||||
_selectedMaxBitDepth = null;
|
||||
_selectedDither = 'none';
|
||||
}),
|
||||
),
|
||||
...bitDepthOptions.map((depth) {
|
||||
return _choice(
|
||||
@@ -222,8 +229,10 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
originalLabel: labels.original,
|
||||
),
|
||||
selected: _selectedMaxSampleRate == null,
|
||||
onTap: () =>
|
||||
setState(() => _selectedMaxSampleRate = null),
|
||||
onTap: () => setState(() {
|
||||
_selectedMaxSampleRate = null;
|
||||
_selectedResampler = 'swr';
|
||||
}),
|
||||
),
|
||||
...sampleRateOptions.map((rate) {
|
||||
return _choice(
|
||||
@@ -243,6 +252,53 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
),
|
||||
),
|
||||
|
||||
if (_isLosslessTarget && _selectedMaxBitDepth != null)
|
||||
_card(
|
||||
cs,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_sectionLabel(cs, context.l10n.trackConvertDithering),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: losslessDitherOptions.map((mode) {
|
||||
return _choice(
|
||||
cs,
|
||||
label: context.l10n.losslessDitherOptionLabel(mode),
|
||||
selected: mode == _selectedDither,
|
||||
onTap: () => setState(() => _selectedDither = mode),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (_isLosslessTarget && _selectedMaxSampleRate != null)
|
||||
_card(
|
||||
cs,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_sectionLabel(cs, context.l10n.trackConvertResampler),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: losslessResamplerOptions.map((mode) {
|
||||
return _choice(
|
||||
cs,
|
||||
label: context.l10n.losslessResamplerOptionLabel(mode),
|
||||
selected: mode == _selectedResampler,
|
||||
onTap: () =>
|
||||
setState(() => _selectedResampler = mode),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (_isLosslessTarget)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
@@ -294,6 +350,10 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
|
||||
maxBitDepth: _selectedMaxBitDepth,
|
||||
maxSampleRate: _selectedMaxSampleRate,
|
||||
),
|
||||
LosslessConversionProcessing(
|
||||
dither: _selectedDither,
|
||||
resampler: _selectedResampler,
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.swap_horiz),
|
||||
style: FilledButton.styleFrom(
|
||||
|
||||
Reference in New Issue
Block a user