mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-28 23:08:59 +02:00
169 lines
4.6 KiB
Dart
169 lines
4.6 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:spotiflac_android/widgets/audio_analysis_widget.dart';
|
|
|
|
void main() {
|
|
group('audio spectrogram filter', () {
|
|
test('keeps source rate and uses a full-range float pipeline', () {
|
|
final filter = buildAudioSpectrogramFilter();
|
|
|
|
expect(filter, contains('[0:a:0]aformat=sample_fmts=fltp'));
|
|
expect(filter, contains('s=1600x800'));
|
|
expect(filter, contains('scale=log'));
|
|
expect(filter, contains('fscale=lin'));
|
|
expect(filter, contains('win_func=hann'));
|
|
expect(filter, contains('drange=120'));
|
|
expect(filter, contains('limit=0'));
|
|
expect(filter, isNot(contains('pan=')));
|
|
expect(filter, isNot(contains('aresample')));
|
|
});
|
|
|
|
test('can render one source channel without app-specific branching', () {
|
|
final filter = buildAudioSpectrogramFilter(channel: 1);
|
|
|
|
expect(filter, contains('[0:a:0]pan=mono|c0=c1,'));
|
|
expect(filter, contains('aformat=sample_fmts=fltp'));
|
|
});
|
|
|
|
test('processes the complete source without resampling or downmixing', () {
|
|
final arguments = buildAudioSpectrogramArguments(
|
|
inputPath: 'source.flac',
|
|
outputPath: 'spectrum.rgba',
|
|
);
|
|
|
|
expect(arguments, containsAllInOrder(['-i', 'source.flac']));
|
|
expect(arguments, containsAllInOrder(['-frames:v', '1']));
|
|
expect(arguments, containsAllInOrder(['-pix_fmt', 'rgba']));
|
|
expect(arguments, isNot(contains('-t')));
|
|
expect(arguments, isNot(contains('-ss')));
|
|
expect(arguments, isNot(contains('-ar')));
|
|
expect(arguments, isNot(contains('-ac')));
|
|
});
|
|
});
|
|
|
|
group('broadband spectral cutoff', () {
|
|
const width = 200;
|
|
const height = 800;
|
|
const nyquist = 96000.0;
|
|
|
|
test('ignores a narrow ultrasonic pilot above a 22 kHz music band', () {
|
|
final rgba = _blankRgba(width, height);
|
|
_paintFrequencyBand(
|
|
rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
lowHz: 0,
|
|
highHz: 22000,
|
|
intensity: 220,
|
|
);
|
|
_paintFrequencyBand(
|
|
rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
lowHz: 53880,
|
|
highHz: 54120,
|
|
intensity: 255,
|
|
);
|
|
|
|
final cutoff = estimateBroadbandSpectralCutoffHz(
|
|
rgba: rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
);
|
|
|
|
expect(cutoff, isNotNull);
|
|
expect(cutoff!, inInclusiveRange(21000, 23500));
|
|
});
|
|
|
|
test('retains genuine broadband ultrasonic content', () {
|
|
final rgba = _blankRgba(width, height);
|
|
_paintFrequencyBand(
|
|
rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
lowHz: 0,
|
|
highHz: 48000,
|
|
intensity: 180,
|
|
);
|
|
|
|
final cutoff = estimateBroadbandSpectralCutoffHz(
|
|
rgba: rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
);
|
|
|
|
expect(cutoff, isNotNull);
|
|
expect(cutoff!, inInclusiveRange(47000, 49500));
|
|
});
|
|
|
|
test('does not report an isolated line as a broadband cutoff', () {
|
|
final rgba = _blankRgba(width, height);
|
|
_paintFrequencyBand(
|
|
rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
lowHz: 53880,
|
|
highHz: 54120,
|
|
intensity: 255,
|
|
);
|
|
|
|
final cutoff = estimateBroadbandSpectralCutoffHz(
|
|
rgba: rgba,
|
|
width: width,
|
|
height: height,
|
|
maxFrequencyHz: nyquist,
|
|
);
|
|
|
|
expect(cutoff, isNull);
|
|
});
|
|
|
|
test('rejects invalid or incomplete images', () {
|
|
expect(
|
|
estimateBroadbandSpectralCutoffHz(
|
|
rgba: Uint8List(3),
|
|
width: 1,
|
|
height: 1,
|
|
maxFrequencyHz: nyquist,
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
Uint8List _blankRgba(int width, int height) {
|
|
final rgba = Uint8List(width * height * 4);
|
|
for (var offset = 3; offset < rgba.length; offset += 4) {
|
|
rgba[offset] = 255;
|
|
}
|
|
return rgba;
|
|
}
|
|
|
|
void _paintFrequencyBand(
|
|
Uint8List rgba, {
|
|
required int width,
|
|
required int height,
|
|
required double maxFrequencyHz,
|
|
required double lowHz,
|
|
required double highHz,
|
|
required int intensity,
|
|
}) {
|
|
for (var y = 0; y < height; y++) {
|
|
final frequency = (height - y - 0.5) / height * maxFrequencyHz;
|
|
if (frequency < lowHz || frequency > highHz) continue;
|
|
for (var x = 0; x < width; x++) {
|
|
final offset = (y * width + x) * 4;
|
|
rgba[offset] = intensity;
|
|
rgba[offset + 1] = intensity;
|
|
rgba[offset + 2] = intensity;
|
|
}
|
|
}
|
|
}
|