Библиотека Bass.NET позволяет не только воспроизводить, но и записывать звук. Но в тоже время многие наверняка сталкивались с тем, что качество записи с её помощью оставляет желать лучшего (запись имеет характерное прерывистое звучание).
Однако причина данной проблемы вовсе не издержки самой библиотеки, а неправильное использование, которое, в свою очередь, является следствием грубых ошибок в документации.
Запись согласно документации
В соответствии с документацией для записи необходимо создать, помимо всего прочего, закрытые поля в виде массива байт для использования в качестве буфера и счётчика записанных данных.
1 2 |
private byte[] _recbuffer; private int _byteswritten = 0; |
А, сам процесс записи осуществлять в методе (функции) обратного вызова по следующему алгоритму.
1 2 3 4 5 6 7 8 9 10 11 |
if (length > 0 && buffer != IntPtr.Zero) { // increase the rec buffer as needed if (_recbuffer == null || _recbuffer.Length < length) _recbuffer = new byte[length]; // copy from managed to unmanaged memory Marshal.Copy(buffer, _recbuffer, 0, length); _byteswritten += length; // write to file ... } |
Данная реализация буфера приведённые манипуляции с ним в функции обратного вызова приводят к тому, что в итоге звук записывается некорректно. При этом не играет роли, куда осуществляется запись, в поток или файл, звучание всё равно будет прерывистым. Данные поступают испорченными уже изначально.
Правильный способ записи
Получить качественную запись с помощью Bass.NET на самом деле несложно.
Для этого требуется отказаться от использования вышеупомянутых закрытых полей, перенести переменную-буфер в саму функцию обратного вызова и выполнять с буфером только действительно необходимые действия.
1 2 3 4 5 6 7 |
private bool MyRecording(int handle, IntPtr buffer, int length, IntPtr user) { byte[] buf = new byte[length]; Marshal.Copy(buffer, buf, 0, length); //Передача данных в поток или файл return true; } |
В результате такой записи будет получен уже «чистый» звук.
Пример использования
Рассмотрим пример прямой записи в файл.
Описываем закрытые члены класса :
- Делегат для функции обратного вызова;
- Поток записи;
- Файловый поток;
1 2 3 |
private RECORDPROC _myRecProc; int _recHandle; FileStream _fileStream; |
Создадим метод создающий пустой файл формирующий в нём заголовок WAV.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private static void WriteWavHeader() { using (Stream stream = new FileStream("test1.wav", FileMode.Create, FileAccess.Write, FileShare.None)) { int channelCount = 2; int sampleRate = 44100; int bitDepth = 16; stream.Position = 0; stream.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4); stream.Write(BitConverter.GetBytes(36), 0, 4); stream.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4); stream.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4); stream.Write(BitConverter.GetBytes(16), 0, 4); stream.Write(BitConverter.GetBytes((ushort)1), 0, 2); stream.Write(BitConverter.GetBytes(channelCount), 0, 2); stream.Write(BitConverter.GetBytes(sampleRate), 0, 4); stream.Write(BitConverter.GetBytes(sampleRate * channelCount * (bitDepth / 8)), 0, 4); stream.Write(BitConverter.GetBytes((ushort)channelCount * (bitDepth / 8)), 0, 2); stream.Write(BitConverter.GetBytes(bitDepth), 0, 2); stream.Write(Encoding.ASCII.GetBytes("data"), 0, 4); stream.Write(BitConverter.GetBytes(0), 0, 4); } } |
Так как продолжительность записи заранее не известна, в последнюю секцию заголовка (где хранится информация о длительности) записываем ноль.
Запишем функцию обратного вызова.
1 2 3 4 5 6 7 |
private bool MyRecording(int handle, IntPtr buffer, int length, IntPtr user) { byte[] buf = new byte[length]; Marshal.Copy(buffer, buf, 0, length); _fileStream.Write(buf, 0, buf.Length); return true; } |
Для того чтобы начать запись. Инициализируем поток записи и создадим пустой WAV файл с заголовком.
1 2 3 |
_myRecProc = new RECORDPROC(MyRecording); _recHandle = Bass.BASS_RecordStart(44100, 2, BASSFlag.BASS_RECORD_PAUSE, _myRecProc, IntPtr.Zero); WriteWavHeader(); |
Далее инициализируем файловый поток для добавления звуковых данных. Чтобы предотвратить перезапись заголовка устанавливаем соответствующую позицию.
1 2 |
_fileStream = new FileStream("test1.wav", FileMode.Open, FileAccess.Write, FileShare.None); _fileStream.Position = 44; |
Теперь можно приступить собственно к записи.
1 |
Bass.BASS_ChannelPlay(_recHandle, false); |
После завершения записи вычисляем её продолжительность и записываем её значение в заголовок.
1 2 3 |
Bass.BASS_ChannelStop(_recHandle);https://streletzcoder.ru/wp-admin/post-new.php# _fileStream.Position = 40; _fileStream.Write(BitConverter.GetBytes(_fileStream.Length - 44), 0, 4); |
Остаётся только корректно освободить ресурсы и можно прослушать записанный звук (например, в проигрывателе).
Добавить комментарий