#include "windows.bi"
#include "win/mmsystem.bi"
#include "crt.bi"
#include "RHVoice.bi"

  declare function  callback cdecl (byval samples as const short ptr,byval num_samples as integer,byval events as const RHVoice_event ptr,byval num_events as integer,byval message as RHVoice_message) as integer
declare sub ShowErrorMessage()
#define nhdr 3
dim shared wFormat as WaveFormatEx ' структура формата звуковых данных
dim  shared wHdr(nhdr) as wavehdr ' структура заголовка буфера
dim shared buf(nhdr) as zstring * 9600 ' буфер для звуковых данных
dim shared numbuf as integer = 0
dim shared lenBuf as integer ' длина буфера
dim shared hOut as HANDLE ' хэндл звукового устройства
dim shared hEvent as HANDLE ' хэндл объекта события
  dim msg as RHVoice_message = 0 ' сообщения RHVoice
dim prog_path as const string = ExePath() ' получим путь к  директории из которой запущена программа
dim Data_path as const string = prog_path & "/RHVoice-data" ' путь к данным синтезатора - там хранятся голоса
dim cfg_path as const string = prog_path & "/RHVoice-config" 'путь к настройкам и словарям синтезатора

dim sample_rate as uInteger ' частота дискретизации

  ' инициализируем RHVoice. функция возвращает частоту дискретизации.  в случае ошибки нуль.
      sample_rate=RHVoice_initialize _
(Data_path, _ ' путь к данным синтезатора
cast(RHvoice_callback ptr, @callback), _ ' указатель на функцию обратного вызова
cfg_path, _ ' путь к настройкам синтезатора
RHVoice_preload_voices) ' опция инициализации - предзагрузка голосов
if sample_rate = 0 then
print "can't initialize RHVoice"
end 1
end if
   ' заполним структуру формата звуковых данных
wFormat.wFormatTag=1 ' данные в формате pcm 
wFormat.nChannels= 1 'число каналов 1
wFormat.nSamplesPerSec = sample_rate ' частота дискретизации
wFormat.nAvgBytesPerSec = sample_rate*2 ' частота выдачи байт
wFormat.nBlockAlign= 2 ' выравнивание
wFormat.wBitsPerSample = 16 ' 16 бит на семпл
   ' создадим объект события
hEvent = CreateEvent(NULL,false,false,NULL)
if waveOutOpen(@hOut, -1,@wFormat,cptr(DWORD,hEvent),0,CALLBACK_EVENT) then ' если при открытии устройства произошла ошибка
ShowErrorMessage() ' покажем сообщение об ошибке
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if


dim voice as integer =           RHVoice_find_voice("Aleksandr") ' найдем голос по имени Aleksandr
if voice > 0 then ' если номер голоса больше нуль, значит голос найден
            RHVoice_set_voice(voice) ' установим голос по  номеру
else
print "no voice"
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if

dim testString as string * 512 = "привет. меня зовут александр. я русский синтезатор речи RHVoice. сотворила меня ольга яковлева, за что ей огромное спасибо! "
dim wideString as wstring * 1024
MultiByteToWideChar(CP_ACP,0,strPtr(testString),len(testString),wideString,len(testString)*2)
      msg=RHVoice_new_message_utf16 _ ' подготовим текст, который будем говорить
(cast(any ptr,@wideString), _ ' строка с текстом
len(wideString), _ ' длина строки
RHVoice_message_text) ' тип сообщения
      if msg= 0 then ' если ошибка
          RHVoice_terminate() ' завершаем работу RHVOICE '
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if
      RHVoice_speak(msg) ' скажем наш текст
      RHVoice_delete_message(msg) ' удаляем сообщение

' выберем другой голос
voice =           RHVoice_find_voice("Elena") ' найдем голос по имени Elena
if voice > 0 then ' если номер голоса больше нуль, значит голос найден
            RHVoice_set_voice(voice) ' установим голос по  номеру
else
print "no voice"
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if

testString = "а меня завут елена. я женский голос синтезатора RHVoice. меня тоже создала ольга."

MultiByteToWideChar(CP_ACP,0,strPtr(testString),len(testString),wideString,len(testString)*2)
      msg=RHVoice_new_message_utf16 _ ' подготовим текст, который будем говорить
(cast(any ptr,@wideString), _ ' строка с текстом
len(wideString), _ ' длина строки
RHVoice_message_text) ' тип сообщения
      if msg= 0 then ' если ошибка
          RHVoice_terminate() ' завершаем работу RHVOICE '
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if
      RHVoice_speak(msg) ' скажем наш текст
      RHVoice_delete_message(msg) ' удаляем сообщение

' вернемся опять к александру
voice =           RHVoice_find_voice("Aleksandr") ' найдем голос по имени Aleksandr
if voice > 0 then ' если номер голоса больше нуль, значит голос найден
            RHVoice_set_voice(voice) ' установим голос по  номеру
else
print "no voice"
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if
RHVoice_set_rate(0.5) ' установим скорость помедленнее
testString = "Я такой пьяный, что язык не ворочается. надо еще за бутылкой сходить."
MultiByteToWideChar(CP_ACP,0,strPtr(testString),len(testString),wideString,len(testString)*2)
      msg=RHVoice_new_message_utf16 _ ' подготовим текст, который будем говорить
(cast(any ptr,@wideString), _ ' строка с текстом
len(wideString), _ ' длина строки
RHVoice_message_text) ' тип сообщения
      if msg= 0 then ' если ошибка
          RHVoice_terminate() ' завершаем работу RHVOICE '
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if
      RHVoice_speak(msg) ' скажем наш текст
      RHVoice_delete_message(msg) ' удаляем сообщение

RHVoice_set_pitch(0.0) ' установим питч по нулям
testString = "ё маё! я уже совсем допился, голос сел нафиг. что же дальше со мной будет! страшно как-то, но за третьей всё равно пойду."
MultiByteToWideChar(CP_ACP,0,strPtr(testString),len(testString),wideString,len(testString)*2)
      msg=RHVoice_new_message_utf16 _ ' подготовим текст, который будем говорить
(cast(any ptr,@wideString), _ ' строка с текстом
len(wideString), _ ' длина строки
RHVoice_message_text) ' тип сообщения
      if msg= 0 then ' если ошибка
          RHVoice_terminate() ' завершаем работу RHVOICE '
closeHandle(hOut)
CloseHandle(hEvent) ' закроем хэндл объекта события
end 1
end if
      RHVoice_speak(msg) ' скажем наш текст
      RHVoice_delete_message(msg) ' удаляем сообщение


      RHVoice_terminate() ' завершим работу RHVoice
WaveOutClose(hOut) ' закроем устройство воспроизведения
CloseHandle(hEvent) ' закроем хэндл объекта события
end

   ' функция обратного вызова
   ' в ней мы будем выводить звуковые данные на звуковую карту
function  callback cdecl _
(byval samples as const short ptr, _ ' указатель на буфер со звуковыми данными
byval num_samples as integer, _ ' число семплов(отсчетов) - в нашем случае один семпл 16 бит - 2 байта(short)
byval events as const RHVoice_event ptr, _ ' указатель на события
byval num_events as integer, _ ' число событий
byval message as RHVoice_message) as integer ' сообщение

lenBuf = num_samples*sizeof(short)
memcpy(@buf(numbuf),cast(any ptr,samples),lenbuf)
   ' заполним структуру заголовка буфера воспроизведения
wHdr(numbuf).lpData = @buf(numbuf)
wHdr(numbuf).dwBufferLength = lenbuf

   ' подготовим заголовок буфера
waveOutPrepareHeader(hOut,@wHdr(numbuf),32)

    ' проиграем буфер
WaveOutWrite(hOut,@wHdr(numbuf),32)
WaitForSingleObject(hEvent, infinite) ' ждем пока драйвер не вернет буфер

WaveOutUnprepareHeader(hOut,@wHdr(numbuf),32)

numbuf +=1
if numbuf > 3 then numbuf = 0

return 1
end function

sub ShowErrorMessage()
dim as dword errid = GetLastError()
dim as zstring * 1024 formatString
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,errid,0,@formatString,1023,0)
messageBox(0,formatString,"RTC",MB_ICONERROR or MB_TASKMODAL)
end sub
