Using xaudio2 and a parallel port together - c++

I am using C++ to code a neuroscience experiment in my research lab. We are studying tactile perception, and we use a parallel port to trigger our brain stimulating device. The timing is very important.
We recently started using xaudio2 to play very simple WAV files, which are used to trigger our vibrotactile stimulators (for example, our "tactile" stimuli are 100 and 200 Hz sounds with a duration of 100 ms, that move a piezo-electric stimulator that is placed on the hand).
Our problem is that we need to send out 3 commands to the brain stimulator via the parallel port: once 40 ms before the tactile stimulus, once 10 ms after the start of the stimulus, and a third time 60 ms into the stimulus. Remember, the tactile stimulus lasts 100 ms.
However, the way xaudio2 triggers sound is that it plays the wave and blocks until it is finished. As a consequence, the program ignores the two parallel port commands which should be sent during the stimulus.
Does anybody know how I can make sure the tactile stimulus is still triggered for the entirety of its 100 ms duration, but also send out parallel port commands during it? I am using the MSDN XAudio2Samples as the basic structure for playing the wav files, and the PlayWave function is the one which "blocks" any other input while the Wav file is playing -- but I can't figure out how to modify it so that it will also take my parallel port commands (which are Out32(888,1)) while a sound is being played.
Thank you!
Here is the code for the PlayWave function:
//--------------------------------------------------------------------------------------
// Name: PlayWave
// Desc: Plays a wave and blocks until the wave finishes playing
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT PlayWave( IXAudio2* pXaudio2, LPCWSTR szFilename )
{
//
// Locate the wave file
//
WCHAR strFilePath[MAX_PATH];
HRESULT hr = FindMediaFileCch( strFilePath, MAX_PATH, szFilename );
if( FAILED( hr ) )
{
wprintf( L"Failed to find media file: %s\n", szFilename );
return hr;
}
//
// Read in the wave file
//
std::unique_ptr<uint8_t[]> waveFile;
DirectX::WAVData waveData;
if ( FAILED( hr = DirectX::LoadWAVAudioFromFileEx( strFilePath, waveFile, waveData ) ) )
{
wprintf( L"Failed reading WAV file: %#X (%s)\n", hr, strFilePath );
return hr;
}
//
// Play the wave using a XAudio2SourceVoice
//
// Create the source voice
IXAudio2SourceVoice* pSourceVoice;
if( FAILED( hr = pXaudio2->CreateSourceVoice( &pSourceVoice, waveData.wfx ) ) )
{
wprintf( L"Error %#X creating source voice\n", hr );
return hr;
}
// Submit the wave sample data using an XAUDIO2_BUFFER structure
XAUDIO2_BUFFER buffer = {0};
buffer.pAudioData = waveData.startAudio;
buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
buffer.AudioBytes = waveData.audioBytes;
if ( waveData.loopLength > 0 )
{
buffer.LoopBegin = waveData.loopStart;
buffer.LoopLength = waveData.loopLength;
buffer.LoopCount = 1; // We'll just assume we play the loop twice
}
#if (_WIN32_WINNT < 0x0602 /*_WIN32_WINNT_WIN8*/)
if ( waveData.seek )
{
XAUDIO2_BUFFER_WMA xwmaBuffer = {0};
xwmaBuffer.pDecodedPacketCumulativeBytes = waveData.seek;
xwmaBuffer.PacketCount = waveData.seekCount;
if( FAILED( hr = pSourceVoice->SubmitSourceBuffer( &buffer, &xwmaBuffer ) ) )
{
wprintf( L"Error %#X submitting source buffer (xWMA)\n", hr );
pSourceVoice->DestroyVoice();
return hr;
}
}
#else
if ( waveData.seek )
{
wprintf( L"This platform does not support xWMA or XMA2\n" );
pSourceVoice->DestroyVoice();
return hr;
}
#endif
else if( FAILED( hr = pSourceVoice->SubmitSourceBuffer( &buffer ) ) )
{
wprintf( L"Error %#X submitting source buffer\n", hr );
pSourceVoice->DestroyVoice();
return hr;
}
hr = pSourceVoice->Start( 0 );
// Let the sound play
BOOL isRunning = TRUE;
while( SUCCEEDED( hr ) && isRunning )
{
XAUDIO2_VOICE_STATE state;
pSourceVoice->GetState( &state );
isRunning = ( state.BuffersQueued > 0 ) != 0;
// Wait till the escape key is pressed
if( GetAsyncKeyState( VK_ESCAPE ) )
break;
Sleep( 10 );
}
// Wait till the escape key is released
while( GetAsyncKeyState( VK_ESCAPE ) )
Sleep( 10 );
pSourceVoice->DestroyVoice();
return hr;
}

Related

How to define an audio output programmatically on Windows 10?

I'm developing a C++ application which implements the Microsoft Speech API (SAPI). I developed numerous functions related to Text-To-Speech. Among them a function which allows the listing of the audio outputs, and a function that allows to define an audio output.
I started developing this program on Windows 7, but now I switched to Windows 10. However, the function that defines the audio output doesn't work anymore. I didn't edited a thing in my code, and on Windows 7 it worked perfectly.
Here is the code which lists the available audio outputs
int getAudioOut( int auOut ) //get audio outputs function
{
if( SUCCEEDED( hr ) )
{
//Enumerate Audio Outputs
hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
cpEnum->GetCount( &vCount );
cpEnum->Item( saveAudio, &cpAudioOutToken );
SpGetDescription( cpAudioOutToken, &dynStr );
printf( "Defined audio output is: %ls\n\n", dynStr );
dynStr.Clear();
//Loop through the audio output list and enumerate them all
for( audioOut = 0; audioOut <= vCount - 1; audioOut++ )
{
cpAudioOutToken.Release();
cpEnum->Item( audioOut, &cpAudioOutToken );
SpGetDescription( cpAudioOutToken, &dynStr );
printf( "Defined Audio Output %i - %ls\n", audioOut, dynStr );
dynStr.Clear();
}
printf( "\n" );
audioOut = saveAudio;
cpEnum.Release();
cpAudioOutToken.Release();
}
else
{
printf( "Could not enumerate available audio outputs\n" );
}
return true;
}
Here is the code which allows the definition of an audio output
int setAudioOut( int auOut ) //define audio output function
{
if( SUCCEEDED( hr ) )
{
hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
cpEnum->GetCount( &vCount );
size_t nOut = auOut;
if( nOut >= vCount )
{
cout << "Not so many audio outputs available! Try again\n" << endl;
}
else
{
cout << "Success" << endl;
}
ULONG audioOut = static_cast<ULONG>( nOut ); //convert nOut to ULONG audioOut
cpEnum->Item( audioOut, &cpAudioOutToken );
SpGetDescription( cpAudioOutToken, &dynStr );
printf( "You chose %ls\n\n", dynStr );
cpVoice->SetOutput( cpAudioOutToken, TRUE ); //Initialization of the Audio Output
dynStr.Clear();
cpEnum.Release();
cpAudioOutToken.Release();
saveAudio = audioOut; //define saveAudio to audioOut value
}
else
{
printf( "Could not set audio output\n" );
}
return true;
}
When I start my program and call the getAudioOut function, I get following listing:
The first line shows the default audio output, and the two below are the available outputs. On Windows 7, when I set the second audio output (Lautsprecher / Kopfhörer) as default, then no sound comes out from the first (Digitalaudio), which makes sense. However, on Windows 10 I have reproduced the same procedure but it doesn't work. The audio output is always defined according to the audio menu.
My question is, does anyone have experienced this issue? Is there an alternative to define an audio output programmatically?
I edited the code as #NikolayShmyrev suggested but it didn't change a thing. However, I continued to dig into the problem an found out that the issue came from another function. Indeed, when I switched from Windows 7 to Windows 10, I encountered other problems with the speech synthesis function and the speech to WAV file function. When I started the program and called the Text-To-Speech function, everything worked great. When I called the Speech2Wav function, it worked too. However, when I recalled the Text-To-Speech function, the variable HRESULT hr = S_OK; changed its value and no sound was played. The value of hr was set to -2147200968 which corresponds to Error 0x80045038: SPERR_STREAM_CLOSED (source/list of error codes)
To solve this issue I had to define an audio output like this cpVoice->SetOutput( cpAudioOutToken, TRUE ); in the Text-To-Speech function.
This brings us back to the problem I stated above. When I set the audio output in the function setAudioOut, I release its value at the end cpAudioOutToken.Release();
However, I reuse the same variable in the Text-To-Speech function. Its value was set to nothing because I released it when I defined the audio output. This is why the audio output was always set to default. In order to solve the problem, I assigned the value of cpAudioOutToken to another variable called cpSpeechOutToken.
Here is the code for the function setAudioOut
int setAudioOut( int auOut ) //define audio output function
{
if( SUCCEEDED( hr ) )
{
hr = SpEnumTokens( SPCAT_AUDIOOUT, NULL, NULL, &cpEnum );
cpEnum->GetCount( &vCount );
size_t nOut = auOut;
if( nOut >= vCount )
{
cout << "Not so many audio outputs available! Try again\n" << endl;
return 0;
}
else
{
cout << "Success" << endl;
}
ULONG audioOut = static_cast<ULONG>( nOut ); //convert nOut to ULONG audioOut
cpEnum->Item( audioOut, &cpAudioOutToken );
SpGetDescription( cpAudioOutToken, &dynStr );
printf( "You chose %ls\n\n", dynStr );
cpVoice->SetOutput( cpAudioOutToken, TRUE ); //Initialization of the Audio Output
dynStr.Clear();
cpEnum.Release();
cpSpeechOutToken = cpAudioOutToken;
cpAudioOutToken.Release();
saveAudio = audioOut; //define saveAudio to audioOut value
}
else
{
printf( "Could not set audio output\n" );
}
return true;
}
Here is the code from the Text-To-Speech function
int ttsSpeak( const char* text ) //Text to Speech speaking function
{
if( SUCCEEDED( hr ) )
{
string xmlSentence( text );
hr = SpEnumTokens( SPCAT_VOICES_WIN10, NULL, NULL, &cpEnum );
//Replace SPCAT_VOICES_WIN10 with SPCAT_VOICES if you want to use it on Windows 7
cpEnum->Item( saveVoice, &cpVoiceToken ); //get saveVoice token defined at line 175
cpVoice->SetVoice( cpVoiceToken ); //Initialization of the voice
//string strText( text );
int wchars_num = MultiByteToWideChar( CP_ACP, 0, xmlSentence.c_str(), -1, NULL, 0 );
wchar_t* wstr = new wchar_t[ wchars_num ];
MultiByteToWideChar( CP_ACP, 0, xmlSentence.c_str(), -1, wstr, wchars_num );
printf( "Text To Speech processing\n" );
cpVoice->SetOutput( cpSpeechOutToken, TRUE );
hr = cpVoice->Speak( wstr, SVSFIsXML, NULL );
saveText = xmlSentence.c_str();
cpEnum.Release();
cpVoiceToken.Release();
delete new wchar_t[ wchars_num ];
}
else
{
printf( "Could not speak entered text\n" );
}
return true;
}

MS Kinect FaceTracker creating IFTResult

I have a fairly simple application that contains the following:
context->mFaceTracker = FTCreateFaceTracker();
hr = context->mFaceTracker->Initialize( &mVideoCameraConfig, &mDepthCameraConfig, NULL, NULL );
which works fine and returns S_OK and mFaceTracker is (as far as I can tell) initialized properly. However, the next line is:
hr = context->mFaceTracker->CreateFTResult( &context->mFTResult );
which always returns FT_ERROR_UNINITIALIZED, doesn't allocate the pointer, and has me puzzled. I've tried many different strategies for getting this to work from changing how the threading for the device and detector works, to changing my FTcontext object from a class to a struct to match the samples, all with no success. The Kinect SDK samples all work fine, but trying to use them in my own application doesn't seem to, despite my closely mirroring how they initialize the device and the Face Tracker. I'm curious if anyone else has run into this or similar problems around initializing either IFTFaceTracker or the IFTResult. Also, I'm curious how else I can test the IFTFaceTracker for correct intialization, other than testing the HRESULT that Initialize() returns. Thanks in advance,
=== edit
I've had a few request for more code. It's built on Cinder and is using this block for Cinder: https://github.com/BanTheRewind/Cinder-KinectSdk
I can't post all of the code, but I've posted at least most of the relevant Kinect initialization code here:
void Kinect::start( const DeviceOptions &deviceOptions )
{
if ( !mCapture ) {
// Copy device options
mDeviceOptions = deviceOptions;
string deviceId = mDeviceOptions.getDeviceId();
int32_t index = mDeviceOptions.getDeviceIndex();
// Clamp device index
if ( index >= 0 ) {
index = math<int32_t>::clamp( index, 0, math<int32_t>::max( getDeviceCount() - 1, 0 ) );
}
// Initialize device instance
long hr = S_OK;
if ( index >= 0 ) {
hr = NuiCreateSensorByIndex( index, &mSensor );
if ( FAILED( hr ) ) {
trace( "Unable to create device instance " + toString( index ) + ": " );
error( hr );
return;
}
} else if ( deviceId.length() > 0 ) {
_bstr_t id = deviceId.c_str();
hr = NuiCreateSensorById( id, &mSensor );
if ( FAILED( hr ) ) {
trace( "Unable to create device instance " + deviceId + ":" );
error( hr );
return;
}
} else {
trace( "Invalid device name or index." );
return;
}
// Check device
hr = mSensor != 0 ? mSensor->NuiStatus() : E_NUI_NOTCONNECTED;
if ( hr == E_NUI_NOTCONNECTED ) {
error( hr );
return;
}
// Get device name and index
if ( mSensor != 0 ) {
mDeviceOptions.setDeviceIndex( mSensor->NuiInstanceIndex() );
BSTR id = ::SysAllocString( mSensor->NuiDeviceConnectionId() );
_bstr_t idStr( id );
if ( idStr.length() > 0 ) {
std::string str( idStr );
mDeviceOptions.setDeviceId( str );
}
::SysFreeString( id );
} else {
index = -1;
deviceId = "";
}
flags |= NUI_INITIALIZE_FLAG_USES_COLOR;
}
hr = mSensor->NuiInitialize( flags );
if ( FAILED( hr ) ) {
trace( "Unable to initialize device " + mDeviceOptions.getDeviceId() + ":" );
error( hr );
return;
}
hr = mSensor->NuiSkeletonTrackingEnable( 0, flags );
if ( FAILED( hr ) ) {
trace( "Unable to initialize skeleton tracking for device " + mDeviceOptions.getDeviceId() + ": " );
error( hr );
return;
}
mIsSkeletonDevice = true;
mThread = CreateThread(NULL, 0, &Kinect::StaticThread, (PVOID)this, 0, 0);
}
}
DWORD WINAPI Kinect::StaticThread(PVOID lpParam)
{
Kinect* device = static_cast<Kinect*>(lpParam);
if (device)
{
return device->run();
}
return 0;
}
void run() {
if(mSensor) {
if(mEnabledFaceTracking)
{
if(mNeedFaceTracker) {
mFaceTracker = new FaceTracker(
mDeviceOptions.getVideoSize().x,
mDeviceOptions.getVideoSize().y,
mDeviceOptions.getDepthSize().x,
mDeviceOptions.getDepthSize().y,
1.0,
1 );
mNeedFaceTracker = false;
}
// make sure we have both color && depth buffers to work with
if(newDepth || newVideo)
{
FT_SENSOR_DATA sensorData(mFTColorImage, mFTDepthImage);
FT_VECTOR3D hint[2]; // this is initialized elsewhere
mFaceTracker->checkFaces( (NUI_SKELETON_FRAME*) &skeletonFrame, mFTColorImage, mFTDepthImage, 1.0, 0);
if(mFaceTracker->getNumFaces() > 0) {
cout << " we have a face " << mFaceTracker->getNumFaces() << endl;
mNewFaceTrackData = true;
mFaceData.clear();
for( int i = 0; i < mFaceTracker->getNumFaces(); i++) {
Face newFace;
mFaceTracker->getProjectedShape(0, newFace.scale, newFace.rotation, newFace.transform, newFace.screenPositions);
mFaceData.push_back(newFace);
}
}
}
}
Sleep( 8 );
}
}
It looks like you never call (or omitted including in the code sample) NuiImageStreamOpen(), such as this code snippet from the SingleFace sample, KinectSensor.cpp in the Init method:
hr = NuiImageStreamOpen(
colorType,
colorRes,
0,
2,
m_hNextVideoFrameEvent,
&m_pVideoStreamHandle );
if (FAILED(hr))
{
return hr;
}
hr = NuiImageStreamOpen(
depthType,
depthRes,
(bNearMode)? NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE : 0,
2,
m_hNextDepthFrameEvent,
&m_pDepthStreamHandle );
Calling those before you call CreateFTResult() may fix the uninitialized error.
Additionally, you call CreateThread() and then call run(), but there is no while loop so that thread will exit almost immediately, certainly without enough time for the Kinect to start providing data to the FaceTracking.
It doesn't look like you have included the Thread or event loop that is checking the sensor for new data, updating mFTColorImage and mFTDepthImage, and setting the newDepth and newVideo flags. This could be in the same thread that you create above (provided you create a while loop, and ignoring performance or other classes needing the Kinect data), or could be a different thread as in the SingleFace Kinect SDK sample.

GmfBridge doesn't connect sink filter with source filter

I am trying to use GmfBridge library to dynamically change source filters from cam to file and vice versa. All functions return S_OK (well, almost all - pMediaControlOutput->Run() returns S_FALSE actually, but in msdn said that this can be ok too), so I've assumed that all is ok but data isn't transfered to the other side of the bridge. I use GraphStudio to connect to remote graph and all seems to be ok - all filters in both graph are connected as it should be. But while playing I receive following messages in bridge log file:
0: Started 2011-09-10 15:58:38
0: Buffer minimum 100
0: Added stream 1: 畡楤o, Decompressed Only, Discard mode
1: Sink 0x7ae0ca8 has 1 pins
1: Sink filter 0x7ae0cf8 in graph 0x193bf0
2: Source 0x7ae1030 has 1 pins
2: Source filter 0x7ae1080 in graph 0x194c18
14: ReceiveConnection Aware: false
14: Bridging 0x194c18 to 0x193bf0
14: Pin 0x7ae3438 disconnect
25: Source 0x7ae1030 pause from 0
25: Source pin 0x7ae3618 active
2234: Pin 0x7ae3438 receive 0x721ec68
2234: Sink pin 0x7ae3438 disconnected: discarding 0x721ec68
3389: Pin 0x7ae3438 receive 0x721ec68
3389: Sink pin 0x7ae3438 disconnected: discarding 0x721ec68
3940: Pin 0x7ae3438 receive 0x721ec68
3940: Sink pin 0x7ae3438 disconnected: discarding 0x721ec68
4440: Pin 0x7ae3438 receive 0x721ec68
So as you can see, left graph isn't connected to right one despite BridgeGraphs() returned S_OK and media sample is discarded. Below there is my code. Where am I going wrong?
// Create graphs
HRESULT hr = m_graphInput.CreateInstance(CLSID_FilterGraph);
ATLASSERT( SUCCEEDED( hr ) );
hr = m_graphOutput.CreateInstance(CLSID_FilterGraph);
ATLASSERT( SUCCEEDED( hr ) );
// Get IMediaControl interfaces
hr = m_graphInput.QueryInterface( IID_IMediaControl, (void**)&pMediaControlInput );
ATLASSERT( SUCCEEDED( hr ) );
hr = m_graphOutput.QueryInterface( IID_IMediaControl, (void**)&pMediaControlOutput );
ATLASSERT( SUCCEEDED( hr ) );
// Get builder interfaces
hr = m_graphInput.QueryInterface( IID_IGraphBuilder, (void**)&pBuilderInput );
ATLASSERT( SUCCEEDED( hr ) );
hr = m_graphOutput.QueryInterface( IID_IGraphBuilder, (void**)&pBuilderOutput );
ATLASSERT( SUCCEEDED( hr ) );
// Load source filter (on sink side)
LocateFilter( SOURCE_FILTER_NAME, CLSID_LegacyAmFilterCategory, &inputDevice );
hr = m_graphInput->AddFilter( inputDevice, SOURCE_FILTER_NAME );
ATLASSERT( SUCCEEDED( hr ) );
// Load render filter (on bridge's source side)
LocateFilter( _T( "Default DirectSound Device" ), CLSID_AudioRendererCategory, &audioOutputPreview );
hr = m_graphOutput->AddFilter( audioOutputPreview, _T( "Default DirectSound Device" ) );
ATLASSERT( SUCCEEDED( hr ) );
// Init bridge
bridge.CreateInstance( __uuidof(GMFBridgeController) );
hr = bridge->SetBufferMinimum( 100 );
ATLASSERT( SUCCEEDED( hr ) );
hr = bridge->AddStream( false, eUncompressed, false );
ATLASSERT( SUCCEEDED( hr ) );
// Add sink filter and connect to input graph
IUnknownPtr pSinkFilter;
{
hr = bridge->InsertSinkFilter( m_graphInput, (IUnknown**)&pSinkFilter );
ATLASSERT( SUCCEEDED( hr ) );
// Using own functions get pins
IPin* pInAudio = CPinController::getOutputPin( inputDevice, _T("Audio"));
IPin* pOutAudio = CPinController::getInputPin( pSinkFilter );
hr = pBuilderInput->Connect( pOutAudio, pInAudio );
ATLASSERT( SUCCEEDED( hr ) );
}
// Add output filter and connect to output graph
IUnknownPtr pFeederFilter;
{
hr = bridge->InsertSourceFilter( pSinkFilter, m_graphOutput, &pFeederFilter );
ATLASSERT( SUCCEEDED( hr ) );
// Get pins
IPin* pInAudio = CPinController::getOutputPin( pFeederFilter/*, _T("Audio")*/);
IPin* pOutAudio = CPinController::getInputPin( audioOutputPreview );
hr = pBuilderOutput->Connect( pInAudio, pOutAudio );
ATLASSERT( SUCCEEDED( hr ) );
}
// Run left
hr = pMediaControlInput->Run();
ATLASSERT( SUCCEEDED( hr ) );
// Run right
hr = pMediaControlOutput->Run();
ATLASSERT( SUCCEEDED( hr ) );
hr = bridge->BridgeGraphs( m_graphOutput, m_graphInput );
ATLASSERT( SUCCEEDED( hr ) );
It's really ridiculous but about a few minutes ago after a day of searching we have found the answer. All deal was about really huge hole in GmfBridge. I was giving wrong interfaces here (there are graphs instead of bridge's sink and source filters) 'coz function needed pointers to IUnknown:
hr = bridge->BridgeGraphs( m_graphOutput, m_graphInput );
And in GmfBridge library this situation wasn't handled properly - there is no "else" brunch to handle error and function returns hr which was set in begin to S_OK:
HRESULT STDMETHODCALLTYPE BridgeGraphs(
/* [in] */ IUnknown *pSourceGraphSinkFilter,
/* [in] */ IUnknown *pRenderGraphSourceFilter)
{
HRESULT hr = S_OK;
...
// if we are not given both filters, then
// we need do nothing
IBridgeSinkPtr pSink = pSourceGraphSinkFilter;
IBridgeSourcePtr pSource = pRenderGraphSourceFilter;
if ((pSink != NULL) && (pSource != NULL))
{
...
}
return hr;
}
So as you can see it just says there is nothing wrong and then it just do nothing! I think it's good idea to notify authors of the lib about this bug.
Hope this info will help someone.

Read Epson Printer Status using C++ and ESC/P

I'm trying to get the status of a PLQ-20 Espon printer, using C++, but with no success.
I tried that using GDI API and Escape function with PASSTHROUGH parameter, but the printer never understands the escape codes with that function.
I tried to use WIN 32 API and the example code found here. That works for sending some escape codes like BEL (to sound the buzzer) or FF (Form Feed, to eject paper from the rear of the printer), but not ESC O (to eject paper from the front of the printer), ESC 0 / ESC 1 (to initialize the printer / reset errors).
So, I tried this way to get the status of the printer with a ESC j escape code but with no success (the ReadPrinter function returns 0). Moreover, the print buffer seems to be not empty nonetheless I only send escape commands.
I don't know if I do a mistake sending escape codes or trying to read the printer status.
If anyone could post examples, it could be fine for everyone because it's hard to find them on the web.
Below is the code I use to send commands and read the result
#include <Windows.h>
#include <StdIO.h>
// **********************************************************************
// PrintError - uses printf() to display error code information
//
// Params:
// dwError - the error code, usually from GetLastError()
// lpString - some caller-defined text to print with the error info
//
// Returns: void
//
void PrintError( DWORD dwError, LPCTSTR lpString )
{
#define MAX_MSG_BUF_SIZE 512
TCHAR *msgBuf;
DWORD cMsgLen;
cMsgLen = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL, dwError,
MAKELANGID(0, SUBLANG_ENGLISH_US), (LPTSTR) &msgBuf,
MAX_MSG_BUF_SIZE, NULL );
printf("%s Error [%d]:: %s\n", lpString, dwError, msgBuf );
LocalFree( msgBuf );
#undef MAX_MSG_BUF_SIZE
}
// end PrintError
// **********************************************************************
// **********************************************************************
// ReadFileWithAlloc - allocates memory for and reads contents of a file
//
// Params:
// szFileName - NULL terminated string specifying file name
// pdwSize - address of variable to receive file bytes size
// ppBytes - address of pointer which will be allocated and contain file bytes
//
// Returns: TRUE for success, FALSE for failure.
//
// Notes: Caller is responsible for freeing the memory using GlobalFree()
//
BOOL ReadFileWithAlloc( LPTSTR szFileName, LPDWORD pdwSize, LPBYTE *ppBytes )
{
HANDLE hFile;
DWORD dwBytes;
BOOL bSuccess = FALSE;
// Validate pointer parameters
if( ( pdwSize == NULL ) || ( ppBytes == NULL ) )
return FALSE;
// Open the file for reading
hFile = CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
PrintError( GetLastError(), TEXT("CreateFile()") );
return FALSE;
}
// How big is the file?
*pdwSize = GetFileSize( hFile, NULL );
if( *pdwSize == (DWORD)-1 )
PrintError( GetLastError(), TEXT("GetFileSize()") );
else
{
// Allocate the memory
*ppBytes = (LPBYTE)GlobalAlloc( GPTR, *pdwSize );
if( *ppBytes == NULL )
PrintError( GetLastError(), TEXT("Failed to allocate memory\n") );
else
{
// Read the file into the newly allocated memory
bSuccess = ReadFile( hFile, *ppBytes, *pdwSize, &dwBytes, NULL );
if( ! bSuccess )
PrintError( GetLastError(), TEXT("ReadFile()") );
}
}
// Clean up
CloseHandle( hFile );
return bSuccess;
}
// End ReadFileWithAlloc
// **********************************************************************
// **********************************************************************
// RawDataToPrinter - sends binary data directly to a printer
//
// Params:
// szPrinterName - NULL terminated string specifying printer name
// lpData - Pointer to raw data bytes
// dwCount - Length of lpData in bytes
//
// Returns: TRUE for success, FALSE for failure.
//
BOOL RawDataToPrinter( LPTSTR szPrinterName, LPBYTE lpData, DWORD dwCount )
{
HANDLE hPrinter;
DOC_INFO_1 DocInfo;
DWORD dwJob;
DWORD dwBytesWritten;
// Need a handle to the printer.
if( ! OpenPrinter( szPrinterName, &hPrinter, NULL ) )
{
PrintError( GetLastError(), TEXT("OpenPrinter") );
return FALSE;
}
// Fill in the structure with info about this "document."
DocInfo.pDocName = TEXT("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = TEXT("RAW");
// Inform the spooler the document is beginning.
if( (dwJob = StartDocPrinter( hPrinter, 1, (LPBYTE)&DocInfo )) == 0 )
{
PrintError( GetLastError(), TEXT("StartDocPrinter") );
ClosePrinter( hPrinter );
return FALSE;
}
// Start a page.
if( ! StartPagePrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("StartPagePrinter") );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
// Send the data to the printer.
if( ! WritePrinter( hPrinter, lpData, dwCount, &dwBytesWritten ) )
{
PrintError( GetLastError(), TEXT("WritePrinter") );
EndPagePrinter( hPrinter );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
/*********************************/
// CODE USED TO READ THE PRINTER
LPBYTE retData = NULL;
LPDWORD bbr = NULL;
if(ReadPrinter(hPrinter, retData, 1, bbr))
{
printf("OUT : %i", retData);
}
else
{
printf("Failed to read printer");
}
/*********************************/
// End the page.
if( ! EndPagePrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("EndPagePrinter") );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
// Inform the spooler that the document is ending.
if( ! EndDocPrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("EndDocPrinter") );
ClosePrinter( hPrinter );
return FALSE;
}
// Tidy up the printer handle.
ClosePrinter( hPrinter );
// Check to see if correct number of bytes were written.
if( dwBytesWritten != dwCount )
{
//printf( TEXT("Wrote %d bytes instead of requested %d bytes.\n"), dwBytesWritten, dwCount );
return FALSE;
}
return TRUE;
}
// End RawDataToPrinter
// **********************************************************************
int main( int argc, char* argv[] )
{
LPBYTE pBytes = NULL;
int textSize = 2;
DWORD dwSize = textSize;
pBytes = (LPBYTE) malloc (textSize*sizeof(BYTE));
pBytes[0] = 0x1B;
pBytes[1] = 0x6A;
if( ! RawDataToPrinter(L"EPSON PLQ-20 ESC/P2", pBytes, dwSize) )
printf("Failed to send data to printer.\n" );
else
printf("Data sent to printer.\n" );
free(pBytes);
return 0;
}
// end main
// **********************************************************************
Thanks!
The product brochure for the Epson PLQ-20, states that printer supports Olivetti PR2E, Epson ESC/P2, Wincor 4915, IBM PPDS, IBM 4722 FP emulation.
It looks like you are using ESC/P2 commands, however after a quick search I cannot find any command to read the current status of the printer.
ESC/P2 References
http://www.pcguru.plus.com/escp2.html
Complete Manual
http://gimp-print.sourceforge.net/developer-html/c464.html
Based on the above references, the command that controls how the paper is ejected is ESC EM

How to get width and height of directshow webcam video stream

I found a bit of code that gets me access to the raw pixel data from my webcam. However I need to know the image width, height, pixel format and preferably the data stride(pitch, memory padding or whatever you want to call it) if its ever gonna be something other than the width * bytes per pixel
#include <windows.h>
#include <dshow.h>
#pragma comment(lib,"Strmiids.lib")
#define DsHook(a,b,c) if (!c##_) { INT_PTR* p=b+*(INT_PTR**)a; VirtualProtect(&c##_,4,PAGE_EXECUTE_READWRITE,&no);\
*(INT_PTR*)&c##_=*p; VirtualProtect(p, 4,PAGE_EXECUTE_READWRITE,&no); *p=(INT_PTR)c; }
// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ;
HRESULT __stdcall Receive ( void* inst, IMediaSample *smp ) {
BYTE* buf; smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
//AM_MEDIA_TYPE* info;
//smp->GetMediaType(&info);
HRESULT ret = Receive_ ( inst, smp );
return ret;
}
int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev,LPSTR cmd,int show){
HRESULT hr = CoInitialize(0); MSG msg={0}; DWORD no;
IGraphBuilder* graph= 0; hr = CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&graph );
IMediaControl* ctrl = 0; hr = graph->QueryInterface( IID_IMediaControl, (void **)&ctrl );
ICreateDevEnum* devs = 0; hr = CoCreateInstance (CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &devs);
IEnumMoniker* cams = 0; hr = devs?devs->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &cams, 0):0;
IMoniker* mon = 0; hr = cams->Next (1,&mon,0); // get first found capture device (webcam?)
IBaseFilter* cam = 0; hr = mon->BindToObject(0,0,IID_IBaseFilter, (void**)&cam);
hr = graph->AddFilter(cam, L"Capture Source"); // add web cam to graph as source
IEnumPins* pins = 0; hr = cam?cam->EnumPins(&pins):0; // we need output pin to autogenerate rest of the graph
IPin* pin = 0; hr = pins?pins->Next(1,&pin, 0):0; // via graph->Render
hr = graph->Render(pin); // graph builder now builds whole filter chain including MJPG decompression on some webcams
IEnumFilters* fil = 0; hr = graph->EnumFilters(&fil); // from all newly added filters
IBaseFilter* rnd = 0; hr = fil->Next(1,&rnd,0); // we find last one (renderer)
hr = rnd->EnumPins(&pins); // because data we are intersted in are pumped to renderers input pin
hr = pins->Next(1,&pin, 0); // via Receive member of IMemInputPin interface
IMemInputPin* mem = 0; hr = pin->QueryInterface(IID_IMemInputPin,(void**)&mem);
DsHook(mem,6,Receive); // so we redirect it to our own proc to grab image data
hr = ctrl->Run();
while ( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
};
Bonus points if you can tell me how get this thing not to render a window but still get me access to the image data.
That's really ugly. Please don't do that. Insert a pass-through filter like the sample grabber instead (as I replied to your other post on the same topic). Connecting the sample grabber to the null renderer gets you the bits in a clean, safe way without rendering the image.
To get the stride, you need to get the media type, either through ISampleGrabber or IPin::ConnectionMediaType. The format block will be either a VIDEOINFOHEADER or a VIDEOINFOHEADER2 (check the format GUID). The bitmapinfo header biWidth and biHeight defines the bitmap dimensions (and hence stride). If the RECT is not empty, then that defines the relevant image area within the bitmap.
I'm going to have to wash my hands now after touching this post.
I am sorry for you. When the interface was created there were probably not the best programmer to it.
// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
BITMAPINFOHEADER bmpInfo; // current bitmap header info
int stride;
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ;
HRESULT __stdcall Receive ( void* inst, IMediaSample *smp )
{
BYTE* buf; smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
HRESULT ret = Receive_ ( inst, smp );
AM_MEDIA_TYPE* info;
HRESULT hr = smp->GetMediaType(&info);
if ( hr != S_OK )
{ //TODO: error }
else
{
if ( type->formattype == FORMAT_VideoInfo )
{
const VIDEOINFOHEADER * vi = reinterpret_cast<VIDEOINFOHEADER*>( type->pbFormat );
const BITMAPINFOHEADER & bmiHeader = vi->bmiHeader;
//! now the bmiHeader.biWidth contains the data stride
stride = bmiHeader.biWidth;
bmpInfo = bmiHeader;
int width = ( vi->rcTarget.right - vi->rcTarget.left );
//! replace the data stride be the actual width
if ( width != 0 )
bmpInfo.biWidth = width;
}
else
{ // unsupported format }
}
DeleteMediaType( info );
return ret;
}
Here's how to add the Null Renderer that suppresses the rendering window. Add directly after creating the IGraphBuilder*
//create null renderer and add null renderer to graph
IBaseFilter *m_pNULLRenderer; hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&m_pNULLRenderer);
hr = graph->AddFilter(m_pNULLRenderer, L"Null Renderer");
That dshook hack is the only elegant directshow code of which I am aware.
In my experience, the DirectShow API is a convoluted nightmare, requiring hundreds of lines of code to do even the simplest operation, and adapting a whole programming paradigm in order to access your web camera. So if this code does the job for you, as it did for me, use it and enjoy fewer lines of code to maintain.