Die endgültige MP3-Datei enthält zwei synchronisierte Audiostreams.
- Der Eingangs-Audiostream vom Mikrofon
- Die Audiostream vom Lautsprecher ausgeben.
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.");
}
}
}
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.