Иммитатор телефонного гудка

Общие вопросы, не зависящие от языка реализации.

Модераторы: Duncon, Hawk, Romeo, Eugie

Ответить
Аватара пользователя
Decoder
Сообщения: 308
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

Привет всем! Подскажите, пожалуйста, каким образом можно программно синтезировать звук, похожий на гудок в телефонной трубке? Я конечно могу тупо найти некий WAV-файл с таким звуком и проигрывать его из программы.
Но мне хотелось бы иметь возможность самому устанавливать частоту сигнала, его уровень, длительность звучания, а так же режим воспроизведения (моно/стерео).
Поумнеть несложно, куда труднее от дури избавиться.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Могу предложить свой вариант:

Код: Выделить всё

#include "stdafx.h"
#include <Mmsystem.h>
#include <math.h>
#include <atlsync.h>
 
#pragma comment (lib, "Winmm.lib")
 
ATL::CEvent _Event(TRUE, FALSE);
 
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg,         
             DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
  switch (uMsg)
  {
    case MM_WOM_OPEN:
      _Event.Reset();
      break;
    case MM_WOM_DONE:
      _Event.Set();
      break;
    default:
      break;
  }
}
 
int _tmain(int argc, _TCHAR* argv[])
{
  WAVEFORMATEX waveForm = {0};
  waveForm.nChannels = 2; // Режим стерео
  waveForm.nSamplesPerSec = 45000;
  waveForm.wBitsPerSample = 16;
  waveForm.wFormatTag = WAVE_FORMAT_PCM;
  waveForm.nBlockAlign = waveForm.nChannels * waveForm.wBitsPerSample/8;
  waveForm.nAvgBytesPerSec = waveForm.nSamplesPerSec * waveForm.nBlockAlign;
  //
  HWAVEOUT hWaveOut = NULL;
  MMRESULT mmr = waveOutOpen( &hWaveOut, WAVE_MAPPER, 
                &waveForm, (DWORD_PTR)waveOutProc, 
                0, CALLBACK_FUNCTION);
  if (mmr == MMSYSERR_NOERROR)
  {
    DWORD* pBuff = new DWORD[waveForm.nSamplesPerSec];
    WAVEHDR waveHdr = {0};
    waveHdr.lpData = reinterpret_cast<LPSTR>(pBuff);
    waveHdr.dwBufferLength = waveForm.nAvgBytesPerSec;
    //
    const int Frequency = 750; // Частота в герцах
    const int ts = waveForm.nSamplesPerSec/Frequency;
    const double Pi = acos(-1.0); // Число ПИ
    for (DWORD n = 0; n < waveForm.nSamplesPerSec; n++)
    {
      int vol = 20000; // Уровень сигнала
      double dSample1 =  // Левый канал
                 sin(Pi*n/ts)*vol + 
                 sin(Pi*n*3/ts)*vol/2 + 
                 sin(Pi*n*5/ts)*vol/4 + 
                 sin(Pi*n*9/ts)*vol/8;
      double dSample2 =  // Правый канал
                 cos(Pi*n/ts)*vol - 
                 cos(Pi*n*3/ts)*vol/2 - 
                 cos(Pi*n*5/ts)*vol/4 - 
                 cos(Pi*n*9/ts)*vol/8;
      pBuff[n] = MAKELONG((WORD)dSample1, (WORD)dSample2);
    }
    //
    mmr = waveOutPrepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
    if (mmr == MMSYSERR_NOERROR)
    {
      mmr = waveOutWrite(hWaveOut, &waveHdr, sizeof(WAVEHDR));
      ::WaitForSingleObject(_Event, INFINITE);
    }
    waveOutUnprepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
    waveOutClose(hWaveOut);
    delete[] pBuff;
  }
  return 0;
}

Однако на сегодняшний день использование функций waveIn/waveOut считается как бы уже устаревшей технологией. Вместо них Microsoft предлагает более современные библиотеки, типа DirectSound или Windows Audio Session (WAS).
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Виталик, я в очередной раз поражаюсь количеством самплов в твоей бездонной библиотеке готовых решений :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Decoder
Сообщения: 308
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

Обалдеть! Прямо как настоящий телефонный зуммер. Просто потрясающе.
WinMain, огромное тебе спасибо.
А не подскажешь, что нужно поменять для переключения режима звучания в моно и для изменения времени звучания?
Поумнеть несложно, куда труднее от дури избавиться.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Вот так будет выглядеть этот код при воспроизведении в режиме моно и с временем звучания 2.5 сек:

Код: Выделить всё

#include "stdafx.h"
#include <math.h>
#include <atlsync.h>
#include <Mmsystem.h>
 
#pragma comment (lib, "Winmm.lib")
 
ATL::CEvent _Event(TRUE, FALSE);
 
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg,         
             DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
  switch (uMsg)
  {
    case MM_WOM_OPEN:
      _Event.Reset();
      break;
    case MM_WOM_DONE:
      _Event.Set();
      break;
    default:
      break;
  }
}
 
int _tmain(int argc, _TCHAR* argv[])
{
  WAVEFORMATEX waveForm = {0};
  waveForm.nChannels = 1; // Режим моно
  waveForm.nSamplesPerSec = 45000;
  waveForm.wBitsPerSample = 16;
  waveForm.wFormatTag = WAVE_FORMAT_PCM;
  waveForm.nBlockAlign = waveForm.nChannels * waveForm.wBitsPerSample/8;
  waveForm.nAvgBytesPerSec = waveForm.nSamplesPerSec * waveForm.nBlockAlign;
  //
  HWAVEOUT hWaveOut = NULL;
  MMRESULT mmr = waveOutOpen( &hWaveOut, WAVE_MAPPER, 
                &waveForm, (DWORD_PTR)waveOutProc, 
                0, CALLBACK_FUNCTION);
  if (mmr == MMSYSERR_NOERROR)
  {
    const DWORD dwDuration = 2500; // Время в миллисекундах
    const DWORD dwTotalSamples = 
        ::MulDiv(waveForm.nSamplesPerSec, dwDuration, 1000);
    WORD* pBuff = new WORD[dwTotalSamples];
    WAVEHDR waveHdr = {0};
    waveHdr.lpData = reinterpret_cast<LPSTR>(pBuff);
    waveHdr.dwBufferLength = dwTotalSamples * waveForm.nBlockAlign;
    //
    const int Frequency = 750; // Частота в герцах
    const int ts = waveForm.nSamplesPerSec/Frequency;
    const double Pi = acos(-1.0); // Число ПИ
    for (DWORD n = 0; n < dwTotalSamples; n++)
    {
      int vol = 20000; // Уровень сигнала
      double dSample = 
      sin(Pi*n/ts)*vol + 
      sin(Pi*n*3/ts)*vol/2 + 
      sin(Pi*n*5/ts)*vol/4 + 
      sin(Pi*n*9/ts)*vol/8;
      pBuff[n] = (WORD)dSample;
    }
    //
    mmr = waveOutPrepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
    if (mmr == MMSYSERR_NOERROR)
    {
      waveOutWrite(hWaveOut, &waveHdr, sizeof(WAVEHDR));
      ::WaitForSingleObject(_Event, INFINITE);
    }
    waveOutUnprepareHeader(hWaveOut, &waveHdr, sizeof(WAVEHDR));
    waveOutClose(hWaveOut);
    delete[] pBuff;
  }
  return 0;
}
Аватара пользователя
Decoder
Сообщения: 308
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

Какая прелесть. :-)
А как бы мне ещё научиться сохранять синтезируемые звуки в WAV-файлы?
Поумнеть несложно, куда труднее от дури избавиться.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Decoder писал(а):Какая прелесть. :-)
А как бы мне ещё научиться сохранять синтезируемые звуки в WAV-файлы?
В принципе, формат WAV-файла (не сжатого) довольно прост.
Вот, например, описание.
Т.е. сначала записывается (в файл) загоровок, затем данные.
Про заголовок что-то там явно перечислено, что-то надо заполнить в зависимости от твоих данных и содержимого записи WAVEFORMATEX waveForm (которая заполняется в прведенных WinMain-ом примерах).
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Могу предложить такой вариант функции записи звука в WAV-файл...

Код: Выделить всё

BOOL waveSaveToFile(LPCTSTR szFileName, LPWAVEFORMATEX lpWaveFormat, LPWAVEHDR lpWaveHdr)
{
  if (szFileName == NULL || lpWaveFormat == NULL || lpWaveHdr == NULL)
  {
    return FALSE;
  }
  //
  struct _WAVEFILEHEADER {
    FOURCC fccSignRIFF;
    DWORD dwChunkSize;
    FOURCC fccSignWAVE;
    FOURCC fccSignFmt;
    DWORD dwDataOffset;
    WAVEFORMAT waveFormat;
    WORD wBitsPerSample; 
    FOURCC fccDataSign; 
    DWORD dwDataSize; 
  } fileHdr;
  //
  fileHdr.fccSignRIFF = FOURCC_RIFF;
  fileHdr.fccSignWAVE = mmioFOURCC('W','A','V','E');
  fileHdr.fccSignFmt  = mmioFOURCC('f','m','t',' ');
  fileHdr.fccDataSign = mmioFOURCC('d','a','t','a');
  fileHdr.dwDataSize  = lpWaveHdr->dwBufferLength;
  fileHdr.dwDataOffset = sizeof(WAVEFORMAT) + sizeof(WORD);
  fileHdr.dwChunkSize = fileHdr.dwDataSize + sizeof(fileHdr) - sizeof(FOURCC);
  fileHdr.wBitsPerSample = lpWaveFormat->wBitsPerSample;
  fileHdr.waveFormat.wFormatTag = lpWaveFormat->wFormatTag;
  fileHdr.waveFormat.nChannels = lpWaveFormat->nChannels;
  fileHdr.waveFormat.nSamplesPerSec = lpWaveFormat->nSamplesPerSec;
  fileHdr.waveFormat.nBlockAlign = lpWaveFormat->nBlockAlign;
  fileHdr.waveFormat.nAvgBytesPerSec = lpWaveFormat->nAvgBytesPerSec;
  //
  FILE* f = NULL;
  _tfopen_s(&f, szFileName, _T("wb"));
  if (f == NULL)
  {
    return FALSE;
  }
  fwrite(&fileHdr, 1, sizeof(fileHdr), f);
  fwrite(lpWaveHdr->lpData, 1, fileHdr.dwDataSize, f);
  fclose(f);
  //
  return TRUE;
}

Применительно к предыдущим примерам, эту функцию можно вызывать перед удалением динамического массива данных. Это будет выглядеть примерно так...

Код: Выделить всё

    .  .  .  .  .
    waveSaveToFile(_T("Phone.wav"), &waveForm, &waveHdr);
    delete[] pBuff;
Ответить