program voicestarter;
{$MODE OBJFPC}{$H+}{$RANGECHECKS ON}

{
    Voice Clock audio starter.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2025  Artyomov Alexander
    Used https://chat.deepseek.com/
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

uses
  BaseUnix, Unix, SysUtils, ALSA;

const
  SAMPLE_RATE = 44100;          // Частота дискретизации
//  SILENCE_THRESHOLD = 0.5;     // Порог уровня звука
  SILENCE_THRESHOLD = 0.9;     // Порог уровня звука
  BUFFER_SIZE = 4096;           // Размер буфера
  BEEP_FILE = 'beep.wav';       // Файл с коротким звуковым сигналом

var
  ShouldStop: Boolean = False;

procedure HandleSignal(Signal: cint); cdecl;
begin
  case Signal of
    SIGINT, SIGTERM:
      begin
        WriteLn('Получен сигнал завершения. Останавливаем программу...');
        ShouldStop := True;
        Halt;
      end;
  end;
end;

procedure PlayBeep(const FileName: string);
var
  alsaHandle: Psnd_pcm_t;
  alsaParams: Psnd_pcm_hw_params_t;
  frames: snd_pcm_uframes_t;
  err: Integer;
  rate: Cardinal;
  buffer: array of Byte;
  fd: cint;
  readSize: ssize_t;
  periodSize: snd_pcm_uframes_t;
  dir: Integer;
  numChannels: Integer;
  wavHeader: array[0..43] of Byte; // Заголовок WAV файла (44 байта)
begin
  // Открываем файл с сигналом
  fd := FpOpen(FileName, O_RDONLY);
  if fd = -1 then
  begin
    WriteLn('Ошибка открытия файла ', FileName);
    Exit;
  end;

  // Читаем заголовок WAV файла
  if FpRead(fd, wavHeader[0], SizeOf(wavHeader)) <> SizeOf(wavHeader) then
  begin
    WriteLn('Ошибка чтения заголовка WAV файла');
    FpClose(fd);
    Exit;
  end;

  // Извлекаем количество каналов из заголовка WAV
  numChannels := wavHeader[22] + (wavHeader[23] shl 8);
  WriteLn('Количество каналов: ', numChannels);

  // Открываем ALSA устройство для воспроизведения
  if snd_pcm_open(@alsaHandle, 'default', SND_PCM_STREAM_PLAYBACK, 0) < 0 then
  begin
    WriteLn('Ошибка открытия ALSA устройства для воспроизведения');
    FpClose(fd);
    Exit;
  end;

  snd_pcm_hw_params_malloc(@alsaParams);
  snd_pcm_hw_params_any(alsaHandle, alsaParams);
  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 := SAMPLE_RATE;
  dir := 0;
  err := snd_pcm_hw_params_set_rate_near(alsaHandle, alsaParams, @rate, @dir);
  if err < 0 then
    WriteLn('Ошибка установки частоты дискретизации: ', snd_strerror(err));

  // Устанавливаем размер периода (period size)
  periodSize := 1024; // Размер периода в кадрах
  err := snd_pcm_hw_params_set_period_size_near(alsaHandle, alsaParams, @periodSize, @dir);
  if err < 0 then
    WriteLn('Ошибка установки размера периода: ', snd_strerror(err));

  // Устанавливаем размер буфера (buffer size)
  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));

  snd_pcm_hw_params_free(alsaParams);

  // Читаем и воспроизводим данные
  SetLength(buffer, periodSize * numChannels * 2); // 16-битные данные (2 байта на сэмпл)
  while True do
  begin
    readSize := FpRead(fd, buffer[0], Length(buffer));
    if readSize <= 0 then Break;

    err := snd_pcm_writei(alsaHandle, @buffer[0], readSize div (numChannels * 2));
    if err = -EPIPE then
    begin
      WriteLn('Underrun!');
      snd_pcm_prepare(alsaHandle);
    end
    else if err < 0 then
    begin
      WriteLn('Ошибка записи в ALSA: ', snd_strerror(err));
      Break;
    end;
  end;

  snd_pcm_drain(alsaHandle);
  snd_pcm_close(alsaHandle);
  FpClose(fd);
end;

procedure ListenMicrophone;
var
  alsaHandle: Psnd_pcm_t;
  alsaParams: Psnd_pcm_hw_params_t;
  buffer: array of Int16;
  i: Integer;
  maxAmplitude: Double;
  rate: Cardinal;
  frames: snd_pcm_uframes_t;
  err: Integer;
begin
  if snd_pcm_open(@alsaHandle, 'default', SND_PCM_STREAM_CAPTURE, 0) < 0 then
  begin
    WriteLn('Ошибка открытия ALSA устройства для захвата');
    Exit;
  end;

  snd_pcm_hw_params_malloc(@alsaParams);
  snd_pcm_hw_params_any(alsaHandle, alsaParams);
  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, 1); // Моно

  rate := SAMPLE_RATE;
  err := snd_pcm_hw_params_set_rate_near(alsaHandle, alsaParams, @rate, nil);
  if err < 0 then
    WriteLn('Ошибка установки частоты дискретизации: ', snd_strerror(err));

  frames := BUFFER_SIZE;
  err := snd_pcm_hw_params_set_buffer_size_near(alsaHandle, alsaParams, @frames);
  if err < 0 then
    WriteLn('Ошибка установки размера буфера: ', snd_strerror(err));

  if snd_pcm_hw_params(alsaHandle, alsaParams) < 0 then
  begin
    WriteLn('Ошибка применения параметров ALSA');
    snd_pcm_hw_params_free(alsaParams);
    snd_pcm_close(alsaHandle);
    Exit;
  end;

  snd_pcm_hw_params_free(alsaParams);

  SetLength(buffer, BUFFER_SIZE);

  WriteLn('Слушаю микрофон...');
  while not ShouldStop do
  begin
    if snd_pcm_readi(alsaHandle, @buffer[0], BUFFER_SIZE) < 0 then
    begin
      WriteLn('Ошибка чтения данных из ALSA');
      Break;
    end;

    // Анализируем уровень звука
    maxAmplitude := 0;
    for i := 0 to BUFFER_SIZE - 1 do
    begin
      if Abs(buffer[i] / 32768.0) > maxAmplitude then
        maxAmplitude := Abs(buffer[i] / 32768.0);
    end;

    // Если уровень звука превышает порог
    if maxAmplitude > SILENCE_THRESHOLD then
    begin
      WriteLn('Слышу звук!');
//      PlayBeep(BEEP_FILE); // Воспроизводим звуковой сигнал
      Break;    // Завершаем программу
    end;
  end;

  snd_pcm_close(alsaHandle);
end;

begin
  fpSignal(SIGINT, @HandleSignal);
  fpSignal(SIGTERM, @HandleSignal);

while not ShouldStop do begin
  ListenMicrophone;
  fpSystem('vc2');
  WriteLn('Сплю');
  Sleep(120000);
end;

end.