LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

sound card pcm

I have seen several topics relating to this, but none completely answer my question. I'm making a sound card oscilloscope by recording line-in data. My computer is a Dell Latitude D620 laptop. I believe it has a STAC9200 SigmaTel/IDT codec at line-in. The datasheet can be found here: http://www.idt.com/?genId=STAC9200# I can record my line-in audio using the waveIn waveform audio API as follows: MMRESULT result; HWAVEIN waveHandle; WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; // standard uncompressed PCM waveFormat.nChannels = 1; // mono waveFormat.nSamplesPerSec = 44100; // 44.1 kHz sample rate waveFormat.wBitsPerSample = 16; // 16 bits per sample waveFormat.nBlockAlign = 2; // 2 bytes per sample waveFormat.nAvgBytesPerSec = 88200; // 44.1 kHz * 2 bytes per sample waveFormat.cbSize = 0; // no extra data appended for PCM short int waveData[220500]; WAVEHDR waveHeader; waveHeader.dwBufferLength = 5 * 88200; // 5 seconds * 88200 bytes per second waveHeader.dwFlags = 0; waveHeader.lpData = (LPSTR)waveData; result = waveInOpen( &waveHandle, 0, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_DIRECT ); result = waveInPrepareHeader( waveHandle, &waveHeader, sizeof(waveHeader) ); result = waveInAddBuffer( waveHandle, &waveHeader, sizeof(waveHeader) ); result = waveInStart( waveHandle ); while ( waveInUnprepareHeader ( waveHandle, &waveHeader, sizeof(waveHeader) ) == WAVERR_STILLPLAYING ); result = waveInClose( waveHandle ); This gives me a waveform, but I do not understand the amplitude of the data. I can change the amplitude of my recording by manipulating the line-in volume slider. In fact, I can move the slider while my program is recording and see that the amplitude is dynamically changing. If I move the line-in volume slider to its max setting, my waveform data starts clipping. I assumed that my data would clip at about +/-32768 because I am recording 16-bit audio, but the data actually clips at about +/-13000. I think I can manipulate the line-in volume programmatically from the mixer API, so I can arbitrarily set my line-in volume to something. But I don't understand the relationship between the raw PCM data from the ADC to the waveform data I am recording. It doesn't seem like it is a simple scaling, because my audio should be clipping at +/-32768 if that were the case. How do I get the raw PCM data, as seen by the STAC9200? Also, the STAC9200 says it is capable of 24-bit sampling. How do I record at 24-bit? According to msdn, I can only use 8-bit and 16-bit when using the WAVE_FORMAT_PCM tag. Ultimately, I want to be able to convert ADC counts to voltage.
0 Kudos
Message 1 of 4
(4,057 Views)

Hi treespicer,

 

To clarify, are you using any NI products, or do you have any questions about using NI products?  If not, you may have better luck posting this on a forum for the products you are using.

 

It doesn't look like your post was formatted correctly, so I'm re-posting it below to make it easier to read.

 

Regards,

Andrew

 

 

I have seen several topics relating to this, but none completely
answer my question.  I'm making a sound card oscilloscope by recording
line-in data.  My computer is a Dell Latitude D620 laptop.  I believe it
has a STAC9200 SigmaTel/IDT codec at line-in.  The datasheet can be found
here: <a href="http://www.idt.com/?genId=STAC9200#" target="_blank"
I can record my line-in audio using the waveIn waveform audio API as
follows:
MMRESULT result;
HWAVEIN waveHandle;
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;  // standard uncompressed PCM
waveFormat.nChannels = 1;     // mono
waveFormat.nSamplesPerSec = 44100;   // 44.1 kHz sample rate
waveFormat.wBitsPerSample = 16;    // 16 bits per sample
waveFormat.nBlockAlign = 2;     // 2 bytes per sample
waveFormat.nAvgBytesPerSec = 88200;   // 44.1 kHz * 2 bytes per sample
waveFormat.cbSize = 0;      // no extra data appended for PCM
short int waveData[220500];
WAVEHDR waveHeader;
waveHeader.dwBufferLength = 5 * 88200;  // 5 seconds * 88200 bytes per
second
waveHeader.dwFlags = 0;
waveHeader.lpData = (LPSTR)waveData;
result = waveInOpen( &amp;waveHandle, 0, &amp;waveFormat, 0, 0,
CALLBACK_NULL | WAVE_FORMAT_DIRECT );
result = waveInPrepareHeader( waveHandle, &amp;waveHeader,
sizeof(waveHeader) );
result = waveInAddBuffer( waveHandle, &amp;waveHeader,
sizeof(waveHeader) );
result = waveInStart( waveHandle );
while ( waveInUnprepareHeader ( waveHandle, &amp;waveHeader,
sizeof(waveHeader) ) == WAVERR_STILLPLAYING );
result = waveInClose( waveHandle );
This gives me a waveform, but I do not understand the amplitude of the
data.  I can change the amplitude of my recording by manipulating the
line-in volume slider.  In fact, I can move the slider while my program is
recording and see that the amplitude is dynamically changing.  If I move
the line-in volume slider to its max setting, my waveform data starts
clipping.  I assumed that my data would clip at about +/-32768 because I am
recording 16-bit audio, but the data actually clips at about +/-13000.  I
think I can manipulate the line-in volume programmatically from the mixer
API, so I can arbitrarily set my line-in volume to something.  But I don't
understand the relationship between the raw PCM data from the ADC to the
waveform data I am recording.  It doesn't seem like it is a simple scaling,
because my audio should be clipping at +/-32768 if that were the case.
How do I get the raw PCM data, as seen by the STAC9200?  Also, the STAC9200
says it is capable of 24-bit sampling.  How do I record at 24-bit?
According to msdn, I can only use 8-bit and 16-bit when using the
WAVE_FORMAT_PCM tag.  Ultimately, I want to be able to convert ADC counts
to voltage.

 

 

National Instruments
0 Kudos
Message 2 of 4
(4,029 Views)

Sorry, I should have previewed my post.  I am using LabWindows/CVI to make a sound card oscilloscope/spectrum analyzer.  I saw that others on the forum had done similar projects, so I thought someone might be familiar with the topic.

 

I've modified my code to fix the line-in volume to 50%.  Whenever I click a "RECORD" button on my interface, the callback function performs this:

 

 

			MMRESULT result;
			HWAVEIN waveHandle;
			WAVEFORMATEX waveFormat;
			short int waveData[220500];
			WAVEHDR waveHeader;
			HMIXER mixerHandle;
			MIXERLINE mixerLine;
			MIXERLINECONTROLS mixerLineControls;
			MIXERCONTROL mixerControl;
			MIXERCONTROLDETAILS mixerDetails;
			MIXERCONTROLDETAILS_UNSIGNED lineInVolume;

			
			waveFormat.wFormatTag = WAVE_FORMAT_PCM; 	// standard uncompressed PCM
			waveFormat.nChannels = 1;					// mono
			waveFormat.nSamplesPerSec = 44100;			// 44.1 kHz sample rate
			waveFormat.wBitsPerSample = 16;				// 16 bits per sample
			waveFormat.nBlockAlign = 2;					// 2 bytes per sample
			waveFormat.nAvgBytesPerSec = 88200;			// 44.1 kHz * 2 bytes per sample
			waveFormat.cbSize = 0;						// no extra data appended for PCM
			// open waveform audio input, no ACM
			result = waveInOpen( &waveHandle, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_DIRECT );
			
			// open mixer associated with waveform input
			result = mixerOpen( &mixerHandle, (UINT)waveHandle, 0, 0, MIXER_OBJECTF_HWAVEIN );
			
			mixerLine.cbStruct = sizeof(MIXERLINE);							// initialize size
			mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;	// look for line-in source mixer line
			// get line info about the line-in source mixer line
			result = mixerGetLineInfo( (HMIXEROBJ)mixerHandle, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE );

			mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);				// initialize size
			mixerLineControls.dwLineID = mixerLine.dwLineID;					// line-in source mixer line ID
			mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;	// look for volume control of line-in
			mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);					// initialize size
			mixerLineControls.cControls = 1;									// one control (volume)
			mixerLineControls.pamxctrl = &mixerControl;							// assign control storage structure
			// get the volume control of the line-in source mixer line
			result = mixerGetLineControls( (HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE );
		
			mixerDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);			// initialize size
			mixerDetails.dwControlID = mixerControl.dwControlID;			// line-in volume control ID
			mixerDetails.cChannels = 1;										// one channel (mono)
			mixerDetails.hwndOwner = 0;										// no window handle
			mixerDetails.cMultipleItems = 0;								// no multiple items
			mixerDetails.paDetails = &lineInVolume;							// assign volume storage structure
			mixerDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);	// initialize size
			lineInVolume.dwValue = 0x7FFF;									// set volume to this value (50%)
			// set volume control of the line-in source mixer line
			result = mixerSetControlDetails( (HMIXEROBJ)mixerHandle, &mixerDetails, MIXER_GETCONTROLDETAILSF_VALUE );

			waveHeader.dwBufferLength = 5 * 88200;		// 5 seconds * 88200 bytes per second
			waveHeader.dwFlags = 0;						// initialize flags to 0
			waveHeader.lpData = (LPSTR)waveData;		// assign waveform data storage structure			
			// prepare header
			result = waveInPrepareHeader( waveHandle, &waveHeader, sizeof(waveHeader) );

			// add buffer
			result = waveInAddBuffer( waveHandle, &waveHeader, sizeof(waveHeader) );

			// start recording
			result = waveInStart( waveHandle );
			
			// wait until recording is finished
			while ( waveInUnprepareHeader ( waveHandle, &waveHeader, sizeof(waveHeader) ) == WAVERR_STILLPLAYING );
			
			// close waveform input and mixer devices
			result = waveInClose( waveHandle );
			result = mixerClose( mixerHandle );
			
			DeleteGraphPlot (panelHandle, PANEL_CHANNEL_LEFT, -1, VAL_IMMEDIATE_DRAW);
			PlotWaveform (panelHandle, PANEL_CHANNEL_LEFT, waveData, 220500, VAL_SHORT_INTEGER,
				1.0, 0.0, 0.0, 1.0/44100, VAL_THIN_LINE, VAL_NO_POINT, VAL_SOLID, 1, VAL_RED);

 

However, the data plotted to the graph window is not at a static volume level.  In the next three samples, I am recording 5 seconds with no input to line-in, so I should record low-level white noise at constant amplitude.  But I get the following:

 

 

 

 

And I don't have a clue as to what is happening.  It's as though there's some sort of AGC or compression on, but I have no idea how to control it.  Does anybody have any ideas?

 

0 Kudos
Message 3 of 4
(4,000 Views)

Hi treespicer,

 

It looks like you are using Windows calls to get the data from the sound card. Because of this, the National Instruments forum may not be the best forum to get help with your application. You might try posting your question on a Windows development forum.

Steven K.
National Instruments
Software Engineer
0 Kudos
Message 4 of 4
(3,967 Views)