diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/RecordMediaStream.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/RecordMediaStream.razor index a166bf3..50ccafd 100644 --- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/RecordMediaStream.razor +++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/RecordMediaStream.razor @@ -25,13 +25,23 @@ } else if (peakFrequencyCount > 0) { - Average Max Peak: @(Math.Round(peakFrequencySum / peakFrequencyCount, 0)) Hz +
+ + + + + + + +
+ + Average Max Peak: @(Math.Round(peakFrequencySum / peakFrequencyCount, 0)) Hz
} else if (audioBuffer is not null) { - + } else if (mediaStream is null) { @@ -67,7 +77,6 @@ else private MediaDevices? mediaDevices; private string? error; private byte[] frequencyMeasurements = Array.Empty(); - private bool makeMeasurements = false; private MediaStream? mediaStream; private List<(string label, string id)> audioOptions = new(); private string? selectedAudioSource; @@ -75,6 +84,11 @@ else private double peakFrequencyCount = 0; bool recording = false; + double offset = 0; + double duration = 0; + + float playbackRate = 1; + MediaRecorder? recorder; EventListener? dataAvailableEventListener; List blobsRecorded = new(); @@ -187,7 +201,7 @@ else await using AudioBufferSourceNode sourceNode = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new() { Buffer = audioBuffer, - PlaybackRate = 2 + PlaybackRate = playbackRate }); analyser = await context.CreateAnalyserAsync(); @@ -196,25 +210,35 @@ else await analyser.ConnectAsync(destination); int bufferLength = (int)await analyser.GetFrequencyBinCountAsync(); - var frequencyDataArray = await Uint8Array.CreateAsync(JSRuntime, bufferLength); + await using var frequencyDataArray = await Uint8Array.CreateAsync(JSRuntime, bufferLength); var sampleRate = await context.GetSampleRateAsync(); var fftSize = await analyser.GetFftSizeAsync(); + bool makeMeasurements; await using EventListener endedListener = await EventListener.CreateAsync(JSRuntime, _ => { makeMeasurements = false; }); await sourceNode.AddOnEndedEventListenerAsync(endedListener); - await sourceNode.StartAsync(); + await sourceNode.StartAsync(when: 0, offset, duration); + peakFrequencySum = 0; + peakFrequencyCount = 1; makeMeasurements = true; while (makeMeasurements) { await analyser.GetByteFrequencyDataAsync(frequencyDataArray); - frequencyMeasurements = await frequencyDataArray.GetAsArrayAsync(); + try + { + frequencyMeasurements = await frequencyDataArray.GetAsArrayAsync(); + } + catch (Exception) + { + Console.WriteLine("Attempted to deserialize an invalid array."); + } byte largestMeasurement = frequencyMeasurements.Max(); var largestFrequencyIndex = frequencyMeasurements.ToList().IndexOf(largestMeasurement); @@ -225,9 +249,14 @@ else } } + public async Task MakePlaybackMatchFrequency(float frequency) + { + playbackRate = (float)(playbackRate * frequency / (peakFrequencySum / peakFrequencyCount)); + await AnalyseFrequency(); + } + async Task StopAudioTrack() { - makeMeasurements = false; if (mediaStream is null) return; var audioTrack = (await mediaStream.GetAudioTracksAsync()).FirstOrDefault(); if (audioTrack is not null) diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioSlicer.razor.cs b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioSlicer.razor.cs index fb0775b..547d5d4 100644 --- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioSlicer.razor.cs +++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioSlicer.razor.cs @@ -34,6 +34,18 @@ public partial class AudioSlicer : ComponentBase, IDisposable [Parameter] public bool PlaySoundWhenEndingMark { get; set; } = true; + [Parameter] + public double MarkedOffset { get; set; } + + [Parameter] + public EventCallback MarkedOffsetChanged { get; set; } + + [Parameter] + public double MarkedDuration { get; set; } + + [Parameter] + public EventCallback MarkedDurationChanged { get; set; } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) @@ -52,13 +64,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender) for (ulong i = 0; i < length; i += samples) { + // We intentionally only take 1 value from each sample slice as we only need this for a visualization float amplitude = 0; - for (ulong j = 0; j < samples && j + i < length; j++) + for (ulong k = 0; k < numberOfChannels; k++) { - for (ulong k = 0; k < numberOfChannels; k++) - { - amplitude += await data[k].AtAsync((long)(i + j)); - } + amplitude += await data[k].AtAsync((long)i); } amplitudes.Add(amplitude / samples / numberOfChannels); } @@ -142,14 +152,18 @@ private async Task PlayMarkedSound() await using AudioDestinationNode destination = await context.GetDestinationAsync(); await using AudioNode __ = await source.ConnectAsync(destination); - double duration = await AudioBuffer.GetDurationAsync(); + double bufferDuration = await AudioBuffer.GetDurationAsync(); double left = start < end ? start : end; double right = end < start ? start : end; - await source.StartAsync(0, left * duration, (right - left) * duration); + double offset = left * bufferDuration; + double duration = (right - left) * bufferDuration; + + await source.StartAsync(0, offset, duration); - await Task.Delay(10_000); + await MarkedOffsetChanged.InvokeAsync(offset); + await MarkedDurationChanged.InvokeAsync(duration); } private async Task UpdateEnd(double x)