December 01, 2024, 11:22:45 AM

News:

IWBasic runs in Windows 11!


RC5 Remote control demodulator

Started by sapero, February 04, 2009, 11:01:28 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

This is a mini manchester demodulator with support for sending commands to Winamp player.
All you need is a Philips RC-5 compatible remote control (infrared transmitter), a universal infrared receiver, 4k7 or 10k resistor, 10ÂÃ,µF capacitor (to separate IR digital output from PC), and any PC with DirectSound and microphone or line input. The typical power supply is 5V.

#include "windows.inc"
#include "dsound.inc"
#include "conio.inc"
#include "stdio.inc"
//#include "winamp\\wa_ipc.inc"

// winamp remote control for philips rc5 transmitters
// Connect infrared receiver module to microphone jack via 10ÂÃ,µF
// compile for console

// pseudo interrupt dispatcher is called every 22.6ÂÃ,µs
enum INTERRUPT_TYPE
{
INTERRUPT_SAMPLE,
INTERRUPT_TIMER
}

enum DECODER_STATE
{
STATE_IDLE = 0,
STATE_WAIT_075,
STATE_WAIT_NEXT_BIT
}

sub main()
{
WAVEFORMATEX wfx;
wfx.wFormatTag      = WAVE_FORMAT_PCM;
wfx.nChannels       = 1;     // mono
wfx.nSamplesPerSec  = 44100; // 44100 samples per second
wfx.nAvgBytesPerSec = 88200;
wfx.nBlockAlign     = 2;
wfx.wBitsPerSample  = 16;    // 16bits pre sample
wfx.cbSize          = 0;

IDirectSoundCapture8 *cap;
if (!DirectSoundCaptureCreate8(_DSDEVID_DefaultCapture, &cap, NULL))
{
DSCBUFFERDESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.dwSize        = sizeof(desc);
desc.dwFlags       = DSCBCAPS_WAVEMAPPED;
desc.dwBufferBytes = wfx.nAvgBytesPerSec/2;
desc.lpwfxFormat   = &wfx;

IDirectSoundCaptureBuffer *buf;
DWORD hr = cap->CreateCaptureBuffer(&desc, &buf, NULL);
if (hr)
{
printf("CreateCaptureBuffer error %x\n", hr);
system("pause");
}
else
{
IDirectSoundNotify *pn;
if (!buf->QueryInterface(_IID_IDirectSoundNotify, &pn))
{
DSBPOSITIONNOTIFY notify[2];
notify[0].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
notify[0].dwOffset = 0;
notify[1].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
notify[1].dwOffset = desc.dwBufferBytes / 2;

pn->SetNotificationPositions(2, &notify);
pn->Release();

if (!buf->Start(DSCBSTART_LOOPING))
{
HANDLE array[2];
array[0] = notify[0].hEventNotify;
array[1] = notify[1].hEventNotify;
// ignore first buffer notification
WaitForSingleObject(array[0], INFINITE);

// record data from microphone until key pressed
while (!kbhit())
{
DWORD index = WaitForMultipleObjects(2, &array, FALSE, 2000);
if (index < 2)
{
word *pData1, *pData2;
int datalen1, datalen2;

// lock the first half-buffer if index is 1, or second if 0
if (!buf->Lock(notify[1].dwOffset * !index, notify[1].dwOffset,
&pData1, &datalen1, &pData2, &datalen2, 0))
{
DispatchSamples(pData1, datalen1/2);
DispatchSamples(pData2, datalen2/2);
buf->Unlock(pData1, datalen1, pData2, datalen2);
}
}
}
buf->Stop();
}
CloseHandle(notify[0].hEventNotify);
CloseHandle(notify[1].hEventNotify);
}
buf->Release();
}
cap->Release();
}
}


sub DispatchSamples(word *pwSamples, int count)
{
if (pwSamples)
{
for (int a=0; a<count; a++)
{
InterruptDispatch(INTERRUPT_SAMPLE, *pwSamples[a]);
}
}
}


// wSample is valid only is type==INTERRUPT_SAMPLE
sub InterruptDispatch(INTERRUPT_TYPE type, word wSample)
{
static int nTimerCounter; // software timer
static int nPrevState;   // recent sample digital value
static int nDecoderState;
static int nBitsReceived;
static int nBitsBuffer;   // buffer to collect bit

if (type == INTERRUPT_SAMPLE)
{
// check if the timer is ON
if (nTimerCounter)
{
nTimerCounter--;
if (!nTimerCounter)
{
InterruptDispatch(INTERRUPT_TIMER, 0);
}
}
// for every audio sample...
int state = nPrevState;

// simple Schmitt trigger
#define LATH1 0 + 16
#define LATH2 0 - 16
if (wSample > LATH1) state = 1;
if (wSample < LATH2) state = 0;

// detect falling and a rising edge
if (nPrevState != state)
{
switch (nDecoderState)
{
case STATE_IDLE:
// data stream detected
nDecoderState = STATE_WAIT_075;
nBitsReceived = 0;
nTimerCounter = GetTimerInterval(0.75);

case STATE_WAIT_NEXT_BIT:
// data stream continues
nDecoderState = STATE_WAIT_075;
nTimerCounter = GetTimerInterval(0.75);
}
}
// save the current sample value
nPrevState = state;
}
if (type == INTERRUPT_TIMER)
{
switch (nDecoderState)
{
case STATE_WAIT_075:
// we are at 25% of (next) bit period
nDecoderState = STATE_WAIT_NEXT_BIT;
// append current sample to buffer
nBitsBuffer = nBitsBuffer<<1 | !nPrevState;
nBitsReceived++;

if (nBitsReceived == 14)
{
nBitsBuffer = nBitsBuffer >> 1;    // remove the last received bit
int address = (nBitsBuffer>>6)&31; // RC5DEVICE
int command = nBitsBuffer&63;      // RC5COMMAND
printf("Addres: %2d  command: %2d\n", address, command);
ExecCommand(address, command);
nDecoderState = STATE_IDLE;
}
else
{
// wait for next bit, but not longer than 1.778ms * 0.5
nTimerCounter = GetTimerInterval(0.50);
}

case STATE_WAIT_NEXT_BIT:
// timeout while waiting for bit change
nDecoderState = STATE_IDLE;
}
}
}

// returns number of interrupts to measure given percentage of 1.778ms.
// Interrupt is called every 22.6ÂÃ,µs, so for 1.778ms we need 78 interrupts
sub GetTimerInterval(float percentage),int
{
return (0.001778 * percentage) / (1.0 / 44100.0);
}

// devices defined by Philips rc5
enum RC5DEVICE
{
RC5DEVICE_TV1 = 0,
RC5DEVICE_TV2,
RC5DEVICE_VIDEOTEXT,
RC5DEVICE_TVEXPANSION,
RC5DEVICE_LASERVIDEOPLAYER,
RC5DEVICE_VCR1,
RC5DEVICE_VCR2,
RC5DEVICE_SAT1 = 8,
RC5DEVICE_VCREXPANSION,
RC5DEVICE_SAT2,
RC5DEVICE_CDVIDEO = 12,
RC5DEVICE_CDPHOTO = 14,
RC5DEVICE_AUDIOPREAMP1 = 16,
RC5DEVICE_TUNER,
RC5DEVICE_TAPE,
RC5DEVICE_AUDIOPREAMP2,
RC5DEVICE_CD,
RC5DEVICE_AUDIORACK,
RC5DEVICE_AUDIOSAT,
RC5DEVICE_DCC,
RC5DEVICE_WRITABLECD = 26
}

// commands defined by Philips rc5
enum RC5COMMAND
{
RC5COMMAND_KEY0 = 0,
RC5COMMAND_KEY1,
RC5COMMAND_KEY2,
RC5COMMAND_KEY3,
RC5COMMAND_KEY4,
RC5COMMAND_KEY5,
RC5COMMAND_KEY6,
RC5COMMAND_KEY7,
RC5COMMAND_KEY8,
RC5COMMAND_KEY9,
RC5COMMAND_KEY11,
RC5COMMAND_STANDBY = 12,
RC5COMMAND_MUTE = 13,
RC5COMMAND_PRESETS = 14,
RC5COMMAND_VOLUMEUP = 16,
RC5COMMAND_VOLUMEDOWN = 17,
RC5COMMAND_BRIGHTNESSUP = 18,
RC5COMMAND_BRIGHTNESSDOWN = 19,
RC5COMMAND_SATURATIONUP = 20,
RC5COMMAND_SATURATIONDOWN = 21,
RC5COMMAND_BASSUP = 22,
RC5COMMAND_BASSDOWN = 23,
RC5COMMAND_TREBLEUP = 24,
RC5COMMAND_TREBLEDOWN = 25,
RC5COMMAND_BALANCERIGHT = 26,
RC5COMMAND_BALANCELEFT = 27,
RC5COMMAND_CONTRASTDOWN = 28,
RC5COMMAND_CONTRASTUP = 29,
RC5COMMAND_KEY12 = 40,
RC5COMMAND_PAUSE = 48,
RC5COMMAND_FASTREVERSE = 50,
RC5COMMAND_FASTFORWARD = 52,
RC5COMMAND_PLAY = 53,
RC5COMMAND_STOP = 54,
RC5COMMAND_RECORD = 55,
RC5COMMAND_CHANNELDOWN = 56,
RC5COMMAND_CHANNELUP = 57,
RC5COMMAND_SYSTEMSELECT = 63
}


sub ExecCommand(int address,int command)
{
static int g_hwndWinamp;
if (!g_hwndWinamp || !IsWindow(g_hwndWinamp))
{
g_hwndWinamp = FindWindow("Winamp v1.x",NULL);
}

if (g_hwndWinamp)
{
WPARAM wParam = 0;
switch (command)
{
case RC5COMMAND_STANDBY:
wParam = 40001;
//case RC5COMMAND_MUTE: // mute
case RC5COMMAND_VOLUMEUP:
wParam = 40058;
case RC5COMMAND_VOLUMEDOWN:
wParam = 40059;
case RC5COMMAND_CHANNELUP: // next song
wParam = 40048;
case RC5COMMAND_CHANNELDOWN: // previous song
wParam = 40044;
case RC5COMMAND_FASTREVERSE:
wParam = 40144;
case RC5COMMAND_FASTFORWARD:
wParam = 40148;
case RC5COMMAND_PLAY:
wParam = 40045;
case RC5COMMAND_STOP:
wParam = 40047;
case RC5COMMAND_PAUSE:
wParam = 40046;
}

if (wParam) SendMessage(g_hwndWinamp, WM_COMMAND, wParam, 0);
}
else if (command == RC5COMMAND_STANDBY)
{
// launch winamp
keybd_event(VK_LAUNCH_MEDIA_SELECT, 0, 0, 0);
keybd_event(VK_LAUNCH_MEDIA_SELECT, 0, KEYEVENTF_KEYUP, 0);
}
}