Уважаемые посетители! Форум CQHAM.RU существует исключительно за счет показа рекламы. Мы будем благодарны, если Вы не будете блокировать рекламу на нашем Форуме. Просим внести cqham.ru в список исключений для Вашего блокировщика рекламы.
Показано с 1 по 6 из 6

Тема: Dit Dot длительность, как функция от WPM

  1. #1
    Аватар для RX1AL
    Регистрация
    06.02.2009
    Адрес
    Санкт Петербург
    Сообщений
    3,812

    Dit Dot длительность, как функция от WPM

    Возможно данный вопрос покажется слишком глупым, но тем не менее...
    Для программной генерации кода Морзе (CW манипуляция) требуется
    рассчитывать длительность точки, в зависимости от выбранного значения
    WPM. Вопрос в том, как сделать это проще? Одну ссылку нашел, но она не
    дает ответа на вопрос:
    http://www.answers.com/topic/morse-code
    По сути метод (функция) принимает на вход только три параметра (Pitch, WPM, Text),
    где Pitch - тон сигнала в килогерцах, WPM - скорость передачи, а Text - текст для
    передачи в звуковую карточку. Функция должна уметь рассчитывать длительность
    точки (тире и пауз) исходя из WPM только. Где найти реализацию такой функции?
    Код на С++, Джава и так далее - любой подойдет. Тапками не бросать... :wink:
    Михаил, ex UV1AL, RG1L, сейчас OE6MAF, HB9/OE6MAF, DL/OE6MAF
    DIG #5645, EPC #5908, #5909, KDR #21


  2. #2
    Вот так делал я. Может и не так совсем... Считайте это как Open Source GPL... Все исправления выкладывайте здесь. Думаю будет интересно.

    Код:
    unit cwMain;
    
    interface
    
    uses
      MMsystem, SysUtils, Classes, Windows, Messages, suCW;
    
      procedure cwSetFormat(const CwFormat: TCwFormat); stdcall;
      procedure cwMute(const bMute: WordBool); stdcall;
      function cwOpen(const iNumSoundCard: Integer; const bManual: WordBool;
        const hHwnd: THandle; CWProc: TCWProc): WordBool; stdcall;
      procedure cwSend(const lKeyCode: Smallint); stdcall;
      procedure cwStop; stdcall;
      procedure cwClose; stdcall;
      procedure cwSetOmniRig(const bEnabled: WordBool;
        const iCWPin, iRigNumber: Integer); stdcall;
      
    implementation
    
    uses cwOmniRigModule, OmniRig_TLB;
    
    const
      SAMPLE_RATE        = 44100;        // Частота дискретизации
      SINE_TABLE_SAMPLES = 1 shl 15;     // Номер сэмпла в таблице синуса
      MAX_BUFFER_SAMPLES = 56000;        // Размер буфера
      FRACT_BITS         = 15;
    
    type
      { Класс автоматического CW потока. }
      TCwKeyboardOut = class(TThread)
      private
        procedure DoCWProc(const H: WordBool);
      protected
        procedure Execute; override;
      end;
    
      { Класс ручного (от ключа) CW потока. }
      TCwKeyOut = class(TThread)
      private
        procedure DoCWProc(const H: WordBool);
      protected
        procedure Execute; override;
      end;
    
      { Для 16-битного аудио. }
      TAudioSample = -32767..32767;
    
      PSineTable = ^TSineTable;
      TSineTable = array [0..SINE_TABLE_SAMPLES - 1] of TAudioSample;
    
      { Буфер. }
      PBuffer = ^TBuffer;
      TBuffer = array [0..MAX_BUFFER_SAMPLES - 1] of TAudioSample;
    
    var
      { Переменные CW. }
      pWavHdr      : array [0..1] of PWaveHdr;
      pWavBuf      : array [0..1] of PBuffer;         // Буферы вывода AUDIO
      hWaveHdr     : array [0..1] of THandle;
      hWavBuf      : array [0..1] of THandle;
      pSinTable    : PSineTable;
      CwProp       : TCwFormat;                       // Свойства CW
      iLenDash     : Integer;                         // Длина тире
      CwKeyboardOut: TCwKeyboardOut;                  // Поток CW
      CwKeyOut     : TCwKeyOut;                       // Ручной CW поток
      bCwOpen      : Boolean;                         // Флаг открытия CW
      hOut         : HWAVEOUT;                        // Идентификатор устройства вывода CW
      hOutEvent    : THandle;                         // Событие вывода CW
      sStrBuf      : array [0..MAXBYTE] of String;    // Временный строковый буфер
      lLetter      : array [0..MAXBYTE] of Smallint;  // Буфер кода символа для CallBack
      lInc         : SmallInt = 0;                    // Индекс массива
      bStop        : Boolean;                         // Флаг останова передачи
      hSendHwnd    : THandle;                         // HWND текстового поля куда будет возвращён символ
      iMemLevel    : Integer;                         // Уровень начальной громкости
      CWProcedure  : TCWProc;
    
    resourcestring
      RES_MB_ERR_GAME  = 'Игровое устройство (джойстик) не установлено.' + #13#10 +
                         'Для его установки воспользуйтесь панелью управления Windows.';
      RES_MB_ERR_MEM   = 'Системная ошибка. Ошибка выделения памяти.';
      RES_MB_ERR_EVENT = 'Системная ошибка. Ошибка создания события.';
      RES_MB_ERR_TITLE = 'Ошибка CW';
    
    
    // =============================================================================
    
    { Вывод окна с сообщениями об ошибках загрузки библиотек.
      Text - текст сообщения. }
    procedure ShowMessage(Text: String; dwErr: MMRESULT = 0);
    var
      hHwnd   : HWND;
      lpBufErr: array [0..MAXBYTE] of Char;
    begin
      hHwnd := GetActiveWindow;
    
      if dwErr <> MMSYSERR_NOERROR then
        begin
          waveOutGetErrorText(dwErr, lpBufErr, MAXBYTE + 1);
          Text := String(lpBufErr);
        end;
    
      MessageBox(hHwnd, PChar(Text), PChar(RES_MB_ERR_TITLE), MB_ICONERROR);
    end;
    
    // =============================================================================
    
    { Таблица синуса. }
    procedure DoSineTable;
    var
      i, uMax   : Cardinal;
      dVol, dSin: Double;
    begin
      uMax := SINE_TABLE_SAMPLES - 1;
    
      { Громкость. }
    	dVol := (CwProp.lLevel / 44.1) * 32767.0;
    
      dSin := Pi / SINE_TABLE_SAMPLES;
    
      for i := 0 to uMax do
        pSinTable^[i] := Round(dVol * Sin(2.0 * i * dSin));
    end;
    
    procedure Sinewave(Buffer: PBuffer; var Index, Samples: Integer);
    var
      Sample   : TAudioSample;
      iDangle  : Integer;
      i, j     : Integer;
      iMaxAngle: Integer;
      iMin     : Integer;
      iAngle   : Integer;
      dFreq    : Double;
    begin
    	iAngle := 0;
      dFreq := CwProp.lFreq;
      iDangle := Round((SINE_TABLE_SAMPLES shl FRACT_BITS) * dFreq / SAMPLE_RATE);
      iMaxAngle := (SINE_TABLE_SAMPLES shl FRACT_BITS) - 1;
    
      for i := 1 to Samples do
        begin
          Sample := pSinTable^[iAngle shr FRACT_BITS];
          if i < 64 then
            for j := i to 64 do
              Sample := (Sample * 44) div 45;
    
          iMin := Samples - 256;
    
          if i > iMin then
            for j := iMin to i do
              Sample := (Sample * 44) div 45;
    
          Buffer^[Index] := Sample;
          Inc(Index);
          Inc(iAngle, iDangle);
          iAngle := iAngle and iMaxAngle;
        end;
    end;
    
    procedure Silence(Buffer: PBuffer; var Index, Samples: Integer);
    var
      i, iMax: Integer;
    begin
      iMax := Samples - 1;
      for i := 0 to iMax do
        begin
          Buffer^[Index] := 0;
          Inc(Index);                               
        end;
    end;
    
    // =============================================================================
    
    { Закрытие аудиокарты и освобождение памяти. }
    procedure CloseAudio;
    begin
      if hOut <> 0 then
        begin
          waveOutReset(hOut);
          waveOutClose(hOut);
          hOut := 0;
        end;
    
      { Закрываем указатель на событие. }
      if hOutEvent <> INVALID_HANDLE_VALUE then
        begin
          CloseHandle(hOutEvent);
          hOutEvent := INVALID_HANDLE_VALUE;
        end;
    
      { Освобождение памяти. }
      if hWaveHdr[0] <> 0 then
        begin
          GlobalUnlock(hWaveHdr[0]);
          GlobalFree(hWaveHdr[0]);
        end;
    
      if hWavBuf[0] <> 0 then
        begin
          GlobalUnlock(hWavBuf[0]);
          GlobalFree(hWavBuf[0]);
        end;
    
      if hWaveHdr[1] <> 0 then
        begin
          GlobalUnlock(hWaveHdr[1]);
          GlobalFree(hWaveHdr[1]);
        end;
    
      if hWavBuf[1] <> 0 then
        begin
          GlobalUnlock(hWavBuf[1]);
          GlobalFree(hWavBuf[1]);
        end;
    
      FreeMem(pSinTable, SizeOf(TSineTable));
      pSinTable := nil;
      bCwOpen := False;
    end;
    
    // =============================================================================
    
    { Установка свойств CW сигнала. CwFormat - структура формата телеграфного
      сигнала. }
    procedure cwSetFormat(const CwFormat: TCwFormat); stdcall;
    begin
      CwProp := CwFormat;
      with CwProp do
        begin
          iSpeed := ((SAMPLE_RATE * 12) div 10) div CwFormat.iSpeed;
          iLenDash := iSpeed * lPointDash;
         // iSpace := iSpeed * (2 + CwFormat.iSpace);
          iMemLevel := lLevel;
        end;
    
      { Синус. }
      { Память под таблицу синуса. }
      if pSinTable = nil then GetMem(pSinTable, SizeOf(TSineTable));
      DoSineTable;
    end;
    
    // =============================================================================
    
    { Отключение/включение звука. iMute - флаг состояния }
    procedure cwMute(const bMute: WordBool); stdcall;
    begin
      with CwProp do
        if bMute then
          begin
            iMemLevel := lLevel;
            lLevel := 0;
          end
        else
          lLevel := iMemLevel;
    
      { Синус. }
      { Память под таблицу синуса. }
      if pSinTable = nil then
        GetMem(pSinTable, SizeOf(TSineTable));
      DoSineTable;
    end;
    
    // =============================================================================
    
    { Открытие устройства аудиовывода. Здесь
     iNumSoundCard - номер открываемой аудиокарты;
     hHwnd - hwnd окна получающего сообщение с кодом текущего передаваемого
     символа. }
    function cwOpen(const iNumSoundCard: Integer; const bManual: WordBool;
      const hHwnd: THandle; CWProc: TCWProc): WordBool; stdcall;
    var
      iBufferBytes: Integer;
      WavFormat   : TWAVEFORMATEX;
      dwErr       : MMRESULT;
      JCaps       : TJoyCaps;
    begin
      Result := False;
    
      { Закрываем устройство если оно открыто. }
      if bCwOpen then cwClose;
    
      try
        { Создаём событие. }
        hOutEvent := CreateEvent(nil, False, False, nil);
        if hOutEvent = INVALID_HANDLE_VALUE then
          begin
            ShowMessage(RES_MB_ERR_EVENT);
            Exit;
          end;
    
        { Открытие устройства вывода аудио. }
        FillChar(WavFormat, SizeOf(WavFormat), #0);
        with WavFormat do
          begin
            wFormatTag := WAVE_FORMAT_PCM;                   // Используется PCM формат
            nChannels := 1;                                  // Число каналов
            nSamplesPerSec := SAMPLE_RATE;                   // Частота дискретизации
            wBitsPerSample := 16;                            // Выборка
            nBlockAlign := wBitsPerSample div 8 * nChannels; // Число байт в выборке
            nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; // Число байт в секундном интервале
            cbSize := 0;                                     // Не используется
          end;
        dwErr := WaveOutOpen(@hOut, iNumSoundCard, @WavFormat,
                             hOutEvent, 0, CALLBACK_EVENT);
        if dwErr <> MMSYSERR_NOERROR then
          begin
            CloseAudio;
            ShowMessage(EmptyStr, dwErr);
            Exit;
          end;
    
        { Выделение памяти под заголовки. }
        hWaveHdr[0] := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(TWaveHdr));
        if hWaveHdr[0] = 0 then
          begin
            CloseAudio;
            ShowMessage(RES_MB_ERR_MEM);
            Exit;
          end;
        pWavHdr[0] := PWaveHdr(GlobalLock(hWaveHdr[0]));
    
        hWaveHdr[1] := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(TWaveHdr));
        if hWaveHdr[1] = 0 then
          begin
            CloseAudio;
            ShowMessage(RES_MB_ERR_MEM);
            Exit;
          end;
        pWavHdr[1] := PWaveHdr(GlobalLock(hWaveHdr[1]));
    
        { Размер буфера. }
    	  iBufferBytes := 2 * MAX_BUFFER_SAMPLES;
    
        { Память под буферы. }
        hWavBuf[0] := GlobalAlloc(GHND or GMEM_SHARE, iBufferBytes);
        if hWavBuf[0] = 0 then
          begin
            CloseAudio;
            ShowMessage(RES_MB_ERR_MEM);
            Exit;
          end;
        pWavBuf[0] := PBuffer(GlobalLock(hWavBuf[0]));
    
        hWavBuf[1] := GlobalAlloc(GHND or GMEM_SHARE, iBufferBytes);
        if hWavBuf[1] = 0 then
          begin
            CloseAudio;
            ShowMessage(RES_MB_ERR_MEM);
            Exit;
          end;
        pWavBuf[1] := PBuffer(GlobalLock(hWavBuf[1]));
    
        { Подготовка буферов. }
        FillChar(pWavHdr[0]^, SizeOf(TWaveHdr), #0);
        pWavHdr[0].lpData := PChar(pWavBuf[0]);
        FillChar(pWavHdr[1]^, SizeOf(TWaveHdr), #0);
        pWavHdr[1].lpData := PChar(pWavBuf[1]);
    
        { Обнуление текстового буфера. }
        FillChar(sStrBuf, SizeOf(sStrBuf), #0);
    
        { Начальные значения переменных. }
        hSendHwnd := hHwnd;
        lInc := 0;
        bStop := False;
        CWProcedure := CWProc;
    
        { Создание потока вывода CW. }
        CwKeyboardOut := TCwKeyboardOut.Create(True);
        CwKeyboardOut.Priority := tpTimeCritical;
    
        { Запуск потока опроса ключа. }
        if bManual then
          begin
            { Проверяем наличие игрового порта }
            dwErr := joyGetDevCaps(JOYSTICKID1, @JCaps, SizeOf(TJoyCaps));
            if dwErr = JOYERR_NOERROR then
              begin
                CwKeyOut := TCwKeyOut.Create(True);
                with CwKeyOut do
                  begin
                    Priority := tpHighest;
                    Resume;
                  end;
              end
            else
              ShowMessage(RES_MB_ERR_GAME);
          end;
    
        { Устройство открыто }
        bCwOpen := True;
    
        Result := True;
      except
        CloseAudio;
      end;
    end;
    
    // =============================================================================
    
    { Преобразование кода символа в код CW. KeyCode - код символа. }
    function CoderCw(const KeyCode: Smallint): String;
    begin
      case KeyCode of
        32:                Result := '2';       // Пробел
        33:                Result := '110011';  // !
        34:                Result := '010010';  // "
        39:                Result := '011110';  // '
        40, 41:            Result := '101101';  // ()
        44:                Result := '010101';  // ,
        45:                Result := '100001';  // -
        46:                Result := '000000';  // .
        48:                Result := '11111';   // 0
        49:                Result := '01111';   // 1
        50:                Result := '00111';   // 2
        51:                Result := '00011';   // 3
        52:                Result := '00001';   // 4
        53:                Result := '00000';   // 5
        54:                Result := '10000';   // 6
        55:                Result := '11000';   // 7
        56:                Result := '11100';   // 8
        57:                Result := '11110';   // 9
        58:                Result := '111000';  // :
        59:                Result := '101010';  // ;
        61:                Result := '10001';   // =
        63:                Result := '001100';  // ?
        92, 47:            Result := '10010';   // / \
        95:                Result := '001101';  // _
        97, 65, 224, 192:  Result := '01';      // A
        98, 66, 225, 193:  Result := '1000';    // B
        99, 67, 246, 214:  Result := '1010';    // C
        100, 68, 228, 196: Result := '100';     // D
        101, 69, 229, 197,
        168, 184         : Result := '0';       // E
        102, 70, 244, 212: Result := '0010';    // F
        103, 71, 227, 195: Result := '110';     // G
        104, 72, 245, 213: Result := '0000';    // H
        105, 73, 232, 200: Result := '00';      // I
        106, 74, 233, 201: Result := '0111';    // J
        107, 75, 234, 202: Result := '101';     // K
        108, 76, 235, 203: Result := '0100';    // L
        109, 77, 236, 204: Result := '11';      // M
        110, 78, 237, 205: Result := '10';      // N
        111, 79, 238, 206: Result := '111';     // O
        112, 80, 239, 207: Result := '0110';    // P
        113, 81, 249, 217: Result := '1101';    // Q
        114, 82, 240, 208: Result := '010';     // R
        115, 83, 241, 209: Result := '000';     // S
        116, 84, 242, 210: Result := '1';       // T
        117, 85, 243, 211: Result := '001';     // U
        118, 86, 230, 198: Result := '0001';    // V
        119, 87, 226, 194: Result := '011';     // W
        120, 88, 252, 220: Result := '1001';    // X
        121, 89, 251, 219: Result := '1011';    // Y
        122, 90, 231, 199: Result := '1100';    // Z
        248, 216:          Result := '1111';    // Ш
        253, 221:          Result := '00100';   // Э
        254, 222:          Result := '0011';    // Ю
        255, 223:          Result := '0101';    // Я
        247, 215:          Result := '1110';    // Ч
        250, 218:          Result := '1001';    // Ъ
    
        { Специальные символы }
        36:                Result := '000101';  // $ (SK)
        38:                Result := '1000101'; // & (BK)
        42:                Result := '10001';   // * (BT)
        43:                Result := '01010';   // + (AR)
        60:                Result := '01000';   // < (AS)
        62:                Result := '10110';   // > (KN)
      end;
    end;
    
    // =============================================================================
    
    { Заполнение буфера и передача текста. lKeyCode - код передаваемого символа. }
    procedure cwSend(const lKeyCode: Smallint); stdcall;
    begin
      { Если устройство закрыто. }
      if not bCwOpen then Exit;
    
      { Превышена максимальная длина
        массива буфера. }
      if lInc > MAXBYTE then Exit;
        
      { Присваиваем значение массиву. }
      sStrBuf[lInc] := CoderCw(lKeyCode);
    
      { Код символа. }
      lLetter[lInc] := lKeyCode;
    
      { Увеличиваем индекс
        массива. }
      Inc(lInc);
    
      { Запуск потока на выполнение,
        если он не запущен. }
      bStop := False;
      with CwKeyboardOut do
        if Suspended then Resume;
    end;
    
      { TCwKeyboardOut }
    
    procedure TCwKeyboardOut.DoCWProc(const H: WordBool);
    begin
      if Assigned(CWProcedure) then CWProcedure(H);
      if not OmniRigModule.OmniRigInst or not CATParams.bEnabled then Exit;
    
      with OmniRigModule.OmniRigX1, CATParams do
        case iRigNumber of
          1:  if iCWPin = 0 then Rig1.PortBits.Dtr := H else Rig1.PortBits.Rts := H;
          2:  if iCWPin = 0 then Rig2.PortBits.Dtr := H else Rig2.PortBits.Rts := H;
        end;
    end;
    
    procedure TCwKeyboardOut.Execute;
    var
      i, j   : SmallInt;
      iSample: Integer;
      pCurHdr: PWaveHdr;
      lCurHdr: Smallint;
      iSilens: Integer;
      sMid   : String;
    begin
      lCurHdr := 0;
      pCurHdr := nil;
      OmniRigModule := TOmniRigModule.Create(nil);
    
      { Бесконечный цикл до завершения потока. }
      while not CwKeyboardOut.Terminated do
        try
          { Цикл по массиву строкового буфера, где
           LEN_BUF - верхняя граница массива. }
          for i := 0 to MAXBYTE do
            begin
              if bStop then Break;
                
              { Если элемент массива не пуст. }
              if Length(sStrBuf[i]) <> 0 then
                begin
                  { Очередной передаваемый символ. }
                  case lLetter[i] of
                    36: { SK }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('S')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 75, 0);
                      end;
                    38: { BK }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('B')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 75, 0);
                      end;
                    42: { BT }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('B')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 84, 0);
                      end;
                    43: { AR }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('A')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 82, 0);
                      end;
                    60: { AS }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('A')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 83, 0);
                      end;
                    62: { KN }
                      begin
                        SendMessage(hSendHwnd, WM_CHAR, Integer(PChar('K')), 0);
                        SendMessage(hSendHwnd, WM_CHAR, 78, 0);
                      end
                    else
                      SendMessage(hSendHwnd, WM_CHAR, lLetter[i], 0);
                  end;
    
                  { Передача буфера. }
                  { Цикл по символам массива где получаем очередной
                    символ из телеграфного буфера ('1' '0' или '2'). }
                  for j := 1 to Length(sStrBuf[i]) do
                    begin
                      pCurHdr := pWavHdr[lCurHdr];
                      lCurHdr := lCurHdr and 1;
                      sMid := Copy(sStrBuf[i], j, 1);
                      iSample := 0;
    
                      { Точка. }
                      if sMid = '0' then
                        begin
                          Sinewave(PBuffer(pCurHdr.lpData), iSample, CwProp.iSpeed);
                          pCurHdr.dwBufferLength := iSample * 2;
                          DoCWProc(True);
                          waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                          waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                          WaitForSingleObject(hOutEvent, INFINITE);
                          waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                          DoCWProc(False);
                        end;
    
                      { Тире. }
                      if sMid = '1' then
                        begin
                          Sinewave(PBuffer(pCurHdr.lpData), iSample, iLenDash);
                          pCurHdr.dwBufferLength := iSample * 2;
                          DoCWProc(True);
                          waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                          waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                          WaitForSingleObject(hOutEvent, INFINITE);
                          waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                          DoCWProc(False);
                        end;
    
                      { Пауза между словами. }
                      if sMid = '2' then
                        begin
                          Silence(PBuffer(pCurHdr.lpData), iSample, CwProp.iSpace);
                          pCurHdr.dwBufferLength := iSample * 2;
                          waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                          waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                          WaitForSingleObject(hOutEvent, INFINITE);
                          waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                        end;
    
                      { Пауза между знаками. }
                      iSample := 0;
                      iSilens := 2;//CwProp.iSpeed;
                      Silence(PBuffer(pCurHdr.lpData), iSample, iSilens);
                      pCurHdr.dwBufferLength := iSample;
                      waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                      waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                      WaitForSingleObject(hOutEvent, INFINITE);
                      waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                    end;
    
                    { Пауза между буквами. }
                    iSample := 0;
                    Silence(PBuffer(pCurHdr.lpData), iSample, iLenDash);
                    pCurHdr.dwBufferLength := iSample * 2;
                    waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
                    waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                    WaitForSingleObject(hOutEvent, INFINITE);
                    waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
    
                    { Элемент массива уже проигран,
                      его можно освободить. }
                    sStrBuf[i] := EmptyStr;
                end;
            end;
          finally
            { Информируем приложение об окончании буфера. }
            SendMessage(hSendHwnd, WM_CHAR, VK_RETURN, 0);
    
            { Поток остановлен. }
            CwKeyboardOut.Suspend;
          end;
          
      OmniRigModule.Free;
    end;
    
    // =============================================================================
    
    { Остановка передачи текста из буфера буфера. }
    procedure cwStop; stdcall;
    begin
      if bCwOpen then
        begin
          { Останавливаем цикл. }
          bStop := True;
    
          { Обнуление телеграфного буфера. }
          FillChar(sStrBuf, SizeOf(sStrBuf), #0);
    
          { Обнуление индекса
            массива. }
          lInc := 0;
        end;
    end;
    
      { TCwKeyOut }
    
    procedure TCwKeyOut.DoCWProc(const H: WordBool);
    begin
      if Assigned(CWProcedure) then CWProcedure(H);
      if not OmniRigModule.OmniRigInst or not CATParams.bEnabled then Exit;
    
      with OmniRigModule.OmniRigX1, CATParams do
        case iRigNumber of
          1:  if iCWPin = 0 then Rig1.PortBits.Dtr := H else Rig1.PortBits.Rts := H;
          2:  if iCWPin = 0 then Rig2.PortBits.Dtr := H else Rig2.PortBits.Rts := H;
        end;
    end;
    
    procedure TCwKeyOut.Execute;
    var
      JyInfo : JOYINFO;
      pCurHdr: PWaveHdr;
      lCurHdr: Smallint;
      iSample: Integer;
    begin
      lCurHdr := 0;
    
      { Бесконечный цикл до завершения
        потока. }
      while not CwKeyOut.Terminated do
        begin
          try
            pCurHdr := pWavHdr[lCurHdr];
            lCurHdr := lCurHdr and 1;
            joyGetPos(JOYSTICKID1, @JyInfo);
            if (JyInfo.wButtons = JOY_BUTTON1) or
              (JyInfo.wButtons = JOY_BUTTON3) then
              begin
                { Нажата тире. }
                iSample := 0;
                Sinewave(PBuffer(pCurHdr.lpData), iSample, iLenDash);
                Silence(PBuffer(pCurHdr.lpData), iSample, CwProp.iSpeed);
                pCurHdr.dwBufferLength := iSample * 2;
    
                waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
    
                DoCWProc(True);
    
                waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                WaitForSingleObject(hOutEvent, INFINITE);
    
                DoCWProc(False);
    
                waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
              end
            else if (JyInfo.wButtons = JOY_BUTTON2) or
              (JyInfo.wButtons = JOY_BUTTON4) then
              begin
                { Нажата точка. }
                iSample := 0;
                Sinewave(PBuffer(pCurHdr.lpData), iSample, CwProp.iSpeed);
                Silence(PBuffer(pCurHdr.lpData), iSample, CwProp.iSpeed);
                pCurHdr.dwBufferLength := iSample * 2;
    
                waveOutPrepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
    
                DoCWProc(True);
    
                waveOutWrite(hOut, pCurHdr, SizeOf(TWaveHdr));
                WaitForSingleObject(hOutEvent, INFINITE);
    
                DoCWProc(False);
                  
                waveOutUnprepareHeader(hOut, pCurHdr, SizeOf(TWaveHdr));
              end;
          finally
            { Разгружаем процессор. }
            Sleep(5);
          end;
        end; 
    end;
    
    // =============================================================================
    
    { Завершение потоков и закрытие устройства аудиовывода. }
    procedure cwClose; stdcall;
    begin
      if bCwOpen then
        begin
          CWProcedure := nil;
          hSendHwnd := 0;
    
          with CwKeyboardOut do
            begin
              Terminate;
              while not Suspended do;
              Free;
            end;
    
          if Assigned(CwKeyOut) then
            with CwKeyOut do
              begin
                Terminate;
                while not Suspended do;
                Free;
              end;   
    
          CloseAudio;
        end;
    end;
    
    procedure cwSetOmniRig(const bEnabled: WordBool;
      const iCWPin, iRigNumber: Integer); stdcall;
    begin
      CATParams.iCWPin := iCWPin;
      CATParams.iRigNumber := iRigNumber;
      CATParams.bEnabled := bEnabled;
    end;
    
    end.

  3. #3
    Аватар для RX1AL
    Регистрация
    06.02.2009
    Адрес
    Санкт Петербург
    Сообщений
    3,812
    Ram:
    В принципе код на Дельфи понятный, когда-то сам писал. Но есть вопрос по реализации. В коде
    написано: iSpeed := ((SAMPLE_RATE * 12) div 10) div CwFormat.iSpeed;
    Вопрос в следующем, почему для определения скорости (переменная iSpeed) используется сэмплинг
    от звуковой карты, который равен константе SAMPLE_RATE = 44100. А если другой сэмплинг, то все
    получается не работает? И как сэмплинг связан со стандартом для WPM по PARIS?

    Геннадий мне прислал кусочек своего кода, пока не выкладываю здесь, так как в ЛС было послано, но
    у него расчет идет именно от PARIS. Вы можете прокомментировать свою идею?
    Михаил, ex UV1AL, RG1L, сейчас OE6MAF, HB9/OE6MAF, DL/OE6MAF
    DIG #5645, EPC #5908, #5909, KDR #21

  4. #4
    RX1AL
    iSpeed := ((SAMPLE_RATE * 12) div 10) div CwFormat.iSpeed - это размер таблицы синуса, т.е. время, в течении которого будет воспроизведён звук заданной частоты при частоте дискретизации SAMPLE_RATE.
    * 12 и div 10 подобраны мной эксперементально.
    При другом значении SAMPLE_RATE тоже всё будет работать, только в Sinewave 44 нужно поменять так же на другое значение.

  5. #5
    Все же стандарт - PARIS. Там ровно 50 точек. Так что длина точки DIT=1/(WPM*50) в минутах, или 60/(WPM*50) в секундах. Это при соотношение точка/пауза 1. Хорошо если веса можно задавать в программе. Например с 0.8 до 1.2.

  6. #6
    Аватар для RX1AL
    Регистрация
    06.02.2009
    Адрес
    Санкт Петербург
    Сообщений
    3,812
    Цитата Сообщение от LZ1VB
    Все же стандарт - PARIS. Там ровно 50 точек. ..... Хорошо если веса можно задавать в программе. Например с 0.8 до 1.2.
    Спасибо... Я именно тоже так и думал, что все же надо делать по стандарту PARIS.
    Веса в программе можно будет менять. Именно для этого и искал формулу, чтобы
    сделать универсальный программный манипулятор.

    2 Ram:
    Также спасибо за пояснения - теперь понял логику этого момента.[/b]
    Михаил, ex UV1AL, RG1L, сейчас OE6MAF, HB9/OE6MAF, DL/OE6MAF
    DIG #5645, EPC #5908, #5909, KDR #21

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Похожие темы

  1. ICOM F110 функция сканирование каналов
    от Белый С.Н. в разделе Модификация радиостанций
    Ответов: 5
    Последнее сообщение: 28.10.2009, 19:11
  2. Функция монитор в Motorola GM-300
    от Intal в разделе Модификация радиостанций
    Ответов: 4
    Последнее сообщение: 05.05.2006, 18:48

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •