win32 soundcard input?

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Also input should be input[j]
while "i" changes like this "i += 2"
"j" changes like this "j++"

Post

:) must have d/led it a while ago, i swapped the buffer for short instead of bytes a while back..

case MM_WIM_DATA:
waveInAddBuffer(hWaveIn, pWaveHdr3, sizeof (WAVEHDR));
waveInAddBuffer(hWaveIn, pWaveHdr4, sizeof (WAVEHDR));
for (i = 0; i < IN_BUFFER_SIZE; i++) {
input0 = input;
}
return TRUE;
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

..instead of lamely offering source for can haz fix (http://xoxos.net/temp/soundin.zip) let's see if my current understanding is correct, as it has recently had to adapt ;) -


my win32 'out' buffer routine is solid, having been used a few times. i now realise that if performance is compromised (cpu load, bumping hard drive) and breakups occur, these may not be "locked into absolute perfect relationship between in and out array positions" and, since the in and out buffer processes are separate, could conceivably change the "phase" between using them..

presently i have two input buffers for one channel - the first is populated by being associated with the wavein header, the second is a duplicate of the first created in MM_WIM_DATA (and visible in the post above this).

since this app is for "known application" birthday party it does not have to be robust, i figured two buffers would be enough..


i am still hearing the "disjunct buffer periodicity" as the input is used in the output, but i am uninformed about why this is - the present state has evolved from being unintelligible as voice only by changing the ratios of the in and out buffer lengths - and every variable i read that has to do with the current sample of each buffer always *seems* to be unwaveringly locked into rigid ^2 performance (i've tried jogging hard drive and things like "printing the current pointer position when drawing the screen" always returns the same values).

so - it occurs to me that as i am not able to effectively gauge the performance deficit with my ears (often it sounds in phase at startup and drifts within a second or two) perhaps the problem is indeed that i should be using several buffers for the input instead of just two. (this would be lengthy to implement for trial because my dsp-not-sdk experience makes me shy of using handles for arrays ((yawn because my compiler fclt does not accept ~array[] and apparently deletes stuff automatically and the protocol for deleting handles or getting a memory leak mysterious because i normally don't get bugs for not deleting stuff)).

do you think that, if i am using two input buffers in this manner and "just using the out buffer pointer modulused by the in buffer length" things will stay in alignment in a system without performance challenges, or do you think the obvious problem is that i am not handling "read position relationship changes" from drop outs..... ?


in jeff's sdk for synthedit i have seen code for a function that stored a variable representing the difference between the in and out positions, but, as said, all my tests "seem to indicate" that the relationship on my system *never* drops out.. the dwBytesRecorded stays fixed et c..

do i sort of know what i'm talking about?

oh - and thank you, it's embarassing to ask accomplished people for their time..
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

i've used winMM / WaveInput in the first version of my sonogram
the app sometimes (too often) hangs on exit.. but it hangs in such a brutal way, that any other audio-related app hangs the same way, and they then become unkillable, windows can't be restarted the normal way, it's aweful

so, i am the least guy to teach anyone how to write winMM code, but i do remember that you need to have more buffers
you pass one, the audio card starts filling it, and when it fills it - you gotta have a ready "spare" buffer .. you better have
then it picks a spare buffer and throws the filled buffer at you, so you have to process it and put it back, flagged as "spare"

2 buffers not much i'd guess
in any case, if you don't manage to process the buffer fast enough - you get problems because there are no spare buffers in that case
so at least use 3 buffers, one will always be "used" by the soundcard/input, one you'll be processing, and the last one will be always available as spare, in case you're still not finished processing the current one while the soundcard has filled its buffer

i hope this makes sense
It doesn't matter how it sounds..
..as long as it has BASS and it's LOUD!

irc.libera.chat >>> #kvr

Post

I developed a quick and "not so" dirty example that plays back the audio input.
You might want to replace the "invisible" queue with a fully fledged one that is also lock-free.

Though it's not thoroughly tested, it seems to work fine in a console application:

Code: Select all


#include <windows.h>
#include <mmsystem.h>
#include <process.h>

#pragma comment (lib, "winmm.lib")

#include <iostream>
using namespace std;

#define BUFFERS_COUNT	8
#define BUFFER_SIZE		1024

WAVEHDR iHdr[BUFFERS_COUNT];
WAVEHDR oHdr[BUFFERS_COUNT];

HWAVEIN hWIn;
HWAVEOUT hWOut;

CRITICAL_SECTION lock; 
HANDLE hThread;
HANDLE hEvent;
bool quit = false;

int head;
int tail;

WAVEHDR *GetFreeBuffer()
{
	WAVEHDR *pWH = NULL;

	EnterCriticalSection(&lock);

	// todo: check "full"
	{
		pWH = &oHdr[tail];
	}

	LeaveCriticalSection(&lock); 

	return pWH;
}

bool Enqueue()
{
	bool ret = false;

	EnterCriticalSection(&lock);
	
	int next = (tail + 1) % BUFFERS_COUNT;
	if (next != head)
	{
		tail = next;
		ret = true;
	}

	LeaveCriticalSection(&lock); 

	return ret;
}

int Dequeue()
{
	int index = -1;

	EnterCriticalSection(&lock);

	if (tail != head)
	{
		index = head;
		head = (head + 1) % BUFFERS_COUNT;	
	}

	LeaveCriticalSection(&lock); 

	return index;
}

void CALLBACK waveInProc
(
  HWAVEIN hwi,
  UINT uMsg,
  DWORD_PTR dwInstance,
  DWORD_PTR dwParam1,
  DWORD_PTR dwParam2
)
{
	switch(uMsg)
	{
		case WIM_DATA:
		{
			WAVEHDR *pWaveHdr = ((WAVEHDR*)dwParam1);
			if ((pWaveHdr->dwFlags & WHDR_DONE) == WHDR_DONE)
            {
				WAVEHDR *pOutHdr = GetFreeBuffer(); 

				// todo: skip if the buffer is still in
				//       use by WinMM
				if (pOutHdr != NULL)
				{
					memcpy(pOutHdr->lpData, pWaveHdr->lpData,   
						pWaveHdr->dwBytesRecorded);
					pOutHdr->dwBytesRecorded  = pWaveHdr->dwBytesRecorded;

					Enqueue();
					SetEvent(hEvent); 
				}

				if (!quit)
				{
					// todo: clear flags?
					waveInAddBuffer(hwi, pWaveHdr, sizeof(WAVEHDR));
				}
            }
		}
		break;
	}

}

unsigned __stdcall Thread(void *arg) 
{
	for (;;)	
	{
		DWORD wres = WaitForSingleObject(hEvent, 400);

		if (wres == WAIT_OBJECT_0)
		{
			int index = -1;
			do
			{
				index = Dequeue();
				if (index >= 0)
				{
					WAVEHDR *pWH = &oHdr[index];
					waveOutWrite(hWOut, pWH, sizeof (WAVEHDR)); 
				}

			} while (index >= 0);
		}

		if (quit)
			break;
	}

	cout << "thread done\n";
	return 0;
}

bool Start(int idevice, int odevice)
{
	hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
	InitializeCriticalSection(&lock); 

	quit = false;

	WAVEFORMATEX wf;
	wf.wFormatTag		= WAVE_FORMAT_PCM;
	wf.wBitsPerSample	= 16;
	wf.nChannels		= 2;
	wf.nSamplesPerSec	= 44100;
	wf.nBlockAlign		= wf.nChannels * wf.wBitsPerSample / 8;
	wf.nAvgBytesPerSec	= wf.nSamplesPerSec * wf.nBlockAlign;
	wf.cbSize			= 0;

	MMRESULT mmres;

	head = tail = 0;

	mmres = waveInOpen(&hWIn,
		idevice,
		(LPWAVEFORMATEX)&wf, 
		(DWORD_PTR)waveInProc, 
		0, CALLBACK_FUNCTION);

	for (int i = 0; i < BUFFERS_COUNT; i++)
	{
		memset(&iHdr[i], 0, sizeof(iHdr[i]));
		int size = BUFFER_SIZE * sizeof(USHORT);
		iHdr[i].dwBufferLength  = size;
		iHdr[i].lpData          = (LPSTR)new BYTE[size];

		waveInPrepareHeader(hWIn, &iHdr[i], sizeof(WAVEHDR));
		waveInAddBuffer(hWIn, &iHdr[i], sizeof(WAVEHDR));
	}

	mmres = waveOutOpen(&hWOut, 
		odevice, &wf, 0, 0, CALLBACK_NULL);


	for (int i = 0; i < BUFFERS_COUNT; i++)
	{
		memset(&oHdr[i], 0, sizeof(oHdr[i]));
		int size = BUFFER_SIZE * sizeof(USHORT);
		oHdr[i].dwBufferLength  = size;
		oHdr[i].lpData          = (LPSTR)new BYTE[size];

		waveOutPrepareHeader(hWOut, &oHdr[i], sizeof(WAVEHDR));
	}


	mmres = waveInStart(hWIn);

	hThread = (HANDLE)_beginthreadex(NULL, 0, Thread, NULL, 0, NULL);


	return true;
}

void Stop()
{
	MMRESULT mmres;
	quit = true;

	WaitForSingleObject(hThread, 2000); 
	CloseHandle(hThread);
	CloseHandle(hEvent);

	waveInReset(hWIn);
	int count = 0;
	do
	{
		Sleep(10);
		count = 0;
		for (int i = 0; i < BUFFERS_COUNT; i++)
		{
			if ( (iHdr[i].dwFlags & WHDR_DONE) == WHDR_DONE)
				count++;
		}
	}
	while (count != BUFFERS_COUNT);

	for (int i = 0; i < BUFFERS_COUNT; i++)
	{
		waveInUnprepareHeader(hWIn, &iHdr[i], sizeof(WAVEHDR));
		delete[] iHdr[i].lpData; 
	}

	mmres = waveInClose(hWIn);


	waveOutReset(hWOut);

	do
	{
		Sleep(10);
		count = 0;
		for (int i = 0; i < BUFFERS_COUNT; i++)
		{
			if ( (oHdr[i].dwFlags & WHDR_DONE) == WHDR_DONE)
				count++;
		}
	}
	while (count != BUFFERS_COUNT);

	for (int i = 0; i < BUFFERS_COUNT; i++)
	{
		waveOutUnprepareHeader(hWOut, &oHdr[i], sizeof(WAVEHDR));
		delete[] oHdr[i].lpData; 
	}

	mmres = waveOutClose(hWOut);
	

	DeleteCriticalSection(&lock); 
}

Usage:

Code: Select all


	int input_device_id = 0;
	int output_device_id = 0;

	Start(input_device_id, output_device_id);
	
	printf("press enter to stop...\n");
	getchar();
	
	Stop();


Post

thank you mr. wise.. will take a break and work with that.

i reexamined what i had last night and noticed an extra pair of WaveInAddBuffers in a WOM statement.. the cause of this was that, at an early point where i probably still beleived in and out would use the same buffer length et c. i wan't finding what i thought i should and made 'wavein' functions by copying all the 'waveout' and changing 'out' to 'in..' ...at some point in that i found that WaveInAddBuffer was the wavein version of my user defined FillAudioBuffer, so that's how that extra pair of lines got there..

cleaned out the mess and thought very slowly about the buffer lengths.. i know they were "all crazy" (one reading 2x too fast, one writing half rate stuff..) and the cause of this is because once an array is associated with a wave header you don't get errors overrunning the array it loops.. those abrupt stops and crashes are informative to someone who is in the dark about how this process is supposed to look..

so that's part of why it is such a mess...

otherwise finished project (may do more to illustrate the loop path for open source..)
http://xoxos.net/temp/delay.zip

break, stretch..
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

still ITD, _createthreadex requires a library to be linked which other borland fclt users appear to have difficulty achieving as well*.

how unpleasingly complicated.



(in case anyone is driven to respond to that,)

*(i know how that sounds, but compared to 5 megs, i find VCe monolithically hostile as it is equally as rare to find meaningful documentation and many times more over more likely to find abusively meaningless documentation**, fclt feels benevolent and unimposing in it's functional elegance. and if i do find something about it, it's likely to be terse and explicit and at worst wrong instead of alluring and exploitive.)

**in case anyone missed their 45 minute tutorial videos for 2008 iirc from which i was able to glean that MS wants me to buy their things.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

this thread (as before) has been a great experience for me :lol: i still don't know how to do it, but now i'm embarassed :)

(bitwise i expect your doc will continue to be informative to me for a while, tym :)
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

again i'm sorry to ask, but i'm not able to discern otherwise -



in MM_WIM_DATA, lParam is the address of the buffer. i can print lParam on the screen and observe eight adresses if i am using eight buffers.

how can i check this against the buffers to indicate which one is being returned?

at first i thought, unsigned long addressvalue[BUFFERS] = &MyWavHeader[BUFFERS]

but that tells me a long int is not the same as a HDR thingy. so, i've tried casting them, doing it directly -
for (i = 0; i < BUFFERS; i++) if (&MyFunkyWavInHdr == (LPWAVEHDR) lParam) whatiwanttoknow = i;

and this never matches either. that (LPWAVEHDR) has been replaced by all sorts of interesting things, that's just the latest one i found.

i don't understand why unsigned long = &header isn't good enough.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

you know.. i'm looking at this situation and thinking...

how about if i use the "loops" part of the header and set each one to a different value to enumerate them.... then, when i get my MM_WIM_DATA message, i can get the loops value, and i'll know..

..i'll know which one it is...

..but then i think, no, that would be wrong, that would be so ass backwards that to press a single key would be bastardry...


shit i'm sorry that i spent all my time doing dsp and audio and didn't think, f**k i'll build a database then i'll be really good at all these inane address tags and formats i could never give a f**k about using..


didn't anyone ever think when they made windows, that people would want to use the microphone and the speaker at the same time without developing a mastery of multithreading?

it's my deficit, but i never set out to be as good as other people stipulate, so, like many other people, i can't do this.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

xoxos wrote:still ITD, _createthreadex requires a library to be linked which other borland fclt users appear to have difficulty achieving as well*.

how unpleasingly complicated.
I don't know Borland fclt.

When you say _createthreadex do you mean _beginthreadex?

Does the compiler compile the code but doesn't link?

If it's so, probably, the function is there but you should include an option to tell the compiler you're using multithreading.

There's also a win32 function called CreateThread, here's the differences:

http://stackoverflow.com/questions/3315 ... tethread-c

Post

xoxos wrote:again i'm sorry to ask, but i'm not able to discern otherwise -



in MM_WIM_DATA, lParam is the address of the buffer. i can print lParam on the screen and observe eight adresses if i am using eight buffers.

how can i check this against the buffers to indicate which one is being returned?

at first i thought, unsigned long addressvalue[BUFFERS] = &MyWavHeader[BUFFERS]

but that tells me a long int is not the same as a HDR thingy. so, i've tried casting them, doing it directly -
for (i = 0; i < BUFFERS; i++) if (&MyFunkyWavInHdr == (LPWAVEHDR) lParam) whatiwanttoknow = i;

and this never matches either. that (LPWAVEHDR) has been replaced by all sorts of interesting things, that's just the latest one i found.

i don't understand why unsigned long = &header isn't good enough.


Without knowing how those variables are declared it's hard to tell you why you don't get any match.

Why are you using "unsigned long"?

Post

started my day with a 16th century fencing manual, "The philosophers are of opinion that we must understand things before starting them and that after it is easy." :)

i know who you are so posting for explanation to free you from this..

why unsigned long.. i think i've only ever had one precedent application for lParam.. haven't reviewed any material but remembering it's the lower part of the message, i figure that it's one or two bytes.. so i figured that ought to cover it..

since my compiler balks i know that doesn't cover it, and it is frustrating that even very simple things i simply don't use balk me when i try to pick them up. "gotta be a byte or a short, an int of any kind would do it."

i'm not "qualifying" lParam when i use it, i'm just typing x = lParam; it works, when i try to make it anything else i find it doesn't work (eg. msg.lParam "needs a structure", i've never wanted to make a modular environment and have never felt the need to use structures..)

..i'm going to stop there.. you're bitwise, you've got better things to do. i'm a very simple, elementary person by my own predeliction. as you can see, the case is that i don't have the knowledge to handle these things, and i don't really want it.

eg. 3d animation - i sorted out frustrums and points before research, but the math to implement opaque surfaces isn't so much beyond me as it is too much work (not for me, for the computer). i still believe i have to be reincarnated as an intel chip and do all these sums by hand in the next life, and i'd hate for it to be for paltry reasons. i have a bullshit/culture/other people threshold i've learned to guard, and i'd be ashamed of myself if i made a computer do all that for an opaque surface unless i had a good reason.

very alien envaluation to others.

i use computers to make statements about culture, to contrast the nature of the industry with the nature of a less coddled perspective. i have no wish to be a competent programmer, only to say a few words others omit. you've probably observed my dramatic outbursts in the past every time it occurs to me to acquire some occluded method. i'm quite traumatised by previous acquisitions (consider how much precious intellectualism there is in petzold's prose that discretises his text from being near a model of efficacy - he's almost as bad as me).

i never have money, i'm fixated beyond empathy on a relic compiler and have a low tolerance for others. what are the chances of successful assistance with these criteria? if it weren't for the fact that some people like the things i do manage, behaving like this i'd be the least appreciated person on this board.

said it like this last night... using an SDK with poor documentation is like digging in someone else's asshole with your finger when you are unsure of what you are looking for until you happen to find it. to me, this observation has a much higher value to culture than my finished product. it's really a gem and should be inscribed places. which would a committed person apply themselves to? i've seen so many people suffer from petzold and other documents that i really believe it is important for these people to appreciate the breadth of their readership and the possible nobility of their intent (well at least if they're a culturally disenfranchised person).

all of the nebulous and fragmented statements i've been through trying to solute this are a form of damage, i don't need more right now.

whereas i can remember how to spell just about every name i've ever seen, all my customer's names et c., i've done the create/beginthreadex mistake elsewhere too. i don't want to remember beginthreadex, because i know i don't want to multithread. multithreading is for people who make performance applications and keep up. i'm not about keeping up, my cultural vector is not aligned in this manner.

thanks, sorry for all the bitching :)


*edit* there's a quote from tagore in my sig in case you hide them... "where roads are made i lose my way" :lol:

aamof.. if you are able to apply the quote at the *beginning* of this post as corollary, you may rapidly approach my wider sentiment towards culture
Last edited by xoxos on Sat Aug 09, 2014 4:11 pm, edited 2 times in total.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

linking this again because i think the visualisation ought to be extremely tangible to novices and maybe someone will borrow it
http://xoxos.net/temp/delay.zip
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

xoxos wrote:so, i've tried casting them, doing it directly -
for (i = 0; i < BUFFERS; i++) if (&MyFunkyWavInHdr == (LPWAVEHDR) lParam) whatiwanttoknow = i;

and this never matches either. that (LPWAVEHDR) has been replaced by all sorts of interesting things, that's just the latest one i found.


If MyFunkyWavInHdr is an array of pointers to WAVEHDR, it should be:

if (MyFunkyWavInHdr == (LPWAVEHDR) lParam)
...

Post Reply

Return to “DSP and Plugin Development”