Смешивание двух аудиобуферов с использованием NAudio в C#C#

Место общения программистов C#
Ответить
Anonymous
 Смешивание двух аудиобуферов с использованием NAudio в C#

Сообщение Anonymous »

У меня есть приложение, которое используется для записи телефонных разговоров. Приложение содержит класс AudioManager. Этот класс предоставляет две функции StartRecording() и StopRecording(). SavingOutputpath предоставляется как свойство класса. Обе функции выполняются нажатием кнопки. При нажатии кнопки записи начинается запись, а при нажатии кнопки StopRecording процесс записи останавливается, и MP3-файл записанного телефонного разговора будет сохранен с именем файла, указанным в SavingOutputPath.
Окончательный файл MP3 содержит два синхронизированных аудиопотока.
  • Входной аудиопоток с микрофона
  • Выходной аудиопоток из динамика.
Эти два потока необходимо смешать таким образом, чтобы что разговор, сохраненный в файле MP3, является плавным, непрерывным и безупречным — без прерывистого аудиопотока, без прерываний, без ненужного молчания и без растянутого аудиофайла. Например, если я запускаю запись в течение 20 секунд, буферы микрофона и динамика работают по 20 секунд каждый, тогда выходной файл должен быть MP3-файлом длиной 20 секунд, содержащим разговор как со входа микрофона, так и с выхода динамика. .
Я использовал библиотеку NAudio на C#. Класс AudioManager, реализующий эту концепцию, показан ниже. Я думаю, что проблема связана с классом AudioManager и BackgroundmixingLoop и MixingSampleProvider (возможно). Когда я запускаю код и начинаю запись на несколько секунд, перекодированный MP3-файл содержит 3 минуты бессмысленного звука и тишины. Данные, сохраняемые между этими паузами, были очень прерывистыми и прерывистыми. Например, если входной звук микрофона — "Это тестирование", а воспроизведение: ".....th.....ee...... это......Те......сс....ти.....инг". Я изучал документацию Github для NAudio, но не смог понять, в чем проблема. Вот код, который я написал:

Код: Выделить всё

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.");
}
}
}
В методе MixingAudios что-то может пойти не так. Не могли бы вы мне помочь?
Вместо этого я попытался смешивать буферы во время записи - записывая каждый звук в отдельные файлы MP3, а в StopRecording() я использовал вызов другой функции микширования, которая должна была микшировать файлы MP3. Однако смешанный файл MP3 также имеет ту же проблему.

Подробнее здесь: https://stackoverflow.com/questions/793 ... in-c-sharp
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»