А deepseek.com сделал и даже работоспособный, но файлы получаются огромными, без оптимизации по размеру получился. Так что только 16 бит и на маленьких файлах пробовать. В таком виде представляет только исследовательский интерес без практического, но его представляет.
Итак: кодек векторного звука:
- Код: Выделить всё
- program vacodec;
 {$mode objfpc}{$H+}
 uses
 SysUtils, vanodeunit, wavfileunit, vaformatunit,vatypesunit;
 procedure CompressWAVToVA(const InputFile, OutputFile: string);
 var
 Amplitudes: TMultiChannelAmplitudeArray;
 SampleRate, NumChannels: Integer;
 Nodes: TVANodeArray;
 begin
 WriteLn('Сжатие WAV-файла: ', InputFile);
 
 // Загрузка WAV-файла
 WriteLn('Загрузка WAV-файла...');
 Amplitudes := LoadWAVToAmplitudeArray(InputFile, SampleRate, NumChannels);
 // Проверка на минимальную длину данных
 WriteLn('Проверка длины данных...');
 if Length(Amplitudes) = 0 then
 raise Exception.Create('Файл не содержит данных');
 if Length(Amplitudes[0]) < 4 then
 raise Exception.Create('Файл слишком короткий для векторизации: требуется минимум 4 сэмпла');
 // Векторизация звука
 WriteLn('Векторизация звука...');
 Nodes := VectorizeAmplitudeArray(Amplitudes, SampleRate);
 // Сохранение в .va
 WriteLn('Сохранение в .va...');
 SaveVAToFile(OutputFile, Nodes, SampleRate, NumChannels);
 WriteLn('Файл успешно сжат: ', OutputFile);
 end;
 procedure DecompressVAToWAV(const InputFile, OutputFile: string);
 var
 SampleRate, NumChannels: Integer;
 Nodes: TVANodeArray;
 Amplitudes: TMultiChannelAmplitudeArray;
 begin
 // Загрузка .va-файла
 Nodes := LoadVAFromFile(InputFile, SampleRate, NumChannels);
 // Восстановление звука
 Amplitudes := ReconstructAmplitudeArray(Nodes, SampleRate, NumChannels);
 // Сохранение в WAV
 SaveAmplitudeArrayToWAV(OutputFile, Amplitudes, SampleRate, NumChannels);
 WriteLn('Файл успешно распакован: ', OutputFile);
 end;
 begin
 try
 if (ParamCount < 2) or (ParamCount > 3) then
 begin
 WriteLn('Использование:');
 WriteLn(' vacodec input.wav output.va — сжатие WAV в VA');
 WriteLn(' vacodec -d input.va output.wav — распаковка VA в WAV');
 Exit;
 end;
 if ParamStr(1) = '-d' then
 DecompressVAToWAV(ParamStr(2), ParamStr(3))
 else
 CompressWAVToVA(ParamStr(1), ParamStr(2));
 except
 on E: Exception do
 begin
 WriteLn('Ошибка: ', E.Message);
 Halt(1);
 end;
 end;
 end.
- Код: Выделить всё
- unit vaformatunit;
 {$mode objfpc}{$H+}
 interface
 uses
 Classes, SysUtils, vanodeunit,vatypesunit;
 const
 VA_MAGIC = 'VAVF'; // Магическое число
 VA_VERSION = 1; // Версия формата
 // Запись векторного звука в файл
 procedure SaveVAToFile(const FileName: string; const Nodes: TVANodeArray; SampleRate: Integer; NumChannels: Integer);
 // Чтение векторного звука из файла
 function LoadVAFromFile(const FileName: string; out SampleRate: Integer; out NumChannels: Integer): TVANodeArray;
 implementation
 procedure SaveVAToFile(const FileName: string; const Nodes: TVANodeArray; SampleRate: Integer; NumChannels: Integer);
 var
 FileStream: TFileStream;
 i, j: Integer;
 begin
 FileStream := TFileStream.Create(FileName, fmCreate);
 try
 // Запись заголовка
 FileStream.WriteBuffer(VA_MAGIC[1], Length(VA_MAGIC));
 FileStream.WriteWord(VA_VERSION);
 FileStream.WriteWord(NumChannels);
 FileStream.WriteDWord(SampleRate);
 FileStream.WriteDWord(Length(Nodes));
 // Запись данных узлов
 for i := 0 to High(Nodes) do
 begin
 FileStream.WriteBuffer(Nodes[i].GetTimeStamp, SizeOf(Double));
 for j := 0 to NumChannels - 1 do
 begin
 FileStream.WriteBuffer(Nodes[i].GetControlPoint(j, 0), SizeOf(TBezierControlPoint));
 FileStream.WriteBuffer(Nodes[i].GetControlPoint(j, 1), SizeOf(TBezierControlPoint));
 FileStream.WriteBuffer(Nodes[i].GetControlPoint(j, 2), SizeOf(TBezierControlPoint));
 FileStream.WriteBuffer(Nodes[i].GetControlPoint(j, 3), SizeOf(TBezierControlPoint));
 end;
 end;
 finally
 FileStream.Free;
 end;
 end;
 function LoadVAFromFile(const FileName: string; out SampleRate: Integer; out NumChannels: Integer): TVANodeArray;
 var
 FileStream: TFileStream;
 Magic: array[1..4] of Char;
 Version: Word;
 NodeCount: Integer;
 i, j: Integer;
 TimeStamp: Double;
 ControlPoints: array[0..3] of TBezierControlPoint;
 begin
 FileStream := TFileStream.Create(FileName, fmOpenRead);
 try
 // Чтение заголовка
 FileStream.ReadBuffer(Magic, SizeOf(Magic));
 if Magic <> VA_MAGIC then
 raise Exception.Create('Неверный формат файла .va');
 Version := FileStream.ReadWord;
 if Version <> VA_VERSION then
 raise Exception.Create('Неподдерживаемая версия формата .va');
 NumChannels := FileStream.ReadWord;
 SampleRate := FileStream.ReadDWord;
 NodeCount := FileStream.ReadDWord;
 // Проверка на допустимое количество узлов
 if NodeCount < 0 then
 raise Exception.Create('Некорректное количество узлов в файле .va');
 // Чтение данных узлов
 SetLength(Result, NodeCount);
 for i := 0 to NodeCount - 1 do
 begin
 FileStream.ReadBuffer(TimeStamp, SizeOf(Double));
 Result[i].Init(TimeStamp, NumChannels);
 for j := 0 to NumChannels - 1 do
 begin
 FileStream.ReadBuffer(ControlPoints[0], SizeOf(TBezierControlPoint));
 FileStream.ReadBuffer(ControlPoints[1], SizeOf(TBezierControlPoint));
 FileStream.ReadBuffer(ControlPoints[2], SizeOf(TBezierControlPoint));
 FileStream.ReadBuffer(ControlPoints[3], SizeOf(TBezierControlPoint));
 Result[i].SetControlPoint(j, 0, ControlPoints[0].X, ControlPoints[0].Y);
 Result[i].SetControlPoint(j, 1, ControlPoints[1].X, ControlPoints[1].Y);
 Result[i].SetControlPoint(j, 2, ControlPoints[2].X, ControlPoints[2].Y);
 Result[i].SetControlPoint(j, 3, ControlPoints[3].X, ControlPoints[3].Y);
 end;
 end;
 finally
 FileStream.Free;
 end;
 end;
 end.
- Код: Выделить всё
- unit vanodeunit;
 {$mode objfpc}{$H+}
 interface
 uses
 Classes, SysUtils, wavfileunit,vatypesunit;
 type
 // Основной объект, представляющий узел векторного звука
 TVANode = object
 private
 FTimeStamp: TTimeStamp; // Временная метка узла
 FControlPoints: array of array[0..3] of TBezierControlPoint; // Контрольные точки кривой Безье для каждого канала
 public
 // Конструктор для инициализации узла
 constructor Init(ATimeStamp: TTimeStamp; NumChannels: Integer);
 
 // Метод для установки контрольных точек кривой Безье
 procedure SetControlPoint(Channel, Index: Integer; X, Y: TAmplitude);
 
 // Метод для получения контрольной точки кривой Безье
 function GetControlPoint(Channel, Index: Integer): TBezierControlPoint;
 
 // Метод для вычисления амплитуды в заданный момент времени
 function GetAmplitudeAtTime(Channel: Integer; Time: TTimeStamp): TAmplitude;
 
 // Метод для получения временной метки узла
 function GetTimeStamp: TTimeStamp;
 end;
 // Тип для хранения массива узлов
 TVANodeArray = array of TVANode;
 // Функция для загрузки WAV-файла в массив амплитуд
 function LoadWAVToAmplitudeArray(const FileName: string; out SampleRate: Integer; out NumChannels: Integer): TMultiChannelAmplitudeArray;
 // Функция для сохранения массива амплитуд в WAV-файл
 procedure SaveAmplitudeArrayToWAV(const FileName: string; const Amplitudes: TMultiChannelAmplitudeArray; SampleRate: Integer; NumChannels: Integer);
 // Функция для векторизации звука (преобразование массива амплитуд в узлы TVANode)
 function VectorizeAmplitudeArray(const Amplitudes: TMultiChannelAmplitudeArray; SampleRate: Integer): TVANodeArray;
 // Функция для восстановления массива амплитуд из узлов TVANode
 function ReconstructAmplitudeArray(const Nodes: TVANodeArray; SampleRate: Integer; NumChannels: Integer): TMultiChannelAmplitudeArray;
 implementation
 constructor TVANode.Init(ATimeStamp: TTimeStamp; NumChannels: Integer);
 begin
 FTimeStamp := ATimeStamp;
 SetLength(FControlPoints, NumChannels);
 end;
 procedure TVANode.SetControlPoint(Channel, Index: Integer; X, Y: TAmplitude);
 begin
 if (Channel >= 0) and (Channel < Length(FControlPoints)) and (Index >= 0) and (Index <= 3) then
 begin
 FControlPoints[Channel][Index].X := X;
 FControlPoints[Channel][Index].Y := Y;
 end;
 end;
 function TVANode.GetControlPoint(Channel, Index: Integer): TBezierControlPoint;
 begin
 if (Channel >= 0) and (Channel < Length(FControlPoints)) and (Index >= 0) and (Index <= 3) then
 Result := FControlPoints[Channel][Index]
 else
 Result := Default(TBezierControlPoint);
 end;
 function TVANode.GetAmplitudeAtTime(Channel: Integer; Time: TTimeStamp): TAmplitude;
 var
 t: Double;
 begin
 // Нормализация времени относительно временной метки узла
 t := (Time - FTimeStamp) / 1.0; // Здесь 1.0 — это длительность узла, можно изменить на нужное значение
 
 // Вычисление амплитуды с использованием кубической кривой Безье
 Result := (1 - t) * (1 - t) * (1 - t) * FControlPoints[Channel][0].Y +
 3 * (1 - t) * (1 - t) * t * FControlPoints[Channel][1].Y +
 3 * (1 - t) * t * t * FControlPoints[Channel][2].Y +
 t * t * t * FControlPoints[Channel][3].Y;
 end;
 function TVANode.GetTimeStamp: TTimeStamp;
 begin
 Result := FTimeStamp;
 end;
 // Загрузка WAV-файла в массив амплитуд
 function LoadWAVToAmplitudeArray(const FileName: string; out SampleRate: Integer; out NumChannels: Integer): TMultiChannelAmplitudeArray;
 var
 WAVFile: TWAVFile;
 i: Integer;
 TotalSamples: Integer;
 begin
 WriteLn('Загрузка WAV-файла: ', FileName);
 WAVFile := TWAVFile.Create(FileName);
 try
 WAVFile.LoadFromFile(FileName);
 SampleRate := WAVFile.GetSampleRate;
 NumChannels := WAVFile.GetNumChannels;
 // Проверка на поддерживаемый формат (PCM)
 if WAVFile.GetBitsPerSample <> 16 then
 raise Exception.Create('Поддерживаются только 16-битные WAV-файлы');
 // Проверка на наличие данных
 if WAVFile.GetSampleRate = 0 then
 raise Exception.Create('Файл не содержит данных');
 // Инициализация массива амплитуд
 WriteLn('Инициализация массива амплитуд...');
 WriteLn('Количество каналов: ', NumChannels);
 WriteLn('Частота дискретизации: ', SampleRate, ' Гц');
 WriteLn('Биты на выборку: ', WAVFile.GetBitsPerSample);
 // Вычисляем общее количество сэмплов
 TotalSamples := WAVFile.GetDataChunkSize div (NumChannels * (WAVFile.GetBitsPerSample div 8));
 WriteLn('Общее количество сэмплов: ', TotalSamples);
 SetLength(Result, NumChannels);
 for i := 0 to NumChannels - 1 do
 begin
 WriteLn('Выделение памяти для канала ', i, ': ', TotalSamples, ' сэмплов');
 SetLength(Result[i], TotalSamples); // Выделяем память для всех сэмплов
 end;
 // Чтение данных
 WriteLn('Чтение данных из WAV-файла...');
 WAVFile.ReadAmplitudes(Result);
 // Отладочное сообщение
 WriteLn('Загружено каналов: ', NumChannels);
 WriteLn('Длина данных в канале 0: ', Length(Result[0]));
 finally
 WAVFile.Free;
 end;
 end;
 // Сохранение массива амплитуд в WAV-файл
 procedure SaveAmplitudeArrayToWAV(const FileName: string; const Amplitudes: TMultiChannelAmplitudeArray; SampleRate: Integer; NumChannels: Integer);
 var
 WAVFile: TWAVFile;
 begin
 WAVFile := TWAVFile.Create(FileName);
 try
 WAVFile.SaveToFile(FileName, Amplitudes, SampleRate, NumChannels, 16); // 16 бит
 finally
 WAVFile.Free;
 end;
 end;
 // Векторизация звука (преобразование массива амплитуд в узлы TVANode)
 function VectorizeAmplitudeArray(const Amplitudes: TMultiChannelAmplitudeArray; SampleRate: Integer): TVANodeArray;
 var
 i, j: Integer;
 TimeStep: Double;
 ControlPoints: array[0..3] of TBezierControlPoint;
 begin
 if Length(Amplitudes) = 0 then
 raise Exception.Create('Нет данных для векторизации');
 // Проверка, что в каждом канале достаточно данных для создания хотя бы одного узла
 for j := 0 to High(Amplitudes) do
 begin
 if Length(Amplitudes[j]) < 4 then
 raise Exception.CreateFmt('Недостаточно данных для векторизации в канале %d: требуется минимум 4 точки', [j]);
 end;
 TimeStep := 1.0 / SampleRate;
 SetLength(Result, Length(Amplitudes[0]) - 3);
 for i := 0 to High(Result) do
 begin
 Result[i].Init(i * TimeStep, Length(Amplitudes));
 for j := 0 to High(Amplitudes) do
 begin
 if (i + 3) > High(Amplitudes[j]) then
 raise Exception.Create('Недостаточно данных для векторизации');
 ControlPoints[0].X := i * TimeStep;
 ControlPoints[0].Y := Amplitudes[j][i];
 ControlPoints[1].X := (i + 1) * TimeStep;
 ControlPoints[1].Y := Amplitudes[j][i + 1];
 ControlPoints[2].X := (i + 2) * TimeStep;
 ControlPoints[2].Y := Amplitudes[j][i + 2];
 ControlPoints[3].X := (i + 3) * TimeStep;
 ControlPoints[3].Y := Amplitudes[j][i + 3];
 Result[i].SetControlPoint(j, 0, ControlPoints[0].X, ControlPoints[0].Y);
 Result[i].SetControlPoint(j, 1, ControlPoints[1].X, ControlPoints[1].Y);
 Result[i].SetControlPoint(j, 2, ControlPoints[2].X, ControlPoints[2].Y);
 Result[i].SetControlPoint(j, 3, ControlPoints[3].X, ControlPoints[3].Y);
 end;
 end;
 end;
 // Восстановление массива амплитуд из узлов TVANode
 function ReconstructAmplitudeArray(const Nodes: TVANodeArray; SampleRate: Integer; NumChannels: Integer): TMultiChannelAmplitudeArray;
 var
 i, j: Integer;
 TimeStep: Double;
 begin
 TimeStep := 1.0 / SampleRate;
 SetLength(Result, NumChannels);
 for j := 0 to NumChannels - 1 do
 SetLength(Result[j], Length(Nodes) * Round(SampleRate * TimeStep));
 for i := 0 to High(Result[0]) do
 begin
 for j := 0 to NumChannels - 1 do
 begin
 Result[j][i] := Nodes[i div Round(SampleRate * TimeStep)].GetAmplitudeAtTime(j, i * TimeStep);
 end;
 end;
 end;
 end.
- Код: Выделить всё
- unit vatypesunit;
 {$MODE OBJFPC}
 interface
 type
 // Тип для хранения амплитуды звука
 TAmplitude = Double;
 // Тип для хранения контрольных точек кривой Безье
 // Тип для хранения массива амплитуд (растровое представление звука)
 TAmplitudeArray = array of TAmplitude;
 // Тип для хранения временной метки узла
 TTimeStamp = Double;
 // Тип для хранения многоканального массива амплитуд
 TMultiChannelAmplitudeArray = array of TAmplitudeArray;
 // Тип для хранения контрольных точек кривой Безье
 TBezierControlPoint = record
 X, Y: TAmplitude;
 end;
 implementation
 end.
- Код: Выделить всё
- unit wavfileunit;
 {$mode objfpc}{$H+}
 interface
 uses
 Classes, SysUtils, BaseUnix, Unix;
 type
 // Тип для хранения массива амплитуд
 TAmplitudeArray = array of Double;
 // Заголовок WAV-файла
 TWAVHeader = packed record
 RIFF: array[0..3] of Char;
 FileSize: Cardinal;
 WAVE: array[0..3] of Char;
 end;
 // Структура для хранения информации о формате WAV-файла
 TFmtChunk = packed record
 ChunkSize: Cardinal;
 AudioFormat: Word;
 NumChannels: Word;
 SampleRate: Cardinal;
 ByteRate: Cardinal;
 BlockAlign: Word;
 BitsPerSample: Word;
 ExtensionSize: Word; // Размер расширения (для WAVE_FORMAT_EXTENSIBLE)
 end;
 // Основной класс для работы с WAV-файлами
 TWAVFile = class
 private
 FFileName: string;
 FHeader: TWAVHeader;
 FFmtChunk: TFmtChunk;
 FDataChunkSize: Cardinal;
 FDataOffset: Integer;
 FFileHandle: cint;
 public
 constructor Create(const FileName: string);
 destructor Destroy; override;
 // Загрузка WAV-файла
 procedure LoadFromFile(const FileName: string);
 // Сохранение WAV-файла
 procedure SaveToFile(const FileName: string; const Amplitudes: array of TAmplitudeArray; SampleRate: Cardinal; NumChannels: Word; BitsPerSample: Word);
 // Чтение данных из WAV-файла в массив амплитуд
 function ReadAmplitudes(out Amplitudes: array of TAmplitudeArray): Integer;
 // Запись массива амплитуд в WAV-файл
 procedure WriteAmplitudes(const Amplitudes: array of TAmplitudeArray);
 // Получение информации о WAV-файле
 function GetSampleRate: Cardinal;
 function GetNumChannels: Word;
 function GetBitsPerSample: Word;
 function GetDataChunkSize: Cardinal;
 end;
 implementation
 constructor TWAVFile.Create(const FileName: string);
 begin
 FFileName := FileName;
 FFileHandle := -1;
 end;
 destructor TWAVFile.Destroy;
 begin
 if FFileHandle <> -1 then
 FpClose(FFileHandle);
 inherited Destroy;
 end;
 procedure TWAVFile.LoadFromFile(const FileName: string);
 var
 chunkID: array[0..3] of Char;
 chunkSize: Cardinal;
 begin
 FFileHandle := FpOpen(FileName, O_RDONLY);
 if FFileHandle = -1 then
 raise Exception.Create('Ошибка открытия файла');
 // Чтение заголовка WAV-файла
 if FpRead(FFileHandle, FHeader, SizeOf(TWAVHeader)) <> SizeOf(TWAVHeader) then
 raise Exception.Create('Ошибка чтения заголовка WAV');
 if (String(FHeader.RIFF) <> 'RIFF') or (String(FHeader.WAVE) <> 'WAVE') then
 raise Exception.Create('Неверный формат WAV файла');
 // Поиск и чтение fmt-секции
 while FpRead(FFileHandle, chunkID, 4) = 4 do
 begin
 if FpRead(FFileHandle, chunkSize, 4) <> 4 then
 Break;
 if String(chunkID) = 'fmt ' then
 begin
 if chunkSize < 16 then
 raise Exception.Create('Ошибка: размер fmt-секции слишком мал');
 FFmtChunk.ChunkSize := chunkSize;
 if FpRead(FFileHandle, FFmtChunk.AudioFormat, 16) <> 16 then
 raise Exception.Create('Ошибка чтения fmt-секции');
 // Пропускаем оставшиеся байты, если это расширенный формат
 if FFmtChunk.ChunkSize > 16 then
 FpLseek(FFileHandle, FFmtChunk.ChunkSize - 16, SEEK_CUR);
 end
 else if String(chunkID) = 'data' then
 begin
 FDataChunkSize := chunkSize;
 FDataOffset := FpLseek(FFileHandle, 0, SEEK_CUR);
 Break;
 end
 else
 begin
 FpLseek(FFileHandle, chunkSize, SEEK_CUR);
 end;
 end;
 end;
 procedure TWAVFile.SaveToFile(const FileName: string; const Amplitudes: array of TAmplitudeArray; SampleRate: Cardinal; NumChannels: Word; BitsPerSample: Word);
 var
 FileHandle: cint;
 i, j: Integer;
 Sample: Int16;
 ChunkSize: Cardinal;
 AudioFormat: Word;
 begin
 FileHandle := FpOpen(FileName, O_WRONLY or O_CREAT or O_TRUNC, &666);
 if FileHandle = -1 then
 raise Exception.Create('Ошибка создания файла');
 try
 // Записываем заголовок RIFF
 FpWrite(FileHandle, 'RIFF', 4);
 ChunkSize := 36 + (Length(Amplitudes[0]) * NumChannels * (BitsPerSample div 8));
 FpWrite(FileHandle, @ChunkSize, SizeOf(ChunkSize));
 FpWrite(FileHandle, 'WAVE', 4);
 // Записываем fmt-секцию
 FpWrite(FileHandle, 'fmt ', 4);
 ChunkSize := 16; // Размер fmt-секции
 FpWrite(FileHandle, @ChunkSize, SizeOf(ChunkSize));
 AudioFormat := 1; // PCM
 FpWrite(FileHandle, @AudioFormat, SizeOf(AudioFormat));
 FpWrite(FileHandle, @NumChannels, SizeOf(NumChannels));
 FpWrite(FileHandle, @SampleRate, SizeOf(SampleRate));
 ChunkSize := SampleRate * NumChannels * (BitsPerSample div 8);
 FpWrite(FileHandle, @ChunkSize, SizeOf(Cardinal));
 ChunkSize := NumChannels * (BitsPerSample div 8);
 FpWrite(FileHandle, @ChunkSize, SizeOf(Word));
 FpWrite(FileHandle, @BitsPerSample, SizeOf(Word));
 // Записываем data-секцию
 FpWrite(FileHandle, 'data', 4);
 ChunkSize := Length(Amplitudes[0]) * NumChannels * (BitsPerSample div 8);
 FpWrite(FileHandle, @ChunkSize, SizeOf(ChunkSize));
 // Записываем амплитуды
 for i := 0 to High(Amplitudes[0]) do
 begin
 for j := 0 to NumChannels - 1 do
 begin
 Sample := Round(Amplitudes[j][i] * 32767);
 if FpWrite(FileHandle, @Sample, SizeOf(Sample)) <> SizeOf(Sample) then
 raise Exception.Create('Ошибка записи данных');
 end;
 end;
 finally
 FpClose(FileHandle);
 end;
 end;
 function TWAVFile.ReadAmplitudes(out Amplitudes: array of TAmplitudeArray): Integer;
 var
 i, j: Integer;
 Sample: Int16;
 begin
 FpLseek(FFileHandle, FDataOffset, SEEK_SET);
 Result := 0;
 // Проверка на наличие данных
 if FDataChunkSize = 0 then
 raise Exception.Create('Файл не содержит данных');
 // Вычисляем количество сэмплов
 Result := FDataChunkSize div (FFmtChunk.NumChannels * (FFmtChunk.BitsPerSample div 8));
 // Инициализация массива Amplitudes
 for j := 0 to High(Amplitudes) do
 SetLength(Amplitudes[j], Result);
 // Чтение данных
 for i := 0 to Result - 1 do
 begin
 for j := 0 to FFmtChunk.NumChannels - 1 do
 begin
 if FpRead(FFileHandle, @Sample, SizeOf(Sample)) <> SizeOf(Sample) then
 Break;
 Amplitudes[j][i] := Sample / 32768.0; // Нормализация амплитуды
 end;
 end;
 end;
 procedure TWAVFile.WriteAmplitudes(const Amplitudes: array of TAmplitudeArray);
 begin
 // Реализация метода (если требуется)
 end;
 function TWAVFile.GetSampleRate: Cardinal;
 begin
 Result := FFmtChunk.SampleRate;
 end;
 function TWAVFile.GetNumChannels: Word;
 begin
 Result := FFmtChunk.NumChannels;
 end;
 function TWAVFile.GetBitsPerSample: Word;
 begin
 Result := FFmtChunk.BitsPerSample;
 end;
 function TWAVFile.GetDataChunkSize: Cardinal;
 begin
 Result := FDataChunkSize;
 end;
 end.
А также vaplayer, который воспроизводит получившиеся .va файлы. Декларации alsa из wavplayer.
- Код: Выделить всё
- program vaplayer;
 {$mode objfpc}{$H+}
 uses
 Classes, SysUtils, BaseUnix, Unix, ALSA, vanodeunit, wavfileunit, vaformatunit, vatypesunit;
 // Восстановление массива амплитуд из узлов TVANode
 function ReconstructAmplitudeArray(const Nodes: TVANodeArray; SampleRate: Integer; NumChannels: Integer; NumSamples: Integer): TMultiChannelAmplitudeArray;
 var
 i, j: Integer;
 TimeStep: Double;
 NodeIndex: Integer;
 SamplesPerNode: Double;
 begin
 if Length(Nodes) = 0 then
 raise Exception.Create('Нет узлов для восстановления');
 if NumSamples = 0 then
 raise Exception.Create('Количество сэмплов не может быть равно нулю');
 // Инициализация массива амплитуд
 SetLength(Result, NumChannels);
 for j := 0 to NumChannels - 1 do
 SetLength(Result[j], NumSamples);
 // Восстановление амплитуд
 TimeStep := 1.0 / SampleRate;
 // Рассчитываем количество сэмплов на один узел
 SamplesPerNode := NumSamples / Length(Nodes);
 for i := 0 to High(Result[0]) do
 begin
 // Рассчитываем индекс узла, соответствующего текущему сэмплу
 NodeIndex := Trunc(i / SamplesPerNode);
 if NodeIndex >= Length(Nodes) then
 NodeIndex := High(Nodes); // Ограничиваем индекс последним узлом
 for j := 0 to NumChannels - 1 do
 begin
 Result[j][i] := Nodes[NodeIndex].GetAmplitudeAtTime(j, i * TimeStep);
 end;
 end;
 end;
 procedure PlayVAFile(const FileName: string);
 var
 Nodes: TVANodeArray;
 SampleRate, NumChannels: Integer;
 Amplitudes: TMultiChannelAmplitudeArray;
 alsaHandle: Psnd_pcm_t;
 alsaParams: Psnd_pcm_hw_params_t;
 format: snd_pcm_format_t;
 frames: snd_pcm_uframes_t;
 err: Integer;
 dir: Integer;
 rate: Cardinal;
 bufferSize: Integer;
 i, j: Integer;
 Sample: Int16;
 Buffer: array of Int16; // Буфер для передачи данных в ALSA
 BufferSizeInSamples: Integer;
 BufferPos: Integer;
 begin
 // Загрузка .va файла
 Nodes := LoadVAFromFile(FileName, SampleRate, NumChannels);
 // Восстановление амплитуд
 Amplitudes := ReconstructAmplitudeArray(Nodes, SampleRate, NumChannels, Length(Nodes) * Round(SampleRate * (1.0 / SampleRate)));
 // Отладочные сообщения
 WriteLn('Частота дискретизации файла: ', SampleRate, ' Гц');
 WriteLn('Количество каналов: ', NumChannels);
 WriteLn('Восстановлено амплитуд: ', Length(Amplitudes[0]));
 // Открываем ALSA устройство
 if snd_pcm_open(@alsaHandle, 'default', SND_PCM_STREAM_PLAYBACK, 0) < 0 then
 begin
 WriteLn('Ошибка открытия ALSA устройства');
 Exit;
 end;
 snd_pcm_hw_params_malloc(@alsaParams);
 snd_pcm_hw_params_any(alsaHandle, alsaParams);
 // Устанавливаем параметры ALSA
 snd_pcm_hw_params_set_access(alsaHandle, alsaParams, SND_PCM_ACCESS_RW_INTERLEAVED);
 snd_pcm_hw_params_set_format(alsaHandle, alsaParams, SND_PCM_FORMAT_S16_LE);
 snd_pcm_hw_params_set_channels(alsaHandle, alsaParams, NumChannels);
 rate := SampleRate; // Используем частоту дискретизации файла
 dir := 0;
 err := snd_pcm_hw_params_set_rate_near(alsaHandle, alsaParams, @rate, @dir);
 if err < 0 then
 WriteLn('Ошибка установки частоты дискретизации: ', snd_strerror(err));
 WriteLn('Используемая частота дискретизации: ', rate, ' Гц');
 // Устанавливаем размер буфера
 frames := 4096;
 err := snd_pcm_hw_params_set_buffer_size_near(alsaHandle, alsaParams, @frames);
 if err < 0 then
 WriteLn('Ошибка установки размера буфера: ', snd_strerror(err));
 err := snd_pcm_hw_params(alsaHandle, alsaParams);
 if err < 0 then
 WriteLn('Ошибка применения параметров: ', snd_strerror(err));
 // Инициализация буфера
 BufferSizeInSamples := 4096 * NumChannels; // Размер буфера в сэмплах
 SetLength(Buffer, BufferSizeInSamples);
 BufferPos := 0;
 // Воспроизведение амплитуд
 for i := 0 to High(Amplitudes[0]) do
 begin
 for j := 0 to NumChannels - 1 do
 begin
 // Нормализация амплитуд
 if Amplitudes[j][i] < -1.0 then Amplitudes[j][i] := -1.0;
 if Amplitudes[j][i] > 1.0 then Amplitudes[j][i] := 1.0;
 Sample := Round(Amplitudes[j][i] * 32767);
 // Запись сэмпла в буфер
 Buffer[BufferPos] := Sample;
 Inc(BufferPos);
 // Если буфер заполнен, отправляем его в ALSA
 if BufferPos >= BufferSizeInSamples then
 begin
 if snd_pcm_writei(alsaHandle, @Buffer[0], frames) < 0 then
 begin
 WriteLn('Ошибка записи в ALSA: ', snd_strerror(err));
 Break;
 end;
 BufferPos := 0; // Сбрасываем позицию буфера
 end;
 end;
 end;
 // Отправляем оставшиеся данные в ALSA
 if BufferPos > 0 then
 begin
 if snd_pcm_writei(alsaHandle, @Buffer[0], BufferPos div NumChannels) < 0 then
 begin
 WriteLn('Ошибка записи в ALSA: ', snd_strerror(err));
 end;
 end;
 // Завершение воспроизведения
 snd_pcm_drain(alsaHandle);
 snd_pcm_close(alsaHandle);
 end;
 begin
 if ParamCount < 1 then
 begin
 WriteLn('Использование: vaplayer <filename.va>');
 Exit;
 end;
 PlayVAFile(ParamStr(1));
 end.



