Воспроизведение нескольких смещенных аудиофайлов со скоростью воспроизведения и возможностями поиска.C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Воспроизведение нескольких смещенных аудиофайлов со скоростью воспроизведения и возможностями поиска.

Сообщение Anonymous »

Представьте себе простой аудиоредактор, в котором вы можете иметь несколько аудиосегментов на нескольких дорожках.
Эти сегменты могут иметь разные начало, длину и скорость воспроизведения, как и любой аудио-/видеоредактор.
Я пытаюсь реализовать воспроизведение звука этих аудиосегментов с помощью NAudio.
Вот как я инициализирую необходимые помощники:
public EditorViewModel()
{
_outputDevice = new WaveOutEvent();
_mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(48000, 2))
{
ReadFully = true
};
_bufferedWaveProvider = new BufferedWaveProvider(_mixer.WaveFormat);
_outputDevice.Init(_bufferedWaveProvider);
}

А вот как я микширую звуковые дорожки. Я думаю, что я должен вызывать этот метод каждый раз, когда изменяется аудиосегмент (если он добавляется, удаляется, перетаскивается на другую временную метку и т. д.):
private void MixAudioTracks()
{
_mixer.RemoveAllMixerInputs();
_audioFileReaders = [];

foreach (var track in Tracks)
{
foreach (var segment in track.Segments)
{
var inputStream = new AudioFileReader(segment.AudioPath)
{
Volume = segment.Volume
};
var offsetProvider = new OffsetSampleProvider(inputStream.ToSampleProvider())
{
DelayBy = TimeSpan.FromTicks(segment.StartTime),
SkipOver = TimeSpan.FromTicks(segment.StartTimeOffset),
Take = TimeSpan.FromTicks(segment.Length - segment.EndTimeOffset)
};
var pitchProvider = new SmbPitchShiftingSampleProvider(offsetProvider)
{
PitchFactor = segment.SpeedFactor / SpeedFactor //Segment vs previewer speed
};

_audioFileReaders.Add(inputStream);
_mixer.AddMixerInput(pitchProvider);
}
}

FillBuffer(true);
}


РЕДАКТИРОВАНИЕ:
Вот то, что я думаю, это единственный способ поиска:
Но проблема заключается в том, что микшер удалит свои входы после завершения общего времени воспроизведения. Даже если я стремлюсь назад к нулю.
private void SeekAudio()
{
foreach (var fileReader in _audioFileReaders)
{
fileReader.CurrentTime = TimeSpan.FromTicks(CurrentTime);
//TODO: Adjust with segment Start and Offsets
}

FillBuffer(true);
}

private void FillBuffer(bool force = false)
{
if (!force && _lastSampleTime >= 0 && (_lastSampleTime < CurrentTime || _lastSampleTime + _bufferedWaveProvider.BufferDuration.Ticks < CurrentTime))
return;

//if (force || CurrentTime < _lastSampleTime)
_bufferedWaveProvider.ClearBuffer();

var floatBuffer = new float[_bufferedWaveProvider.BufferLength / 4];
var bytesRead = _mixer.Read(floatBuffer, 0, floatBuffer.Length);

//Convert float array to byte array
var byteBuffer = new byte[bytesRead * 4]; //4 bytes per float
Buffer.BlockCopy(floatBuffer, 0, byteBuffer, 0, byteBuffer.Length);

_bufferedWaveProvider.AddSamples(byteBuffer, 0, byteBuffer.Length);

//Update the last sample timestamp.
_lastSampleTime = CurrentTime;
}

Например, если я воспроизведу 5 секунд 10-секундной записи, а затем снова перейду к 00:00, начало звука будет воспроизведено снова, но только в течение 5 секунд.< /p>
Проверив _mixer, я увижу, что входы были очищены.
РЕДАКТИРОВАТЬ 2:
Хорошо, у меня все работает. но выглядит беспорядочно.
Все еще тестируем на наличие ошибок.
Мне бы хотелось, чтобы в MixingSampleProvider было свойство CurrentTime, это облегчило бы задачу.
private void SeekAudio(long seekTo)
{
CurrentTime = seekTo;

MixAudioTracks();
FillBuffer(true);
}

private void MixAudioTracks()
{
_mixer.RemoveAllMixerInputs();

foreach (var track in Tracks)
{
foreach (var segment in track.Segments)
{
//If audio segment is out of range, ignore it.
if (CurrentRenderTime > segment.StartTime + segment.Length)
continue;

var inputStream = new AudioFileReader(segment.AudioPath)
{
Volume = segment. Volume, //0f to 1f
};

var offsetProvider = new OffsetSampleProvider(inputStream.ToSampleProvider());

//Audio segment needs to be played at the exact time, based on its position, length, trims/offset
if (segment.StartTime < CurrentTime)
{
offsetProvider.SkipOver = TimeSpan.FromTicks(CurrentRenderTime - segment.StartTime + segment.StartTimeOffset);
offsetProvider.Take = TimeSpan.FromTicks(segment.Length - segment.EndTimeOffset - CurrentTime - segment.StartTime);
}
else
{
offsetProvider.DelayBy = TimeSpan.FromTicks(segment.StartTime - CurrentTime);
offsetProvider.SkipOver = TimeSpan.FromTicks(segment.StartTimeOffset);
offsetProvider.Take = TimeSpan.FromTicks(segment.Length - segment.EndTimeOffset);
}

var pitchProvider = new SmbPitchShiftingSampleProvider(offsetProvider)
{
PitchFactor = segment.SpeedFactor / (float)PreviewerSpeedFactor
};

_mixer.AddMixerInput(pitchProvider);
}
}
}

Метод заполнения буфера всегда вызывается в каждом цикле рендеринга, но на самом деле буфер заполняется только при необходимости.
Значение 10_000_000 тиков является приблизительным, буфер должен загружаться по мере приближения к концу.
private void FillBuffer(bool force = false)
{
//Only fill buffer if forced, never filled, current time in past or current time past buffer length.
if (!force && _lastSampleTime >= 0 && (_lastSampleTime > CurrentRenderTime ||
_lastSampleTime + _bufferedWaveProvider.BufferDuration.Ticks - 10_000_000 > CurrentTime))
return;

_bufferedWaveProvider.ClearBuffer();

var floatBuffer = new float[_bufferedWaveProvider.BufferLength / 4];
var bytesRead = _mixer.Read(floatBuffer, 0, floatBuffer.Length);

//Convert float array to byte array
var byteBuffer = new byte[bytesRead * 4]; //4 bytes per float
Buffer.BlockCopy(floatBuffer, 0, byteBuffer, 0, byteBuffer.Length);

_bufferedWaveProvider.AddSamples(byteBuffer, 0, byteBuffer.Length);

//Update the last sample timestamp.
_lastSampleTime = CurrentTime;
}



Подробнее здесь: https://stackoverflow.com/questions/793 ... apabilitie
Реклама
Ответить Пред. темаСлед. тема

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

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

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

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

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

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