private static unsafe void DrawWaves(ThumbnailsRenderContext ctx, MediaStream audioStream) { var height = 88; var width = 88; using var recorder = new SKPictureRecorder(); using var canvas = recorder.BeginRecording(SKRect.Create(width, height)); var columnCount = 88 * 2; var columnWidth = (float)width / columnCount; using var wavePaint = new SKPaint { Color = new SKColor(128, 128, 128, 76), StrokeWidth = columnWidth }; var totalSample = audioStream.SampleRate * audioStream.Duration; var columnMaxSample = (int)(totalSample / columnCount); var columns = new short[columnCount]; using var decoder = audioStream.CreateStreamDecoder(); using var filter = new AudioFormatFilter("sample_fmts=s16:channel_layouts=mono", audioStream, decoder); filter.Build(); long sum = 0; var n = 0; var c = 0; while (filter.MoveNext()) { var frame = filter.Current.Value; var p = (short *)frame->data[0]; for (var i = 0; i < frame->nb_samples; i++) { sum += SampleAbs(p[i]); n++; if (n == columnMaxSample && c < columnCount) { columns[c] = (short)(sum / n); n = 0; sum = 0; c++; } } } for (var i = 0; i < columnCount; i++) { var h = Math.Max((float)columns[i] * height / short.MaxValue, 0.5f); var x = i * columnWidth; canvas.DrawLine(x, (height - h) / 2.0f, x, (height + h) / 2.0f, wavePaint); } _cachedWaveformDecorationImage ??= SKImage.FromEncodedData(ReadWaveformDecorationImage()); canvas.DrawImage(_cachedWaveformDecorationImage, SKRect.Create((width - 28) / 2.0f, (height - 36) / 2.0f, 28, 36)); using var picture = recorder.EndRecording(); ThumbnailUtils.DrawShadowView( ctx, new SkPictureView( picture, new SKSize(88, 88))); }