Mischen von zwei Audiopuffern mit NAudio in C#C#

Ein Treffpunkt für C#-Programmierer
Guest
 Mischen von zwei Audiopuffern mit NAudio in C#

Post by Guest »

Ich habe eine Anwendung, die zum Aufzeichnen von Telefongesprächen verwendet wird. Die Anwendung enthält eine Klasse namens AudioManager. Diese Klasse stellt zwei Funktionen bereit: StartRecording() und StopRecording(). Der SavingOutputpath wird als Klasseneigenschaft angegeben. Beide Funktionen werden mit einem Button-Klick ausgeführt. Wenn Sie auf die Schaltfläche „Aufzeichnen“ klicken, beginnt die Aufzeichnung. Wenn Sie auf „Aufzeichnung stoppen“ klicken, wird der Aufzeichnungsvorgang gestoppt und die MP3-Datei des aufgezeichneten Gesprächs des Telefons wird unter dem in SavingOutputPath angegebenen Dateinamen gespeichert.
Die endgültige MP3-Datei enthält zwei synchronisierte Audiostreams.
  • Der Eingangs-Audiostream vom Mikrofon
  • Die Audiostream vom Lautsprecher ausgeben.
Diese beiden Streams müssen zusammengemischt werden, damit ein Gespräch entsteht, wie es im MP3-Format gespeichert ist Datei ist flüssig, kontinuierlich und fehlerfrei – kein abgehackter Audiostream, keine Unterbrechungen, keine unnötigen Pausen und keine gestreckte Audiodatei. Wenn ich beispielsweise die Aufnahme 20 Sekunden lang laufen lasse, laufen sowohl die Mikrofon- als auch die Lautsprecherpuffer jeweils 20 Sekunden lang, dann muss die Ausgabedatei eine 20 Sekunden lange MP3-Datei sein, die das Gespräch sowohl vom Mikrofoneingang als auch vom Lautsprecherausgang enthält .
Ich habe die NAudio-Bibliothek in C# verwendet. Die Klasse AudioManager, die das Konzept implementiert und unten gezeigt wird. Ich denke, dass das Problem bei der AudioManager-Klasse und BackgroundmixingLoop und dem MixingSampleProvider liegt (vielleicht). Wenn ich den Code ausführe und mit der Aufnahme für ein paar Sekunden beginne, enthält die neu kodierte MP3-Datei 3 Minuten unsinniges Audio und Stille. Die zwischen diesen Stillezeiten gespeicherten Daten waren sehr abgehackt und unterbrochen. Wenn zum Beispiel das Eingangsaudio des Mikrofons "This is Testing" ist, die Wiedergabe aber: ".....th.....ee...... ist......Te......ss....ti.....ing". Ich habe die Github-Dokumentation für NAudio studiert, konnte aber nicht herausfinden, wo das Problem liegt. Hier ist der Code, den ich geschrieben habe:

Code: Select all

using System;
using System.Threading;
using System.Threading.Tasks;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using NAudio.Lame;

public class AudioManager
{
private WaveInEvent _microphoneCapture;
private WasapiLoopbackCapture _speakerCapture;
private BufferedWaveProvider _micBufferedProvider;
private BufferedWaveProvider _speakerBufferedProvider;
private MixingSampleProvider _mixingSampleProvider;
private LameMP3FileWriter _mp3Writer;

private Task _mixingTask;
private CancellationTokenSource _cancellationTokenSource;
private readonly object _stateLock = new();
private bool _isRecording;

public string SavingOutputPath { get; set; }

public void StartRecording()
{
lock (_stateLock)
{
if (_isRecording)
throw new InvalidOperationException("Recording is already active.");

if (string.IsNullOrEmpty(SavingOutputPath))
throw new InvalidOperationException("Saving output path is not set.");

_isRecording = true;
}

// Initialize MP3 writer
_mp3Writer = new LameMP3FileWriter(SavingOutputPath, new WaveFormat(44100, 16, 2), LAMEPreset.STANDARD);

// Configure microphone capture
_microphoneCapture = new WaveInEvent { WaveFormat = new WaveFormat(44100, 16, 1) };
_micBufferedProvider = new BufferedWaveProvider(_microphoneCapture.WaveFormat)
{
BufferDuration = TimeSpan.FromMilliseconds(500)
};

// Configure speaker capture
_speakerCapture = new WasapiLoopbackCapture { WaveFormat = new WaveFormat(44100, 16, 2) };
_speakerBufferedProvider = new BufferedWaveProvider(_speakerCapture.WaveFormat)
{
BufferDuration = TimeSpan.FromMilliseconds(500)
};

// Create sample providers
var micSampleProvider = new VolumeSampleProvider(_micBufferedProvider.ToSampleProvider().ToStereo())
{
Volume = 1.0f
};
var speakerSampleProvider = new VolumeSampleProvider(_speakerBufferedProvider.ToSampleProvider())
{
Volume = 1.0f
};

// Mix the microphone and speaker sample providers
_mixingSampleProvider = new MixingSampleProvider(new[] { micSampleProvider, speakerSampleProvider })
{
ReadFully = true
};

// Start mixing task
_cancellationTokenSource = new CancellationTokenSource();
_mixingTask = Task.Run(() => BackgroundMixingLoop(_cancellationTokenSource.Token));

// Attach event handlers
_microphoneCapture.DataAvailable += OnMicrophoneDataAvailable;
_speakerCapture.DataAvailable += OnSpeakerDataAvailable;

// Start audio capture
_microphoneCapture.StartRecording();
_speakerCapture.StartRecording();

Console.WriteLine("Recording started.");
}

public async Task StopRecording()
{
lock (_stateLock)
{
if (!_isRecording)
throw new InvalidOperationException("No active recording to stop.");

_isRecording = false;
}

// Stop audio capture
_microphoneCapture.StopRecording();
_speakerCapture.StopRecording();

// Cancel mixing task and wait for it to finish
_cancellationTokenSource.Cancel();

try
{
if (_mixingTask != null)
await _mixingTask;
}
catch (OperationCanceledException)
{
// Expected exception during cancellation
}

// Finalize MP3 file
_mp3Writer.Close();
_mp3Writer.Dispose();

Console.WriteLine($"Recording stopped.  File saved to {SavingOutputPath}");
}

private void OnMicrophoneDataAvailable(object sender, WaveInEventArgs e)
{
if (_isRecording)
_micBufferedProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
}

private void OnSpeakerDataAvailable(object sender, WaveInEventArgs e)
{
if (_isRecording)
_speakerBufferedProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
}

private async Task BackgroundMixingLoop(CancellationToken cancellationToken)
{
try
{
float[] floatBuffer = new float[44100 * 2 / 10]; // 100ms buffer (stereo, 44100Hz)
byte[] byteBuffer = new byte[floatBuffer.Length * 2];

while (!cancellationToken.IsCancellationRequested)
{
// Read from MixingSampleProvider
int samplesRead = _mixingSampleProvider.Read(floatBuffer, 0, floatBuffer.Length);

if (samplesRead > 0)
{
// Convert float samples to PCM
for (int i = 0; i < samplesRead; i++)
{
short pcmSample = (short)(floatBuffer[i] * short.MaxValue);
byteBuffer[i * 2] = (byte)(pcmSample & 0xFF);
byteBuffer[i * 2 + 1] = (byte)((pcmSample >> 8) & 0xFF);
}

// Write to MP3 file
_mp3Writer.Write(byteBuffer, 0, samplesRead * 2);
}

await Task.Delay(10, cancellationToken);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Mixing task canceled.");
}
finally
{
Console.WriteLine("Mixing task stopped.");
}
}
}
In der Methode MixingAudios läuft möglicherweise etwas schief. Könnten Sie mir bitte helfen?
Ich habe stattdessen versucht, die Puffer während der Aufnahme zu mischen – ich habe jedes Audio in separate MP3-Dateien aufgenommen und in StopRecording() habe ich eine verwendet Aufruf einer anderen Mischfunktion, die die MP3-Dateien mischen sollte. Allerdings weist auch die gemischte MP3-Datei das gleiche Problem auf.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post