waveInOpen 으로 마이크입력을 바로 스피커출력하는 방법 조언 부탁드립니다.(qt)

pogusm의 이미지

# waveinout.pro
 
SOURCES       = main.cpp
LIBS += -lWinmm

// main.cpp
 
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
 
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	//printf("(waveInProc) hWaveIn 0x%p, nMessage 0x%04X, nInstance %d, nParameter1 %d, nParameter2 %d\n", hwi, uMsg, dwInstance, dwParam1, dwParam2);
 
	WAVEHDR *pHdr=NULL;
	switch(uMsg)
	{
	case WIM_CLOSE:
		cout << "waveInProc()... WIM_CLOSE" << endl;
		break;
 
	case WIM_DATA:
	{
		pHdr = (WAVEHDR *)dwParam1;
		cout << "waveInProc()... WIM_DATA : " << "pHdr->dwBufferLength:" << pHdr->dwBufferLength << "  pHdr->dwBytesRecorded:" << pHdr->dwBytesRecorded << endl;
	}
		break;
 
	case WIM_OPEN:
		cout << "waveInProc()... WIM_OPEN" << endl;
		break;
 
	default:
		break;
	}
}
 
int main(int argv, char **args)
{
	const int NUMPTS = 44100 * 10;
	int sampleRate = 44100;
	short int waveIn[NUMPTS];
 
	HWAVEIN hWaveIn;
	WAVEHDR WaveInHdr;
	MMRESULT result;
	HWAVEOUT hWaveOut;
 
	WAVEFORMATEX pFormat;
	pFormat.wFormatTag = WAVE_FORMAT_PCM;
	pFormat.nChannels = 1;
	pFormat.nSamplesPerSec = sampleRate;
	pFormat.nAvgBytesPerSec = 2 * sampleRate;
	pFormat.nBlockAlign = 2;
	pFormat.wBitsPerSample = 16;
	pFormat.cbSize = 0;
 
 
	result = waveInOpen(&hWaveIn, WAVE_MAPPER, &pFormat, (DWORD_PTR)waveInProc, NULL, CALLBACK_FUNCTION);
 
	if(result)
	{
		char fault[256];
		waveInGetErrorTextA(result, fault, 256);
		MessageBoxA(NULL, fault, "Failed to open waveform input device.", MB_OK | MB_ICONEXCLAMATION);
		return 1;
	}
 
	WaveInHdr.lpData = (LPSTR)waveIn;
	WaveInHdr.dwBufferLength = 2 * NUMPTS;
	WaveInHdr.dwBytesRecorded = 0;
	WaveInHdr.dwUser = 0;
	WaveInHdr.dwFlags = 0;
	WaveInHdr.dwLoops = 0;
	waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
 
	result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
	if(result)
	{
		MessageBoxA(NULL, "Failed to read block from device", NULL, MB_OK | MB_ICONEXCLAMATION);
		return 1;
	}
 
	result = waveInStart(hWaveIn);
	if(result)
	{
		MessageBoxA(NULL, "Failed to start recording", NULL, MB_OK | MB_ICONEXCLAMATION);
		return 1;
	}
 
 
	cout << "Recording..." << endl;
	Sleep((NUMPTS/sampleRate) * 1000); //Sleep while recording
 
	cout << "Playing..." << endl;
 
	if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &pFormat, 0, 0, WAVE_FORMAT_DIRECT))
	{
		MessageBoxA(NULL, "Failed to replay", NULL, MB_OK | MB_ICONEXCLAMATION );
	}
	waveOutWrite(hWaveOut, &WaveInHdr, sizeof(WaveInHdr));
	Sleep((NUMPTS/sampleRate) * 1000);
	waveOutUnprepareHeader(hWaveOut, &WaveInHdr, sizeof(WAVEHDR));
	waveOutClose(hWaveOut);
 
	waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
	waveInClose(hWaveIn);
 
	return 0;
}

위 예제는 10초간 마이크입력을 받아, 10초간 스피커로 출력을 하는 내용입니다.
그런데 마이크입력을 바로 스피커로 출력하도록 하고 싶은데,
waveInProc 콜백함수에서 처리를 하는거 같은데.. 잘 모르겠습니다.

조언 부탁드립니다.
(windows7, QT(c++))

File attachments: 
첨부파일 크기
Package icon waveinout.zip1.52 KB
qiiiiiiiip의 이미지

output을 input에 redirection하는것이 아니라면,
입력과 "동시"에 출력을 내는 것은 불가능하겠습니다..

대신 사람이 느끼지 못할 정도의 delay를 두고 출력하는 방법으로 하셔야겠지요.

WaveInHdr.dwBufferLength 를 짧은 길이 ( 예를 들어 10msec = 44100*0.01 ) 정도로 두고,
매 10msec마다 WIN_DATA 부분이 호출될테니,
그 부분에서 들어온 길이(pHdr->dwBytesRecorded)만큼 출력하면 되지 않을까 싶네요..

10초제한은 마지막 부분에 Sleep( 10초 )를 넣어도되고,
보다 정확히 제한하려면,
while(flag) 를 넣고, pHdr->dwBytesRecorded 를 누적하여 누적된 길이가 10초(=44100*10)
를 초과하면 flag를 reset 하도록 하면 되겠네요..

--

그런데 이게 qt 코드인가요?

pogusm의 이미지

"사람이 느끼지 못할 정도의 delay를 두고 출력하는 방법"을 구현하기 위해,
어느정도 해결방법에 다다랐는데.. 실제 audio output(출력)은 아직 답을 못찾고 있습니다 ㅠㅠ

어떻게 해야 할지 막막합니다 ㅠㅠ

그리고 혹시 input을 output으로 redirection 하는 방법은 어떻게 하는건지 설명 해주실수 있나요?

감사합니다.

ps. 제가 visual studio 를 잘 못다뤄서, qt-creator 에서 작성했는데...
QT라이브러리가 사용되지 않고 windows.h 를 사용해서, QT코드처럼 보이지 않는겁니다.

pogusm의 이미지

#include <QDebug>
#include <QByteArray>
 
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
 
const int NUMPTS = 8000 * 10;
int sampleRate = 8000;
short int waveIn[2*80];
HWAVEIN hWaveIn;
WAVEHDR WaveInHdr;
MMRESULT result;
HWAVEOUT hWaveOut;
WAVEFORMATEX pFormat;
 
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
 
	printf("(waveInProc) hWaveIn 0x%p, nMessage 0x%04X, nInstance %d, nParameter1 %d, nParameter2 %d\n", hwi, uMsg, dwInstance, dwParam1, dwParam2);
 
	//cout << "waveInProc()..." << endl;
	WAVEHDR *pHdr=NULL;
	WAVEHDR pHdr2;
	switch(uMsg)
	{
		case WIM_CLOSE:
			cout << "waveInProc()... WIM_CLOSE" << endl;
			break;
 
		case WIM_DATA:
			{
				pHdr = (WAVEHDR *)dwParam1;
				pHdr2.lpData = (LPSTR)pHdr->lpData;
				pHdr2.dwBufferLength = pHdr->dwBufferLength;
				pHdr2.dwBytesRecorded = pHdr->dwBytesRecorded;
				pHdr2.dwUser = 0;
				pHdr2.dwFlags = 0;
				pHdr2.dwLoops = 0;
 
				cout << "waveInProc()... WIM_DATA : " << "pHdr->dwBufferLength:" << pHdr->dwBufferLength << "  pHdr->dwBytesRecorded:" << pHdr->dwBytesRecorded << endl;
				cout << QByteArray(pHdr->lpData, pHdr->dwBytesRecorded).toHex().constData() << " : " << QByteArray(pHdr->lpData, pHdr->dwBytesRecorded).length() << endl;
 
				waveOutPrepareHeader(hWaveOut, &pHdr2, sizeof(WAVEHDR));
				waveOutWrite(hWaveOut, &pHdr2, sizeof(WAVEHDR));
 
				waveInPrepareHeader(hwi, pHdr, sizeof(WAVEHDR));
				waveInAddBuffer(hwi, pHdr, sizeof(WAVEHDR));
			}
			break;
 
		case WIM_OPEN:
			cout << "waveInProc()... WIM_OPEN" << endl;
			break;
 
		default:
			break;
	}
}
 
int main(int argv, char **args)
{
 
	pFormat.wFormatTag = WAVE_FORMAT_PCM;
	pFormat.nChannels = 1;
	pFormat.nSamplesPerSec = sampleRate;
	pFormat.nAvgBytesPerSec = 2 * sampleRate;
	pFormat.nBlockAlign = 2;
	pFormat.wBitsPerSample = 16;
	pFormat.cbSize = 0;
 
		result = waveInOpen(&hWaveIn, WAVE_MAPPER, &pFormat, (DWORD_PTR)waveInProc, NULL, CALLBACK_FUNCTION);
 
		if(result)
		{
			char fault[256];
			waveInGetErrorTextA(result, fault, 256);
			MessageBoxA(NULL, fault, "Failed to open waveform input device.", MB_OK | MB_ICONEXCLAMATION);
			return 1;
		}
 
		WaveInHdr.lpData = (LPSTR)waveIn;
		WaveInHdr.dwBufferLength = 2 * 80;
		WaveInHdr.dwBytesRecorded = 0;
		WaveInHdr.dwUser = 0;
		WaveInHdr.dwFlags = 0;
		WaveInHdr.dwLoops = 0;
		waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
 
		result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
		if(result)
		{
			MessageBoxA(NULL, "Failed to read block from device", NULL, MB_OK | MB_ICONEXCLAMATION);
			return 1;
		}
 
		result = waveInStart(hWaveIn);
		if(result)
		{
			MessageBoxA(NULL, "Failed to start recording", NULL, MB_OK | MB_ICONEXCLAMATION);
			return 1;
		}
 
		if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &pFormat, 0, 0, WAVE_FORMAT_DIRECT))
		{
			MessageBoxA(NULL, "Failed to replay", NULL, MB_OK | MB_ICONEXCLAMATION );
		}
 
		Sleep((NUMPTS/sampleRate) * 1000);
 
		waveOutUnprepareHeader(hWaveOut, &WaveInHdr, sizeof(WAVEHDR));
		//waveOutReset(hWaveOut);
		waveOutClose(hWaveOut);
 
		waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
		//waveInStop(hWaveIn);
		//waveInReset(hWaveIn);
		waveInClose(hWaveIn);
 
	return 0;
}

1. sampleRate 를 44100 으로 하면 음질이 심하게 왜곡되어 8000 으로 맞춤
2. 2*80=160byte 씩 끊은 이유는 rtp 전송용으로 사용하기 위함. (낮추거나 높여도 음질 개선 효과는 별로 없음)

의문
1. audio input -> audio output (실시간출력)에서 음질을 높일수 있는 방법은? (또는 input을 output으로 redirection 하는 방법은?)
2. 위의 패킷을 rtp로 전송하여 audio output으로 출력하면 좀더 나은 음질을 기대할 수 있을까?

qiiiiiiiip의 이미지

잘 하셨네요..

왜곡이 있다면, 일단 call back함수에서 입력과 출력을 구분하고,
입력 루틴에서는 입력을 ring buffer에 buffering만 하고,
출력쪽에서는 ring buffer에서 가져오기만 해서 play만 하도록 해야하고요,
여러개의 WaveHdr사용(input/output), critical section 처리 등을 하면 끊김이 줄어들겁니다..

아래 링크 참고하세요
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3
입력도 비슷하게 처리하면 될 듯이요..
(링크가 깨지면 Playing Audio in Windows using waveOut Interface 를 구글링해보세요)

--

redirection은 저는 해보지않아서 가능한지도 잘 모르겠습니다.
그냥 이론적으로 그렇다는 뜻이었습니다..

windows의 경우 제어판->오디오(믹서?) 에 보면 마이크 입력을 출력으로 나오게 하는 옵션이 있습니다.
그걸 이용하는 것과 비슷하지 않을까요?

pogusm의 이미지

sampleRate 8000 을 160byte씩 끊어서 1/50초마다 callback 함수를 호출하는것보다
그냥 1초 지연된채로 8000byte씩 끊어서 callback 함수를 호출하면
음질 왜곡이 거의 없더라구요..

1초마다 callback 함수가 호출될때
8000byte의 데이터를 50개로 나누어서 rtp로 전송하면 될거같은데..
초당 50회의 RTP(UDP) 패킷 전송(214byte)을 해도 무리가 없을지는 아직 해보지 않아서 모르겠습니다.

qiiiiiiiip 님의 도움이 컸습니다~
감사합니다.

ghrhrsla의 이미지

완성된 소스좀 보여주실수있나요?ㅠㅠㅠ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.