[patch] Send IR with hw_audio

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 | Next >

[patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

The attached patch add send support to hw_audio.

An 18 kHz carrier is generated in antiphase on the left and right
outputs, when rectified this makes a 36 kHz carrier wave.
The simplest way of rectifying would be to connect two IR leds
antiparallel to the left and right outputs (hence the antiphase, gives
more voltage, gnd is not used).

This works for me with a philips RC19002 at a distance of about 4 meters
when directly aiming.

Regards,

Bob.

Index: configure.ac
===================================================================
RCS file: /cvsroot/lirc/lirc/configure.ac,v
retrieving revision 1.32
diff -u -r1.32 configure.ac
--- configure.ac 13 Sep 2009 10:49:09 -0000 1.32
+++ configure.ac 28 Oct 2009 23:32:46 -0000
@@ -510,7 +510,7 @@
  CFLAGS="$CFLAGS `${LIBUSB_CONFIG} --cflags`"
  ;;
  audio)
- hw_module="${hw_module} hw_audio.o receive.o"
+ hw_module="${hw_module} hw_audio.o transmit.o receive.o"
  portaudio_lib="-lportaudio ${portaudio_lib_other}"
  ;;
  audio_alsa)
@@ -777,7 +777,7 @@
 
 if test "$driver" = "audio"; then
   lirc_driver="audio"
-  hw_module="hw_audio.o receive.o"
+  hw_module="hw_audio.o transmit.o receive.o"
   HW_DEFAULT="hw_audio"
   portaudio_lib="-lportaudio ${portaudio_lib_other}"
 fi
Index: ./daemons/hw_audio.c
===================================================================
RCS file: /cvsroot/lirc/lirc/daemons/hw_audio.c,v
retrieving revision 5.6
diff -u -r5.6 hw_audio.c
--- ./daemons/hw_audio.c 28 Oct 2009 19:20:41 -0000 5.6
+++ ./daemons/hw_audio.c 28 Oct 2009 23:32:46 -0000
@@ -49,9 +49,11 @@
 /* PortAudio Includes */
 #include <portaudio.h>
 
-#define SAMPLE_RATE  (44100)
-#define NUM_CHANNELS    (2)
-#define DITHER_FLAG     (0) /**/
+#define SAMPLE_RATE  (48000)
+#define NUM_CHANNELS     (2)
+#define DITHER_FLAG      (0) /**/
+#define CARRIER      (18000)
+#define PI               (3.141592654)
 
 /* Select sample format. */
 #define PA_SAMPLE_TYPE  paUInt8
@@ -63,6 +65,10 @@
  int lastSign;
  int pulseSign;
  unsigned int lastCount;
+ double carrierPos; //position the sine generator is in
+ double remainingSignal;//length of the remaining signal is stored here when the callback exits
+ int signalPhase;    //1 means we need to send a pulse, 0 means we send a space
+ int             signaledDone;
 }
 paTestData;
 
@@ -74,7 +80,8 @@
 
 char ptyName[256];
 int master;
-
+int sendPipe[2];   //we write signals here and read them from the callback
+int signalPipe[2]; //we write a byte here when we have sent all signals
 
 int myfd = -1;
 
@@ -104,7 +111,7 @@
  int samplerate = SAMPLE_RATE;
  int diff;
 
- (void) outputBuffer; /* Prevent unused variable warnings. */
+ /* Prevent unused variable warnings. */
  (void) outTime;
 
  for ( i=0; i < framesPerBuffer; i++, myPtr++)
@@ -179,6 +186,59 @@
  if (NUM_CHANNELS == 2)
  myPtr++;
  }
+
+ //generate output
+ SAMPLE* outptr = (SAMPLE*)outputBuffer;
+ int     out;
+ double  currentSignal = data->remainingSignal;
+ lirc_t  signal;
+
+ for (i = 0; i < framesPerBuffer; i++)
+ {
+ if (currentSignal <= 0.0) //last signal we sent went out
+ {
+ if (read(sendPipe[0], &signal, sizeof(signal)) > 0) //try to read a new signal, non blocking
+ {
+ currentSignal += signal; //when a new signal is read, add it
+ data->signalPhase = data->signalPhase ? 0 : 1; //invert the phase
+ data->signaledDone = 0;
+ }
+ else if (!data->signaledDone) //signal that we have written all signals
+ {
+ data->signaledDone = 1;
+ char done = 0;
+ write(signalPipe[1], &done, sizeof(done));
+ }
+ }
+
+ if (currentSignal > 0.0)
+ {
+ if (data->signalPhase) //write carrier
+ out = rint(sin(data->carrierPos / (180.0 / PI)) * 127.0 + 128.0);
+ else
+ out = 128;
+
+ *outptr++ = out;
+ *outptr++ = 256 - out;
+
+ currentSignal -= 1000000.0 / SAMPLE_RATE;
+ }
+ else
+ {
+ *outptr++ = 128;
+ *outptr++ = 128;
+ }
+
+ //increase carrier position
+ data->carrierPos += (double)CARRIER / SAMPLE_RATE * 360.0;
+
+ if (data->carrierPos >= 360.0)
+ data->carrierPos -= 360.0;
+ }
+
+ //save how much we still have to write
+ data->remainingSignal = currentSignal;
+
  return 0;
 }
 
@@ -209,6 +269,36 @@
  return(data);
 }
 
+static int audio_send(struct ir_remote *remote,struct ir_ncode *code)
+{
+ int length;
+ lirc_t *signals;
+
+ if(!init_send(remote, code))
+ return 0;
+
+ length = send_buffer.wptr;
+ signals = send_buffer.data;
+
+ if (length <= 0 || signals == NULL)
+ {
+ LOGPRINTF(1, "nothing to send");
+ return 0;
+ }
+
+ if (write(sendPipe[1], signals, length * sizeof(lirc_t)) == -1)
+ {
+ logprintf(LOG_ERR,"write failed");
+ logperror(LOG_ERR,"write()");
+ return 0;
+ }
+
+ //wait for the callback to signal us that all signals are written
+ char done;
+ read(signalPipe[0], &done, sizeof(done));
+
+ return 1;
+}
 
 /*
   interface functions
@@ -219,6 +309,7 @@
 {
 
  PaStreamParameters inputParameters;
+ PaStreamParameters outputParameters;
  PaError    err;
  int flags;
  struct termios t;
@@ -237,6 +328,10 @@
  data.lastSign = 0;
  data.lastCount = 0;
  data.pulseSign = 0;
+ data.carrierPos = 0.0;
+ data.remainingSignal = 0.0;
+ data.signalPhase = 0;
+ data.signaledDone = 1;
 
  err = Pa_Initialize();
  if( err != paNoError ) goto error;
@@ -248,17 +343,27 @@
  }
  inputParameters.channelCount = NUM_CHANNELS; /* stereo input */
  inputParameters.sampleFormat = PA_SAMPLE_TYPE;
- inputParameters.suggestedLatency =
+ inputParameters.suggestedLatency =
  Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  inputParameters.hostApiSpecificStreamInfo = NULL;
 
+ outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+ if (outputParameters.device == paNoDevice) {
+ logprintf(LOG_ERR, "No default output device");
+ goto error;
+ }
+ outputParameters.channelCount = NUM_CHANNELS; /* stereo output */
+ outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+ outputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
 
  // Record some audio. --------------------------------------------
  err = Pa_OpenStream
  (
  &stream,
  &inputParameters,
- NULL,  // output parameters
+ &outputParameters,
  SAMPLE_RATE,
  512,             // frames per buffer
  0,  // flags
@@ -299,6 +404,25 @@
 
  hw.fd=ptyfd;
 
+ //make a pipe for sending signals to the callback
+ if (pipe(sendPipe) == -1 || pipe(signalPipe) == -1)
+ {
+ logprintf(LOG_ERR,"pipe failed");
+ logperror(LOG_ERR,"pipe()");
+ }
+
+ //make the readable end non-blocking
+ flags = fcntl(sendPipe[0], F_GETFL, 0);
+ if(flags != -1)
+ {
+ fcntl(sendPipe[0], F_SETFL, flags | O_NONBLOCK);
+ }
+ else
+ {
+ logprintf(LOG_ERR,"fcntl failed");
+ logperror(LOG_ERR,"fcntl()");
+ }
+
  err = Pa_StartStream( stream );
  if( err != paNoError ) goto error;
 
@@ -332,6 +456,11 @@
  close(master);
  close(ptyfd);
 
+ close(sendPipe[0]);
+ close(sendPipe[1]);
+ close(signalPipe[0]);
+ close(signalPipe[1]);
+
  return 1;
 
  error:
@@ -353,14 +482,14 @@
 {
  "pty",    /* simple device */
  -1,                 /* fd */
- LIRC_CAN_REC_MODE2, /* features */
- 0,                  /* send_mode */
+ LIRC_CAN_REC_MODE2 | LIRC_CAN_SEND_PULSE, /* features */
+ LIRC_MODE_PULSE,    /* send_mode */
  LIRC_MODE_MODE2,    /* rec_mode */
  0,                  /* code_length */
  audio_init,         /* init_func */
  NULL,    /* config_func */
  audio_deinit,       /* deinit_func */
- NULL,    /* send_func */
+ audio_send,      /* send_func */
  audio_rec,          /* rec_func */
  receive_decode,     /* decode_func */
  NULL,               /* ioctl_func */

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
> The attached patch add send support to hw_audio.
>
> An 18 kHz carrier is generated in antiphase on the left and right
> outputs, when rectified this makes a 36 kHz carrier wave.
> The simplest way of rectifying would be to connect two IR leds
> antiparallel to the left and right outputs (hence the antiphase, gives
> more voltage, gnd is not used).

Can you put together some website/documentation to be included with LIRC  
with some schematics? Otherwise hardly anyone will be able to take  
advantage of your code.

Christoph

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
  
The attached patch add send support to hw_audio.

An 18 kHz carrier is generated in antiphase on the left and right
outputs, when rectified this makes a 36 kHz carrier wave.
The simplest way of rectifying would be to connect two IR leds
antiparallel to the left and right outputs (hence the antiphase, gives
more voltage, gnd is not used).
    

Can you put together some website/documentation to be included with LIRC  
with some schematics? Otherwise hardly anyone will be able to take  
advantage of your code.

Christoph
  
Will do.

I have attached my latest patch which addresses two issues:

- Input needs to be ignored when transmitting, with this patch it ignores one second of input since the latest transmit.
This could be improved by using the input and output latency, but it seems to be a bit complicated.

- The full buffer needs to be played out before shutting down portaudio.
Although portaudio should guarantee this it doesn't seem to work, a simple sleep the length of the output latency fixed that.

Bob.

Index: configure.ac
===================================================================
RCS file: /cvsroot/lirc/lirc/configure.ac,v
retrieving revision 1.32
diff -u -r1.32 configure.ac
--- configure.ac 13 Sep 2009 10:49:09 -0000 1.32
+++ configure.ac 29 Oct 2009 19:54:38 -0000
@@ -510,7 +510,7 @@
  CFLAGS="$CFLAGS `${LIBUSB_CONFIG} --cflags`"
  ;;
  audio)
- hw_module="${hw_module} hw_audio.o receive.o"
+ hw_module="${hw_module} hw_audio.o transmit.o receive.o"
  portaudio_lib="-lportaudio ${portaudio_lib_other}"
  ;;
  audio_alsa)
@@ -777,7 +777,7 @@
 
 if test "$driver" = "audio"; then
   lirc_driver="audio"
-  hw_module="hw_audio.o receive.o"
+  hw_module="hw_audio.o transmit.o receive.o"
   HW_DEFAULT="hw_audio"
   portaudio_lib="-lportaudio ${portaudio_lib_other}"
 fi
Index: ./daemons/hw_audio.c
===================================================================
RCS file: /cvsroot/lirc/lirc/daemons/hw_audio.c,v
retrieving revision 5.6
diff -u -r5.6 hw_audio.c
--- ./daemons/hw_audio.c 28 Oct 2009 19:20:41 -0000 5.6
+++ ./daemons/hw_audio.c 29 Oct 2009 19:54:38 -0000
@@ -49,9 +49,11 @@
 /* PortAudio Includes */
 #include <portaudio.h>
 
-#define SAMPLE_RATE  (44100)
-#define NUM_CHANNELS    (2)
-#define DITHER_FLAG     (0) /**/
+#define SAMPLE_RATE  (48000)
+#define NUM_CHANNELS     (2)
+#define DITHER_FLAG      (0) /**/
+#define CARRIER      (18000)
+#define PI               (3.141592654)
 
 /* Select sample format. */
 #define PA_SAMPLE_TYPE  paUInt8
@@ -63,6 +65,11 @@
  int lastSign;
  int pulseSign;
  unsigned int lastCount;
+ double carrierPos; //position the sine generator is in
+ double remainingSignal;//length of the remaining signal is stored here when the callback exits
+ int signalPhase;    //1 means we need to send a pulse, 0 means we send a space
+ int             signaledDone;
+ int samplesToIgnore;
 }
 paTestData;
 
@@ -74,7 +81,9 @@
 
 char ptyName[256];
 int master;
-
+int sendPipe[2];      //we write signals here and read them from the callback
+int completedPipe[2]; //we write a byte here when we have sent all signals
+int outputLatency;
 
 int myfd = -1;
 
@@ -96,7 +105,7 @@
 {
  paTestData *data = (paTestData*)userData;
  SAMPLE *rptr = (SAMPLE*)inputBuffer;
- long i;
+ long i, j;
 
  SAMPLE *myPtr = rptr;
 
@@ -104,11 +113,18 @@
  int samplerate = SAMPLE_RATE;
  int diff;
 
- (void) outputBuffer; /* Prevent unused variable warnings. */
+ /* Prevent unused variable warnings. */
  (void) outTime;
 
  for ( i=0; i < framesPerBuffer; i++, myPtr++)
  {
+ //check if we have to ignore this sample
+ if (data->samplesToIgnore)
+ {
+ *myPtr = 128;
+ data->samplesToIgnore--;
+ }
+
  /* New Algo */
  diff = abs(data->lastFrames[0] - *myPtr);
  if ( diff > 100)
@@ -179,6 +195,65 @@
  if (NUM_CHANNELS == 2)
  myPtr++;
  }
+
+ //generate output
+ SAMPLE* outptr = (SAMPLE*)outputBuffer;
+ int     out;
+ double  currentSignal = data->remainingSignal;
+ lirc_t  signal;
+
+ for (i = 0; i < framesPerBuffer; i++)
+ {
+ if (currentSignal <= 0.0) //last signal we sent went out
+ {
+ if (read(sendPipe[0], &signal, sizeof(signal)) > 0) //try to read a new signal, non blocking
+ {
+ currentSignal += signal; //when a new signal is read, add it
+ data->signalPhase = data->signalPhase ? 0 : 1; //invert the phase
+ data->signaledDone = 0;
+
+ //when transmitting, ignore input samples for one second
+ data->samplesToIgnore = SAMPLE_RATE;
+ }
+ else if (!data->signaledDone) //signal that we have written all signals
+ {
+ data->signaledDone = 1;
+ char done = 0;
+ write(completedPipe[1], &done, sizeof(done));
+ }
+ }
+
+ if (currentSignal > 0.0)
+ {
+ if (data->signalPhase) //write carrier
+ out = rint(sin(data->carrierPos / (180.0 / PI)) * 127.0 + 128.0);
+ else
+ out = 128;
+
+ //one channel is inverted, so both channels can be used to double the voltage
+ *outptr++ = out;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 256 - out;
+
+ currentSignal -= 1000000.0 / SAMPLE_RATE;
+ }
+ else
+ {
+ *outptr++ = 128;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 128;
+ }
+
+ //increase carrier position
+ data->carrierPos += (double)CARRIER / SAMPLE_RATE * 360.0;
+
+ if (data->carrierPos >= 360.0)
+ data->carrierPos -= 360.0;
+ }
+
+ //save how much we still have to write
+ data->remainingSignal = currentSignal;
+
  return 0;
 }
 
@@ -209,6 +284,48 @@
  return(data);
 }
 
+static int audio_send(struct ir_remote *remote,struct ir_ncode *code)
+{
+ int length;
+ lirc_t *signals;
+ int flags;
+
+ if(!init_send(remote, code))
+ return 0;
+
+ length = send_buffer.wptr;
+ signals = send_buffer.data;
+
+ if (length <= 0 || signals == NULL)
+ {
+ LOGPRINTF(1, "nothing to send");
+ return 0;
+ }
+
+ //set signal pipe to non blocking
+ flags = fcntl(completedPipe[0], F_GETFL, 0);
+ fcntl(completedPipe[0], F_SETFL, flags | O_NONBLOCK);
+
+ //remove any unwanted completed bytes
+ char completed;
+ while (read(completedPipe[0], &completed, sizeof(completed)) == 1);
+
+ //set signal pipe to blocking
+ fcntl(completedPipe[0], F_SETFL, flags & ~O_NONBLOCK);
+
+ //write signals to sendpipe
+ if (write(sendPipe[1], signals, length * sizeof(lirc_t)) == -1)
+ {
+ logprintf(LOG_ERR,"write failed");
+ logperror(LOG_ERR,"write()");
+ return 0;
+ }
+
+ //wait for the callback to signal us that all signals are written
+ read(completedPipe[0], &completed, sizeof(completed));
+
+ return 1;
+}
 
 /*
   interface functions
@@ -219,6 +336,7 @@
 {
 
  PaStreamParameters inputParameters;
+ PaStreamParameters outputParameters;
  PaError    err;
  int flags;
  struct termios t;
@@ -237,6 +355,11 @@
  data.lastSign = 0;
  data.lastCount = 0;
  data.pulseSign = 0;
+ data.carrierPos = 0.0;
+ data.remainingSignal = 0.0;
+ data.signalPhase = 0;
+ data.signaledDone = 1;
+ data.samplesToIgnore = 0;
 
  err = Pa_Initialize();
  if( err != paNoError ) goto error;
@@ -252,16 +375,28 @@
  Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  inputParameters.hostApiSpecificStreamInfo = NULL;
 
+ outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+ if (outputParameters.device == paNoDevice) {
+ logprintf(LOG_ERR, "No default output device");
+ goto error;
+ }
+ outputParameters.channelCount = NUM_CHANNELS; /* stereo output */
+ outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+ outputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ outputLatency = outputParameters.suggestedLatency * 1000000;
 
  // Record some audio. --------------------------------------------
  err = Pa_OpenStream
  (
  &stream,
  &inputParameters,
- NULL,  // output parameters
+ &outputParameters,
  SAMPLE_RATE,
  512,             // frames per buffer
- 0,  // flags
+ paPrimeOutputBuffersUsingStreamCallback,  // flags
  recordCallback,
  &data );
 
@@ -299,6 +434,25 @@
 
  hw.fd=ptyfd;
 
+ //make a pipe for sending signals to the callback
+ if (pipe(sendPipe) == -1 || pipe(completedPipe) == -1)
+ {
+ logprintf(LOG_ERR,"pipe failed");
+ logperror(LOG_ERR,"pipe()");
+ }
+
+ //make the readable end non-blocking
+ flags = fcntl(sendPipe[0], F_GETFL, 0);
+ if(flags != -1)
+ {
+ fcntl(sendPipe[0], F_SETFL, flags | O_NONBLOCK);
+ }
+ else
+ {
+ logprintf(LOG_ERR,"fcntl failed");
+ logperror(LOG_ERR,"fcntl()");
+ }
+
  err = Pa_StartStream( stream );
  if( err != paNoError ) goto error;
 
@@ -319,6 +473,11 @@
 
  LOGPRINTF(1,"hw_audio_deinit()");
 
+ //make absolutely sure the full output buffer has played out
+ //even though portaudio should wait for it, it doesn't always happen
+ sleep(outputLatency / 1000000);
+ usleep(outputLatency % 1000000);
+
  // close port audio
  err = Pa_CloseStream( stream );
  if( err != paNoError ) goto error;
@@ -332,6 +491,11 @@
  close(master);
  close(ptyfd);
 
+ close(sendPipe[0]);
+ close(sendPipe[1]);
+ close(completedPipe[0]);
+ close(completedPipe[1]);
+
  return 1;
 
  error:
@@ -353,14 +517,14 @@
 {
  "pty",    /* simple device */
  -1,                 /* fd */
- LIRC_CAN_REC_MODE2, /* features */
- 0,                  /* send_mode */
+ LIRC_CAN_REC_MODE2 | LIRC_CAN_SEND_PULSE, /* features */
+ LIRC_MODE_PULSE,    /* send_mode */
  LIRC_MODE_MODE2,    /* rec_mode */
  0,                  /* code_length */
  audio_init,         /* init_func */
  NULL,    /* config_func */
  audio_deinit,       /* deinit_func */
- NULL,    /* send_func */
+ audio_send,      /* send_func */
  audio_rec,          /* rec_func */
  receive_decode,     /* decode_func */
  NULL,               /* ioctl_func */

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]
> I have attached my latest patch which addresses two issues:

Please use C-style comments and put variable declarations at the beginning  
of blocks. Older compilers won't compile this code.

Christoph

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
I have attached my latest patch which addresses two issues:
    

Please use C-style comments and put variable declarations at the beginning  
of blocks. Older compilers won't compile this code.

Christoph
Latest patch and documentation.

Bob.

Index: configure.ac
===================================================================
RCS file: /cvsroot/lirc/lirc/configure.ac,v
retrieving revision 1.32
diff -u -r1.32 configure.ac
--- configure.ac 13 Sep 2009 10:49:09 -0000 1.32
+++ configure.ac 30 Oct 2009 19:38:38 -0000
@@ -510,7 +510,7 @@
  CFLAGS="$CFLAGS `${LIBUSB_CONFIG} --cflags`"
  ;;
  audio)
- hw_module="${hw_module} hw_audio.o receive.o"
+ hw_module="${hw_module} hw_audio.o transmit.o receive.o"
  portaudio_lib="-lportaudio ${portaudio_lib_other}"
  ;;
  audio_alsa)
@@ -777,7 +777,7 @@
 
 if test "$driver" = "audio"; then
   lirc_driver="audio"
-  hw_module="hw_audio.o receive.o"
+  hw_module="hw_audio.o transmit.o receive.o"
   HW_DEFAULT="hw_audio"
   portaudio_lib="-lportaudio ${portaudio_lib_other}"
 fi
Index: ./daemons/hw_audio.c
===================================================================
RCS file: /cvsroot/lirc/lirc/daemons/hw_audio.c,v
retrieving revision 5.6
diff -u -r5.6 hw_audio.c
--- ./daemons/hw_audio.c 28 Oct 2009 19:20:41 -0000 5.6
+++ ./daemons/hw_audio.c 30 Oct 2009 19:38:38 -0000
@@ -49,9 +49,11 @@
 /* PortAudio Includes */
 #include <portaudio.h>
 
-#define SAMPLE_RATE  (44100)
-#define NUM_CHANNELS    (2)
-#define DITHER_FLAG     (0) /**/
+#define SAMPLE_RATE  (48000)
+#define NUM_CHANNELS     (2)
+#define DITHER_FLAG      (0) /**/
+#define CARRIER      (18000)
+#define PI               (3.141592654)
 
 /* Select sample format. */
 #define PA_SAMPLE_TYPE  paUInt8
@@ -63,6 +65,11 @@
  int lastSign;
  int pulseSign;
  unsigned int lastCount;
+ double carrierPos; /* position the sine generator is in */
+ double remainingSignal; /* length of the remaining signal is stored here when the callback exits */
+ int signalPhase;     /* 1 = pulse, 0 = space */
+ int             signaledDone;
+ int samplesToIgnore;
 }
 paTestData;
 
@@ -74,7 +81,9 @@
 
 char ptyName[256];
 int master;
-
+int sendPipe[2];      /* signals are written from audio_send and read from the callback */
+int completedPipe[2]; /* a byte is written here when the callback has processed all signals */
+int outputLatency;
 
 int myfd = -1;
 
@@ -96,7 +105,7 @@
 {
  paTestData *data = (paTestData*)userData;
  SAMPLE *rptr = (SAMPLE*)inputBuffer;
- long i;
+ long i, j;
 
  SAMPLE *myPtr = rptr;
 
@@ -104,11 +113,23 @@
  int samplerate = SAMPLE_RATE;
  int diff;
 
- (void) outputBuffer; /* Prevent unused variable warnings. */
+ SAMPLE* outptr = (SAMPLE*)outputBuffer;
+ int     out;
+ double  currentSignal = data->remainingSignal;
+ lirc_t  signal;
+
+ /* Prevent unused variable warnings. */
  (void) outTime;
 
  for ( i=0; i < framesPerBuffer; i++, myPtr++)
  {
+ /* check if we have to ignore this sample */
+ if (data->samplesToIgnore)
+ {
+ *myPtr = 128;
+ data->samplesToIgnore--;
+ }
+
  /* New Algo */
  diff = abs(data->lastFrames[0] - *myPtr);
  if ( diff > 100)
@@ -179,6 +200,65 @@
  if (NUM_CHANNELS == 2)
  myPtr++;
  }
+
+ /* generate output */
+ for (i = 0; i < framesPerBuffer; i++)
+ {
+ if (currentSignal <= 0.0) /* last signal we sent went out */
+ {
+ if (read(sendPipe[0], &signal, sizeof(signal)) > 0) /* try to read a new signal, non blocking */
+ {
+ currentSignal += signal; /* when a new signal is read, add it */
+ data->signalPhase = data->signalPhase ? 0 : 1; /* invert the phase */
+ data->signaledDone = 0;
+
+ /* when transmitting, ignore input samples for one second */
+ data->samplesToIgnore = SAMPLE_RATE;
+ }
+ else
+ {
+ data->signalPhase = 0; /* no more signals, reset phase */
+ if (!data->signaledDone) /* signal that we have written all signals */
+ {
+ char done = 0;
+ data->signaledDone = 1;
+ write(completedPipe[1], &done, sizeof(done));
+ }
+ }
+ }
+
+ if (currentSignal > 0.0)
+ {
+ if (data->signalPhase) /* write carrier */
+ out = rint(sin(data->carrierPos / (180.0 / PI)) * 127.0 + 128.0);
+ else
+ out = 128;
+
+ /* one channel is inverted, so both channels can be used to double the voltage */
+ *outptr++ = out;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 256 - out;
+
+ /* subtract how much of the current signal was sent */
+ currentSignal -= 1000000.0 / SAMPLE_RATE;
+ }
+ else
+ {
+ *outptr++ = 128;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 128;
+ }
+
+ /* increase carrier position */
+ data->carrierPos += (double)CARRIER / SAMPLE_RATE * 360.0;
+
+ if (data->carrierPos >= 360.0)
+ data->carrierPos -= 360.0;
+ }
+
+ /* save how much we still have to write */
+ data->remainingSignal = currentSignal;
+
  return 0;
 }
 
@@ -209,6 +289,48 @@
  return(data);
 }
 
+static int audio_send(struct ir_remote *remote,struct ir_ncode *code)
+{
+ int     length;
+ lirc_t* signals;
+ int     flags;
+ char    completed;
+
+ if(!init_send(remote, code))
+ return 0;
+
+ length = send_buffer.wptr;
+ signals = send_buffer.data;
+
+ if (length <= 0 || signals == NULL)
+ {
+ LOGPRINTF(1, "nothing to send");
+ return 0;
+ }
+
+ /* set completed pipe to non blocking */
+ flags = fcntl(completedPipe[0], F_GETFL, 0);
+ fcntl(completedPipe[0], F_SETFL, flags | O_NONBLOCK);
+
+ /* remove any unwanted completed bytes */
+ while (read(completedPipe[0], &completed, sizeof(completed)) == 1);
+
+ /* set completed pipe to blocking */
+ fcntl(completedPipe[0], F_SETFL, flags & ~O_NONBLOCK);
+
+ /* write signals to sendpipe */
+ if (write(sendPipe[1], signals, length * sizeof(lirc_t)) == -1)
+ {
+ logprintf(LOG_ERR,"write failed");
+ logperror(LOG_ERR,"write()");
+ return 0;
+ }
+
+ /* wait for the callback to signal us that all signals are written */
+ read(completedPipe[0], &completed, sizeof(completed));
+
+ return 1;
+}
 
 /*
   interface functions
@@ -219,6 +341,7 @@
 {
 
  PaStreamParameters inputParameters;
+ PaStreamParameters outputParameters;
  PaError    err;
  int flags;
  struct termios t;
@@ -237,6 +360,11 @@
  data.lastSign = 0;
  data.lastCount = 0;
  data.pulseSign = 0;
+ data.carrierPos = 0.0;
+ data.remainingSignal = 0.0;
+ data.signalPhase = 0;
+ data.signaledDone = 1;
+ data.samplesToIgnore = 0;
 
  err = Pa_Initialize();
  if( err != paNoError ) goto error;
@@ -252,16 +380,28 @@
  Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  inputParameters.hostApiSpecificStreamInfo = NULL;
 
+ outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+ if (outputParameters.device == paNoDevice) {
+ logprintf(LOG_ERR, "No default output device");
+ goto error;
+ }
+ outputParameters.channelCount = NUM_CHANNELS; /* stereo output */
+ outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+ outputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ outputLatency = outputParameters.suggestedLatency * 1000000;
 
  // Record some audio. --------------------------------------------
  err = Pa_OpenStream
  (
  &stream,
  &inputParameters,
- NULL,  // output parameters
+ &outputParameters,
  SAMPLE_RATE,
  512,             // frames per buffer
- 0,  // flags
+ paPrimeOutputBuffersUsingStreamCallback,
  recordCallback,
  &data );
 
@@ -299,6 +439,26 @@
 
  hw.fd=ptyfd;
 
+ /* make a pipe for sending signals to the callback */
+ /* make a pipe for signaling from the callback that everything was sent */
+ if (pipe(sendPipe) == -1 || pipe(completedPipe) == -1)
+ {
+ logprintf(LOG_ERR,"pipe failed");
+ logperror(LOG_ERR,"pipe()");
+ }
+
+ /* make the readable end non-blocking */
+ flags = fcntl(sendPipe[0], F_GETFL, 0);
+ if(flags != -1)
+ {
+ fcntl(sendPipe[0], F_SETFL, flags | O_NONBLOCK);
+ }
+ else
+ {
+ logprintf(LOG_ERR,"fcntl failed");
+ logperror(LOG_ERR,"fcntl()");
+ }
+
  err = Pa_StartStream( stream );
  if( err != paNoError ) goto error;
 
@@ -319,6 +479,11 @@
 
  LOGPRINTF(1,"hw_audio_deinit()");
 
+ /* make absolutely sure the full output buffer has played out
+   even though portaudio should wait for it, it doesn't always happen */
+ sleep(outputLatency / 1000000);
+ usleep(outputLatency % 1000000);
+
  // close port audio
  err = Pa_CloseStream( stream );
  if( err != paNoError ) goto error;
@@ -332,6 +497,11 @@
  close(master);
  close(ptyfd);
 
+ close(sendPipe[0]);
+ close(sendPipe[1]);
+ close(completedPipe[0]);
+ close(completedPipe[1]);
+
  return 1;
 
  error:
@@ -353,14 +523,14 @@
 {
  "pty",    /* simple device */
  -1,                 /* fd */
- LIRC_CAN_REC_MODE2, /* features */
- 0,                  /* send_mode */
+ LIRC_CAN_REC_MODE2 | LIRC_CAN_SEND_PULSE, /* features */
+ LIRC_MODE_PULSE,    /* send_mode */
  LIRC_MODE_MODE2,    /* rec_mode */
  0,                  /* code_length */
  audio_init,         /* init_func */
  NULL,    /* config_func */
  audio_deinit,       /* deinit_func */
- NULL,    /* send_func */
+ audio_send,      /* send_func */
  audio_rec,          /* rec_func */
  receive_decode,     /* decode_func */
  NULL,               /* ioctl_func */

Using the audio driver for transmitting.


Pros:

Very simple circuit.
No need for a kernel module.

Cons:

Doesn't transmit very far without an amplifier (about 3 meters when directly aiming).
A reasonably good soundcard is required (cheap cards might not provide enough voltage, or might not be able to output a correct 18 kHz sine).


How it works:

The audio driver can send IR signals using a (reasonably good) soundcard and a very simple circuit.
It does this by outputting a 18 kHz sine, which after rectification becomes a 36 kHz carrier wave.
The wave is inverted on the right channel, so the left and right channels can be used to double the voltage.


The top wave is how the wave looks when it comes out the soundcard,
the bottom wave is how it looks after rectification, as you can see the frequency is doubled.

The rectification is done using the following circuit:



LED1 and LED2 are 950nm infrared leds, R1 is a 0.25 watt resistor.

Because leds are diodes, they only conduct one way.
Since the soundcard outputs a wave that goes both positive and negative, two leds are placed antiparallel,
that way infrared is emitted on both the positive and negative cycles.

R1 is used to limit the current, this presents a load to the soundcard that is roughly the same as a pair of 32 ohms headphones.
To make the transmission more powerful, you can try lowering the value of R1 (or just short it out),
but this might damage your soundcard, the leds, or both. So try at your own risk!

Another way to make the transmission more powerful is to use a small speaker amplifier (5 watts or less),
in this case a 5 watt resistor should be used for safety. The volume should be adjusted so that the amplifier outputs its full voltage without clipping.


Setting up:

Compile lirc with the audio driver (not the IR diode or alsa ones) and install it as usual.
Connect the circuit to the soundcard and set the volume to the maximum level.
Use irsend to test if it works.

Known issues:

The audio driver uses portaudio to interface with the soundcard, there seems to be a bug in some later versions
that makes portaudio hang completely, lircd becomes unresponsive and you have to kill it with killall -9 lircd.
To get around this use the portaudio stable release from December 7, 2007.



------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference

transmitter.png (1K) Download Attachment
wave.png (2K) Download Attachment

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
> Latest patch and documentation.

What's the reason for changing SAMPLE_RATE to 48000?
Unless there's a very good reason I'd like to keep the current value to  
avoid problems with soundcards that only support 44100.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Ryan Voots :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Saturday 07 November 2009 15:08:00 Christoph Bartelmus wrote:

> Hi!
>
> Bob van Loosen "bob.loosen@..." wrote:
> > Latest patch and documentation.
>
> What's the reason for changing SAMPLE_RATE to 48000?
> Unless there's a very good reason I'd like to keep the current value to
> avoid problems with soundcards that only support 44100.
>
> Christoph

While i don't know his exact motivations, it should allow for a better
approximation of the 18000 wave which should make it work more reliably.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Ryan Voots "simcop2387@..." wrote:

> On Saturday 07 November 2009 15:08:00 Christoph Bartelmus wrote:
>> Bob van Loosen "bob.loosen@..." wrote:
>>> Latest patch and documentation.
>>
>> What's the reason for changing SAMPLE_RATE to 48000?
>> Unless there's a very good reason I'd like to keep the current value to
>> avoid problems with soundcards that only support 44100.
>>
> While i don't know his exact motivations, it should allow for a better
> approximation of the 18000 wave which should make it work more reliably.

Hm, I'm not convinced that it gives a substantial improvement.
But it brings me to the next point: the carrier should not be fixed. It  
should be derived from the frequency set in the remote configuration.
Anything above the SAMPLE_RATE probably should just be limited to  
SAMPLE_RATE. And maybe carrierPos should always start with 90°. Otherwise  
you'll get no output at all if sample_rate == 2 * carrier.
The longer I think about this the more surprised I am that this really  
works.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
  
Latest patch and documentation.
    

What's the reason for changing SAMPLE_RATE to 48000?
Unless there's a very good reason I'd like to keep the current value to  
avoid problems with soundcards that only support 44100.

Christoph
A higher samplerate increases the rolloff frequency of the soundcard's internal lowpass, considering the high carrier frequency and the square carrier modulation you need all the bandwidth you can get to ensure reliable transmission.
I don't think it's much of an issue considering that almost all soundcards do 48 khz for the past 8 years or so, even the really cheap ones.

Bob.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]

>> What's the reason for changing SAMPLE_RATE to 48000?
>> Unless there's a very good reason I'd like to keep the current value to
>> avoid problems with soundcards that only support 44100.
>>
> A higher samplerate increases the rolloff frequency of the soundcard's
> internal lowpass, considering the high carrier frequency and the square
> carrier modulation you need all the bandwidth you can get to ensure
> reliable transmission.
> I don't think it's much of an issue considering that almost all
> soundcards do 48 khz for the past 8 years or so, even the really cheap ones.

Ok, can we make this configurable, the same way it is done for the  
audio_alsa driver? Then I wouldn't have any problem setting the default to  
48kHz.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
What's the reason for changing SAMPLE_RATE to 48000?
Unless there's a very good reason I'd like to keep the current value to
avoid problems with soundcards that only support 44100.

      
A higher samplerate increases the rolloff frequency of the soundcard's
internal lowpass, considering the high carrier frequency and the square
carrier modulation you need all the bandwidth you can get to ensure
reliable transmission.
I don't think it's much of an issue considering that almost all
soundcards do 48 khz for the past 8 years or so, even the really cheap ones.
    

Ok, can we make this configurable, the same way it is done for the  
audio_alsa driver? Then I wouldn't have any problem setting the default to  
48kHz.
Sure, all that's needed is to replace the SAMPLE_RATE constant with a variable that's set somewhere.

Bob.


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
What's the reason for changing SAMPLE_RATE to 48000?
Unless there's a very good reason I'd like to keep the current value to
avoid problems with soundcards that only support 44100.

      
A higher samplerate increases the rolloff frequency of the soundcard's
internal lowpass, considering the high carrier frequency and the square
carrier modulation you need all the bandwidth you can get to ensure
reliable transmission.
I don't think it's much of an issue considering that almost all
soundcards do 48 khz for the past 8 years or so, even the really cheap ones.
    

Ok, can we make this configurable, the same way it is done for the  
audio_alsa driver? Then I wouldn't have any problem setting the default to  
48kHz.

Christoph
Attached patch should do it.

Bob.

Index: configure.ac
===================================================================
RCS file: /cvsroot/lirc/lirc/configure.ac,v
retrieving revision 1.32
diff -u -r1.32 configure.ac
--- configure.ac 13 Sep 2009 10:49:09 -0000 1.32
+++ configure.ac 9 Nov 2009 17:02:40 -0000
@@ -510,7 +510,7 @@
  CFLAGS="$CFLAGS `${LIBUSB_CONFIG} --cflags`"
  ;;
  audio)
- hw_module="${hw_module} hw_audio.o receive.o"
+ hw_module="${hw_module} hw_audio.o transmit.o receive.o"
  portaudio_lib="-lportaudio ${portaudio_lib_other}"
  ;;
  audio_alsa)
@@ -777,7 +777,7 @@
 
 if test "$driver" = "audio"; then
   lirc_driver="audio"
-  hw_module="hw_audio.o receive.o"
+  hw_module="hw_audio.o transmit.o receive.o"
   HW_DEFAULT="hw_audio"
   portaudio_lib="-lportaudio ${portaudio_lib_other}"
 fi
Index: ./daemons/hw_audio.c
===================================================================
RCS file: /cvsroot/lirc/lirc/daemons/hw_audio.c,v
retrieving revision 5.6
diff -u -r5.6 hw_audio.c
--- ./daemons/hw_audio.c 28 Oct 2009 19:20:41 -0000 5.6
+++ ./daemons/hw_audio.c 9 Nov 2009 17:02:40 -0000
@@ -49,9 +49,11 @@
 /* PortAudio Includes */
 #include <portaudio.h>
 
-#define SAMPLE_RATE  (44100)
-#define NUM_CHANNELS    (2)
-#define DITHER_FLAG     (0) /**/
+#define DEFAULT_SAMPLERATE (48000)
+#define NUM_CHANNELS           (2)
+#define DITHER_FLAG            (0)
+#define CARRIER            (18000)
+#define PI                     (3.141592654)
 
 /* Select sample format. */
 #define PA_SAMPLE_TYPE  paUInt8
@@ -63,6 +65,13 @@
  int lastSign;
  int pulseSign;
  unsigned int lastCount;
+ double carrierPos; /* position the sine generator is in */
+ int carrierFreq;
+ double remainingSignal; /* length of the remaining signal is stored here when the callback exits */
+ int signalPhase;     /* 1 = pulse, 0 = space */
+ int             signaledDone;
+ int samplesToIgnore;
+ int samplerate;
 }
 paTestData;
 
@@ -74,7 +83,9 @@
 
 char ptyName[256];
 int master;
-
+int sendPipe[2];      /* signals are written from audio_send and read from the callback */
+int completedPipe[2]; /* a byte is written here when the callback has processed all signals */
+int outputLatency;
 
 int myfd = -1;
 
@@ -101,14 +112,25 @@
  SAMPLE *myPtr = rptr;
 
  unsigned int time;
- int samplerate = SAMPLE_RATE;
  int diff;
 
- (void) outputBuffer; /* Prevent unused variable warnings. */
+ SAMPLE* outptr = (SAMPLE*)outputBuffer;
+ int     out;
+ double  currentSignal = data->remainingSignal;
+ lirc_t  signal;
+
+ /* Prevent unused variable warnings. */
  (void) outTime;
 
  for ( i=0; i < framesPerBuffer; i++, myPtr++)
  {
+ /* check if we have to ignore this sample */
+ if (data->samplesToIgnore)
+ {
+ *myPtr = 128;
+ data->samplesToIgnore--;
+ }
+
  /* New Algo */
  diff = abs(data->lastFrames[0] - *myPtr);
  if ( diff > 100)
@@ -133,7 +155,7 @@
  // printf("CHANGE ++ ");
  data->lastSign = 1;
 
- time = data->lastCount * 1000000 / samplerate;
+ time = data->lastCount * 1000000 / data->samplerate;
  if (data->lastSign == data->pulseSign)
  {
  addCode( time );
@@ -152,7 +174,7 @@
  // printf("CHANGE -- ");
  data->lastSign = -1;
 
- time = data->lastCount * 1000000 / samplerate;
+ time = data->lastCount * 1000000 / data->samplerate;
  if (data->lastSign == data->pulseSign)
  {
  // printf("Pause: %d us, %d \n", time, data->lastCount);
@@ -179,6 +201,65 @@
  if (NUM_CHANNELS == 2)
  myPtr++;
  }
+
+ /* generate output */
+ for (i = 0; i < framesPerBuffer; i++)
+ {
+ if (currentSignal <= 0.0) /* last signal we sent went out */
+ {
+ if (read(sendPipe[0], &signal, sizeof(signal)) > 0) /* try to read a new signal, non blocking */
+ {
+ currentSignal += signal; /* when a new signal is read, add it */
+ data->signalPhase = data->signalPhase ? 0 : 1; /* invert the phase */
+ data->signaledDone = 0;
+
+ /* when transmitting, ignore input samples for one second */
+ data->samplesToIgnore = data->samplerate;
+ }
+ else
+ {
+ data->signalPhase = 0; /* no more signals, reset phase */
+ if (!data->signaledDone) /* signal that we have written all signals */
+ {
+ char done = 0;
+ data->signaledDone = 1;
+ write(completedPipe[1], &done, sizeof(done));
+ }
+ }
+ }
+
+ if (currentSignal > 0.0)
+ {
+ if (data->signalPhase) /* write carrier */
+ out = rint(sin(data->carrierPos / (180.0 / PI)) * 127.0 + 128.0);
+ else
+ out = 128;
+
+ /* one channel is inverted, so both channels can be used to double the voltage */
+ *outptr++ = out;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 256 - out;
+
+ /* subtract how much of the current signal was sent */
+ currentSignal -= 1000000.0 / data->samplerate;
+ }
+ else
+ {
+ *outptr++ = 128;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 128;
+ }
+
+ /* increase carrier position */
+ data->carrierPos += (double)CARRIER / data->samplerate * 360.0;
+
+ if (data->carrierPos >= 360.0)
+ data->carrierPos -= 360.0;
+ }
+
+ /* save how much we still have to write */
+ data->remainingSignal = currentSignal;
+
  return 0;
 }
 
@@ -209,6 +290,123 @@
  return(data);
 }
 
+static int audio_send(struct ir_remote *remote,struct ir_ncode *code)
+{
+ int     length;
+ lirc_t* signals;
+ int     flags;
+ char    completed;
+
+ if(!init_send(remote, code))
+ return 0;
+
+ length = send_buffer.wptr;
+ signals = send_buffer.data;
+
+ if (length <= 0 || signals == NULL)
+ {
+ LOGPRINTF(1, "nothing to send");
+ return 0;
+ }
+
+ /* set completed pipe to non blocking */
+ flags = fcntl(completedPipe[0], F_GETFL, 0);
+ fcntl(completedPipe[0], F_SETFL, flags | O_NONBLOCK);
+
+ /* remove any unwanted completed bytes */
+ while (read(completedPipe[0], &completed, sizeof(completed)) == 1);
+
+ /* set completed pipe to blocking */
+ fcntl(completedPipe[0], F_SETFL, flags & ~O_NONBLOCK);
+
+ /* write signals to sendpipe */
+ if (write(sendPipe[1], signals, length * sizeof(lirc_t)) == -1)
+ {
+ logprintf(LOG_ERR,"write failed");
+ logperror(LOG_ERR,"write()");
+ return 0;
+ }
+
+ /* wait for the callback to signal us that all signals are written */
+ read(completedPipe[0], &completed, sizeof(completed));
+
+ return 1;
+}
+
+void audio_parsedevicestr(char* api, char* device, int* rate)
+{
+ int ret;
+
+ /*empty device string means default*/
+ if (strlen(hw.device))
+ {
+ /*device string is api:device[@rate] or @rate*/
+ ret = sscanf(hw.device, "%1023[^:]:%1023[^@]@%i", api, device, rate);
+
+ if (ret == 2 || (ret == 3 && *rate <= 0))
+ *rate = DEFAULT_SAMPLERATE;
+
+ if (ret >= 2)
+ return;
+
+ /*check for @rate*/
+ if (sscanf(hw.device, "@%i", rate) == 1)
+ {
+ api[0] = 0;
+ device[0] = 0;
+ if (*rate <= 0)
+ *rate = DEFAULT_SAMPLERATE;
+
+ return;
+ }
+
+ logprintf(LOG_ERR, "Malformed device string %s, syntax is api:device[@rate] or @rate", hw.device);
+ }
+
+ api[0] = 0;
+ device[0] = 0;
+ *rate = DEFAULT_SAMPLERATE;
+}
+
+void audio_choosedevice(PaStreamParameters* streamparameters, int input, char* api, char* device)
+{
+ char* direction = input ? "input" : "output";
+
+ if (strlen(api) && strlen(device))
+  {
+ int   i;
+ int   nrdevices;
+ const PaDeviceInfo*  deviceinfo;
+ const PaHostApiInfo* hostapiinfo;
+
+ nrdevices = Pa_GetDeviceCount();
+ for (i = 0; i < nrdevices; i++)
+ {
+ deviceinfo = Pa_GetDeviceInfo(i);
+
+ /*check if device can do input or output if we need it*/
+ if ((deviceinfo->maxOutputChannels >= NUM_CHANNELS && !input) || (deviceinfo->maxInputChannels >= NUM_CHANNELS && input))
+ {
+ hostapiinfo = Pa_GetHostApiInfo(deviceinfo->hostApi);
+ if (strcmp(api, hostapiinfo->name) == 0 && strcmp(device, deviceinfo->name) == 0)
+ {
+ streamparameters->device = i;
+ logprintf(LOG_INFO, "Using %s device %i: %s:%s", direction, i, hostapiinfo->name, deviceinfo->name);
+ return;
+ }
+ }
+ }
+
+ logprintf(LOG_ERR, "Device %s %s:%s not found", direction, api, device);
+  }
+
+ logprintf(LOG_INFO, "Using default %s device", direction);
+
+ if (input)
+ streamparameters->device = Pa_GetDefaultInputDevice(); /* default input device */
+ else
+ streamparameters->device = Pa_GetDefaultOutputDevice(); /* default output device */
+}
 
 /*
   interface functions
@@ -219,9 +417,12 @@
 {
 
  PaStreamParameters inputParameters;
+ PaStreamParameters outputParameters;
  PaError    err;
  int flags;
  struct termios t;
+ char api[1024];
+ char device[1024];
 
  LOGPRINTF(1,"hw_audio_init()");
 
@@ -237,13 +438,22 @@
  data.lastSign = 0;
  data.lastCount = 0;
  data.pulseSign = 0;
+ data.carrierPos = 0.0;
+ data.remainingSignal = 0.0;
+ data.signalPhase = 0;
+ data.signaledDone = 1;
+ data.samplesToIgnore = 0;
 
  err = Pa_Initialize();
  if( err != paNoError ) goto error;
 
- inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+ audio_parsedevicestr(api, device, &data.samplerate);
+ logprintf(LOG_INFO, "Using samplerate %i", data.samplerate);
+
+ /*choose input device*/
+ audio_choosedevice(&inputParameters, 1, api, device);
  if (inputParameters.device == paNoDevice) {
- logprintf(LOG_ERR, "No default input device");
+ logprintf(LOG_ERR, "No input device found");
  goto error;
  }
  inputParameters.channelCount = NUM_CHANNELS; /* stereo input */
@@ -252,16 +462,29 @@
  Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  inputParameters.hostApiSpecificStreamInfo = NULL;
 
+ /*choose output device*/
+ audio_choosedevice(&outputParameters, 0, api, device);
+ if (outputParameters.device == paNoDevice) {
+ logprintf(LOG_ERR, "No output device found");
+ goto error;
+ }
+ outputParameters.channelCount = NUM_CHANNELS; /* stereo output */
+ outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+ outputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ outputLatency = outputParameters.suggestedLatency * 1000000;
 
  // Record some audio. --------------------------------------------
  err = Pa_OpenStream
  (
  &stream,
  &inputParameters,
- NULL,  // output parameters
- SAMPLE_RATE,
+ &outputParameters,
+ data.samplerate,
  512,             // frames per buffer
- 0,  // flags
+ paPrimeOutputBuffersUsingStreamCallback,
  recordCallback,
  &data );
 
@@ -299,6 +522,26 @@
 
  hw.fd=ptyfd;
 
+ /* make a pipe for sending signals to the callback */
+ /* make a pipe for signaling from the callback that everything was sent */
+ if (pipe(sendPipe) == -1 || pipe(completedPipe) == -1)
+ {
+ logprintf(LOG_ERR,"pipe failed");
+ logperror(LOG_ERR,"pipe()");
+ }
+
+ /* make the readable end non-blocking */
+ flags = fcntl(sendPipe[0], F_GETFL, 0);
+ if(flags != -1)
+ {
+ fcntl(sendPipe[0], F_SETFL, flags | O_NONBLOCK);
+ }
+ else
+ {
+ logprintf(LOG_ERR,"fcntl failed");
+ logperror(LOG_ERR,"fcntl()");
+ }
+
  err = Pa_StartStream( stream );
  if( err != paNoError ) goto error;
 
@@ -319,6 +562,11 @@
 
  LOGPRINTF(1,"hw_audio_deinit()");
 
+ /* make absolutely sure the full output buffer has played out
+   even though portaudio should wait for it, it doesn't always happen */
+ sleep(outputLatency / 1000000);
+ usleep(outputLatency % 1000000);
+
  // close port audio
  err = Pa_CloseStream( stream );
  if( err != paNoError ) goto error;
@@ -332,6 +580,11 @@
  close(master);
  close(ptyfd);
 
+ close(sendPipe[0]);
+ close(sendPipe[1]);
+ close(completedPipe[0]);
+ close(completedPipe[1]);
+
  return 1;
 
  error:
@@ -351,16 +604,16 @@
 
 struct hardware hw_audio=
 {
- "pty",    /* simple device */
+ "",                /* default device */
  -1,                 /* fd */
- LIRC_CAN_REC_MODE2, /* features */
- 0,                  /* send_mode */
+ LIRC_CAN_REC_MODE2 | LIRC_CAN_SEND_PULSE, /* features */
+ LIRC_MODE_PULSE,    /* send_mode */
  LIRC_MODE_MODE2,    /* rec_mode */
  0,                  /* code_length */
  audio_init,         /* init_func */
  NULL,    /* config_func */
  audio_deinit,       /* deinit_func */
- NULL,    /* send_func */
+ audio_send,      /* send_func */
  audio_rec,          /* rec_func */
  receive_decode,     /* decode_func */
  NULL,               /* ioctl_func */

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]
>> Ok, can we make this configurable, the same way it is done for the
>> audio_alsa driver? Then I wouldn't have any problem setting the default to
>> 48kHz.
>>
> Attached patch should do it.

Almost there.
I saw that you've added carrierFreq to the struct but are not using it  
yet.
In audio_send you can use something like:
data.carrierFreq = remote->freq==0 ? DEFAULT_FREQ:remote->freq;
And then of course make use of it in recordCallback.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
Ok, can we make this configurable, the same way it is done for the
audio_alsa driver? Then I wouldn't have any problem setting the default to
48kHz.

      
Attached patch should do it.
    

Almost there.
I saw that you've added carrierFreq to the struct but are not using it  
yet.
In audio_send you can use something like:
data.carrierFreq = remote->freq==0 ? DEFAULT_FREQ:remote->freq;
And then of course make use of it in recordCallback.

Christoph
  
Should be done with the pipe instead, otherwise it's not threadsafe.

Bob.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]
>> Almost there.
>> I saw that you've added carrierFreq to the struct but are not using it
>> yet.
>> In audio_send you can use something like:
>> data.carrierFreq = remote->freq==0 ? DEFAULT_FREQ:remote->freq;
>> And then of course make use of it in recordCallback.
>>
> Should be done with the pipe instead, otherwise it's not threadsafe.

Hm, you've serialized access with completedPipe already, or do I miss  
something?

Christoph


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
Should be done with the pipe instead, otherwise it's not threadsafe.
    

Hm, you've serialized access with completedPipe already, or do I miss  
something?

Christoph
That part is ok, but I'm not sure you can guarantee synchronization between the data written/read from sendPipe and the assignment of the carrier frequency.

Bob.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]

>>> Should be done with the pipe instead, otherwise it's not threadsafe.
>>>
>>
>> Hm, you've serialized access with completedPipe already, or do I miss
>> something?
>>
>> Christoph
> That part is ok, but I'm not sure you can guarantee synchronization
> between the data written/read from sendPipe and the assignment of the
> carrier frequency.

Wait, this whole thing won't work anyway. fork() creates a new process  
with its own address space. You can change any variable in the parent  
process as you like, it won't affect the child.
Shared memory seems like overkill, so maybe your first idea with using  
sendPipe to pass the carrier frequency to the child is the best option.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
That part is ok, but I'm not sure you can guarantee synchronization
between the data written/read from sendPipe and the assignment of the
carrier frequency.
    

Wait, this whole thing won't work anyway. fork() creates a new process  
with its own address space. You can change any variable in the parent  
process as you like, it won't affect the child.
Shared memory seems like overkill, so maybe your first idea with using  
sendPipe to pass the carrier frequency to the child is the best option.
Latest patch assigns carrier frequency, I also added a usleep to give portaudio some time to set up properly, otherwise the first transmission might fail.

Bob.


Index: configure.ac
===================================================================
RCS file: /cvsroot/lirc/lirc/configure.ac,v
retrieving revision 1.32
diff -u -r1.32 configure.ac
--- configure.ac 13 Sep 2009 10:49:09 -0000 1.32
+++ configure.ac 14 Nov 2009 16:20:27 -0000
@@ -510,7 +510,7 @@
  CFLAGS="$CFLAGS `${LIBUSB_CONFIG} --cflags`"
  ;;
  audio)
- hw_module="${hw_module} hw_audio.o receive.o"
+ hw_module="${hw_module} hw_audio.o transmit.o receive.o"
  portaudio_lib="-lportaudio ${portaudio_lib_other}"
  ;;
  audio_alsa)
@@ -777,7 +777,7 @@
 
 if test "$driver" = "audio"; then
   lirc_driver="audio"
-  hw_module="hw_audio.o receive.o"
+  hw_module="hw_audio.o transmit.o receive.o"
   HW_DEFAULT="hw_audio"
   portaudio_lib="-lportaudio ${portaudio_lib_other}"
 fi
Index: ./daemons/hw_audio.c
===================================================================
RCS file: /cvsroot/lirc/lirc/daemons/hw_audio.c,v
retrieving revision 5.6
diff -u -r5.6 hw_audio.c
--- ./daemons/hw_audio.c 28 Oct 2009 19:20:41 -0000 5.6
+++ ./daemons/hw_audio.c 14 Nov 2009 16:20:27 -0000
@@ -49,9 +49,11 @@
 /* PortAudio Includes */
 #include <portaudio.h>
 
-#define SAMPLE_RATE  (44100)
-#define NUM_CHANNELS    (2)
-#define DITHER_FLAG     (0) /**/
+#define DEFAULT_SAMPLERATE (48000)
+#define NUM_CHANNELS           (2)
+#define DITHER_FLAG            (0)
+#define DEFAULT_CARRIER    (36000)
+#define PI                     (3.141592654)
 
 /* Select sample format. */
 #define PA_SAMPLE_TYPE  paUInt8
@@ -63,6 +65,13 @@
  int lastSign;
  int pulseSign;
  unsigned int lastCount;
+ double carrierPos; /* position the sine generator is in */
+ lirc_t carrierFreq;
+ double remainingSignal; /* length of the remaining signal is stored here when the callback exits */
+ int signalPhase;     /* 1 = pulse, 0 = space */
+ int             signaledDone;
+ int samplesToIgnore;
+ int samplerate;
 }
 paTestData;
 
@@ -74,7 +83,9 @@
 
 char ptyName[256];
 int master;
-
+int sendPipe[2];      /* signals are written from audio_send and read from the callback */
+int completedPipe[2]; /* a byte is written here when the callback has processed all signals */
+int outputLatency;
 
 int myfd = -1;
 
@@ -88,9 +99,9 @@
 ** that could mess up the system like calling malloc() or free().
 */
 
-static int recordCallback( void *inputBuffer, void *outputBuffer,
+static int recordCallback( const void *inputBuffer, void *outputBuffer,
                            unsigned long framesPerBuffer,
-                           PaStreamCallbackTimeInfo* outTime,
+                           const PaStreamCallbackTimeInfo* outTime,
                            PaStreamCallbackFlags status,
                            void *userData )
 {
@@ -101,14 +112,25 @@
  SAMPLE *myPtr = rptr;
 
  unsigned int time;
- int samplerate = SAMPLE_RATE;
  int diff;
 
- (void) outputBuffer; /* Prevent unused variable warnings. */
+ SAMPLE* outptr = (SAMPLE*)outputBuffer;
+ int     out;
+ double  currentSignal = data->remainingSignal;
+ lirc_t  signal;
+
+ /* Prevent unused variable warnings. */
  (void) outTime;
 
  for ( i=0; i < framesPerBuffer; i++, myPtr++)
  {
+ /* check if we have to ignore this sample */
+ if (data->samplesToIgnore)
+ {
+ *myPtr = 128;
+ data->samplesToIgnore--;
+ }
+
  /* New Algo */
  diff = abs(data->lastFrames[0] - *myPtr);
  if ( diff > 100)
@@ -133,7 +155,7 @@
  // printf("CHANGE ++ ");
  data->lastSign = 1;
 
- time = data->lastCount * 1000000 / samplerate;
+ time = data->lastCount * 1000000 / data->samplerate;
  if (data->lastSign == data->pulseSign)
  {
  addCode( time );
@@ -152,7 +174,7 @@
  // printf("CHANGE -- ");
  data->lastSign = -1;
 
- time = data->lastCount * 1000000 / samplerate;
+ time = data->lastCount * 1000000 / data->samplerate;
  if (data->lastSign == data->pulseSign)
  {
  // printf("Pause: %d us, %d \n", time, data->lastCount);
@@ -179,6 +201,73 @@
  if (NUM_CHANNELS == 2)
  myPtr++;
  }
+
+ /* generate output */
+ for (i = 0; i < framesPerBuffer; i++)
+ {
+ if (currentSignal <= 0.0) /* last signal we sent went out */
+ {
+ if (read(sendPipe[0], &signal, sizeof(signal)) > 0) /* try to read a new signal, non blocking */
+ {
+ if (data->signaledDone) /*first one sent is the carrier frequency*/
+ {
+ data->carrierFreq = signal;
+ data->signaledDone = 0;
+ }
+ else
+ {
+ currentSignal += signal; /* when a new signal is read, add it */
+ data->signalPhase = data->signalPhase ? 0 : 1; /* invert the phase */
+ }
+
+ /* when transmitting, ignore input samples for one second */
+ data->samplesToIgnore = data->samplerate;
+ }
+ else
+ {
+ data->signalPhase = 0; /* no more signals, reset phase */
+ if (!data->signaledDone) /* signal that we have written all signals */
+ {
+ char done = 0;
+ data->signaledDone = 1;
+ write(completedPipe[1], &done, sizeof(done));
+ }
+ }
+ }
+
+ if (currentSignal > 0.0)
+ {
+ if (data->signalPhase) /* write carrier */
+ out = rint(sin(data->carrierPos / (180.0 / PI)) * 127.0 + 128.0);
+ else
+ out = 128;
+
+ /* one channel is inverted, so both channels can be used to double the voltage */
+ *outptr++ = out;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 256 - out;
+
+ /* subtract how much of the current signal was sent */
+ currentSignal -= 1000000.0 / data->samplerate;
+ }
+ else
+ {
+ *outptr++ = 128;
+ if (NUM_CHANNELS == 2)
+ *outptr++ = 128;
+ }
+
+ /* increase carrier position */
+ /* carrier frequency is halved */
+ data->carrierPos += (double)data->carrierFreq / data->samplerate * 360.0 / 2.0;
+
+ if (data->carrierPos >= 360.0)
+ data->carrierPos -= 360.0;
+ }
+
+ /* save how much we still have to write */
+ data->remainingSignal = currentSignal;
+
  return 0;
 }
 
@@ -209,6 +298,134 @@
  return(data);
 }
 
+static int audio_send(struct ir_remote *remote,struct ir_ncode *code)
+{
+ int     length;
+ lirc_t* signals;
+ int     flags;
+ char    completed;
+ lirc_t  freq;
+ static  lirc_t prevfreq = 0;
+
+ if(!init_send(remote, code))
+ return 0;
+
+ length = send_buffer.wptr;
+ signals = send_buffer.data;
+
+ if (length <= 0 || signals == NULL)
+ {
+ LOGPRINTF(1, "nothing to send");
+ return 0;
+ }
+
+ /* set completed pipe to non blocking */
+ flags = fcntl(completedPipe[0], F_GETFL, 0);
+ fcntl(completedPipe[0], F_SETFL, flags | O_NONBLOCK);
+
+ /* remove any unwanted completed bytes */
+ while (read(completedPipe[0], &completed, sizeof(completed)) == 1);
+
+ /* set completed pipe to blocking */
+ fcntl(completedPipe[0], F_SETFL, flags & ~O_NONBLOCK);
+
+ /*write carrier frequency*/
+ freq = remote->freq ? remote->freq : DEFAULT_CARRIER;
+ write(sendPipe[1], &freq, sizeof(freq));
+ if (freq != prevfreq)
+ {
+ prevfreq = freq;
+ logprintf(LOG_INFO, "Using carrier frequency %i", freq);
+ }
+
+ /* write signals to sendpipe */
+ if (write(sendPipe[1], signals, length * sizeof(lirc_t)) == -1)
+ {
+ logprintf(LOG_ERR,"write failed");
+ logperror(LOG_ERR,"write()");
+ return 0;
+ }
+
+ /* wait for the callback to signal us that all signals are written */
+ read(completedPipe[0], &completed, sizeof(completed));
+
+ return 1;
+}
+
+void audio_parsedevicestr(char* api, char* device, int* rate)
+{
+ int ret;
+
+ /*empty device string means default*/
+ if (strlen(hw.device))
+ {
+ /*device string is api:device[@rate] or @rate*/
+ ret = sscanf(hw.device, "%1023[^:]:%1023[^@]@%i", api, device, rate);
+
+ if (ret == 2 || (ret == 3 && *rate <= 0))
+ *rate = DEFAULT_SAMPLERATE;
+
+ if (ret >= 2)
+ return;
+
+ /*check for @rate*/
+ if (sscanf(hw.device, "@%i", rate) == 1)
+ {
+ api[0] = 0;
+ device[0] = 0;
+ if (*rate <= 0)
+ *rate = DEFAULT_SAMPLERATE;
+
+ return;
+ }
+
+ logprintf(LOG_ERR, "Malformed device string %s, syntax is api:device[@rate] or @rate", hw.device);
+ }
+
+ api[0] = 0;
+ device[0] = 0;
+ *rate = DEFAULT_SAMPLERATE;
+}
+
+void audio_choosedevice(PaStreamParameters* streamparameters, int input, char* api, char* device)
+{
+ char* direction = input ? "input" : "output";
+
+ if (strlen(api) && strlen(device))
+ {
+ int   i;
+ int   nrdevices;
+ const PaDeviceInfo*  deviceinfo;
+ const PaHostApiInfo* hostapiinfo;
+
+ nrdevices = Pa_GetDeviceCount();
+ for (i = 0; i < nrdevices; i++)
+ {
+ deviceinfo = Pa_GetDeviceInfo(i);
+
+ /*check if device can do input or output if we need it*/
+ if ((deviceinfo->maxOutputChannels >= NUM_CHANNELS && !input) || (deviceinfo->maxInputChannels >= NUM_CHANNELS && input))
+ {
+ hostapiinfo = Pa_GetHostApiInfo(deviceinfo->hostApi);
+ if (strcmp(api, hostapiinfo->name) == 0 && strcmp(device, deviceinfo->name) == 0)
+ {
+ streamparameters->device = i;
+ logprintf(LOG_INFO, "Using %s device %i: %s:%s", direction, i, hostapiinfo->name, deviceinfo->name);
+ return;
+ }
+ }
+ }
+
+ logprintf(LOG_ERR, "Device %s %s:%s not found", direction, api, device);
+ }
+
+ logprintf(LOG_INFO, "Using default %s device", direction);
+
+ if (input)
+ streamparameters->device = Pa_GetDefaultInputDevice(); /* default input device */
+ else
+ streamparameters->device = Pa_GetDefaultOutputDevice(); /* default output device */
+}
 
 /*
   interface functions
@@ -219,9 +436,12 @@
 {
 
  PaStreamParameters inputParameters;
+ PaStreamParameters outputParameters;
  PaError    err;
  int flags;
  struct termios t;
+ char api[1024];
+ char device[1024];
 
  LOGPRINTF(1,"hw_audio_init()");
 
@@ -237,13 +457,23 @@
  data.lastSign = 0;
  data.lastCount = 0;
  data.pulseSign = 0;
+ data.carrierPos = 0.0;
+ data.remainingSignal = 0.0;
+ data.signalPhase = 0;
+ data.signaledDone = 1;
+ data.samplesToIgnore = 0;
+ data.carrierFreq = DEFAULT_CARRIER;
 
  err = Pa_Initialize();
  if( err != paNoError ) goto error;
 
- inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+ audio_parsedevicestr(api, device, &data.samplerate);
+ logprintf(LOG_INFO, "Using samplerate %i", data.samplerate);
+
+ /*choose input device*/
+ audio_choosedevice(&inputParameters, 1, api, device);
  if (inputParameters.device == paNoDevice) {
- logprintf(LOG_ERR, "No default input device");
+ logprintf(LOG_ERR, "No input device found");
  goto error;
  }
  inputParameters.channelCount = NUM_CHANNELS; /* stereo input */
@@ -252,16 +482,29 @@
  Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
  inputParameters.hostApiSpecificStreamInfo = NULL;
 
+ /*choose output device*/
+ audio_choosedevice(&outputParameters, 0, api, device);
+ if (outputParameters.device == paNoDevice) {
+ logprintf(LOG_ERR, "No output device found");
+ goto error;
+ }
+ outputParameters.channelCount = NUM_CHANNELS; /* stereo output */
+ outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+ outputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ outputLatency = outputParameters.suggestedLatency * 1000000;
 
  // Record some audio. --------------------------------------------
  err = Pa_OpenStream
  (
  &stream,
  &inputParameters,
- NULL,  // output parameters
- SAMPLE_RATE,
+ &outputParameters,
+ data.samplerate,
  512,             // frames per buffer
- 0,  // flags
+ paPrimeOutputBuffersUsingStreamCallback,
  recordCallback,
  &data );
 
@@ -299,9 +542,32 @@
 
  hw.fd=ptyfd;
 
+ /* make a pipe for sending signals to the callback */
+ /* make a pipe for signaling from the callback that everything was sent */
+ if (pipe(sendPipe) == -1 || pipe(completedPipe) == -1)
+ {
+ logprintf(LOG_ERR,"pipe failed");
+ logperror(LOG_ERR,"pipe()");
+ }
+
+ /* make the readable end non-blocking */
+ flags = fcntl(sendPipe[0], F_GETFL, 0);
+ if(flags != -1)
+ {
+ fcntl(sendPipe[0], F_SETFL, flags | O_NONBLOCK);
+ }
+ else
+ {
+ logprintf(LOG_ERR,"fcntl failed");
+ logperror(LOG_ERR,"fcntl()");
+ }
+
  err = Pa_StartStream( stream );
  if( err != paNoError ) goto error;
 
+ /*wait for portaudio to settle*/
+ usleep(50000);
+
  return(1);
 
  error:
@@ -319,6 +585,11 @@
 
  LOGPRINTF(1,"hw_audio_deinit()");
 
+ /* make absolutely sure the full output buffer has played out
+   even though portaudio should wait for it, it doesn't always happen */
+ sleep(outputLatency / 1000000);
+ usleep(outputLatency % 1000000);
+
  // close port audio
  err = Pa_CloseStream( stream );
  if( err != paNoError ) goto error;
@@ -332,6 +603,11 @@
  close(master);
  close(ptyfd);
 
+ close(sendPipe[0]);
+ close(sendPipe[1]);
+ close(completedPipe[0]);
+ close(completedPipe[1]);
+
  return 1;
 
  error:
@@ -351,16 +627,16 @@
 
 struct hardware hw_audio=
 {
- "pty",    /* simple device */
+ "",                /* default device */
  -1,                 /* fd */
- LIRC_CAN_REC_MODE2, /* features */
- 0,                  /* send_mode */
+ LIRC_CAN_REC_MODE2 | LIRC_CAN_SEND_PULSE, /* features */
+ LIRC_MODE_PULSE,    /* send_mode */
  LIRC_MODE_MODE2,    /* rec_mode */
  0,                  /* code_length */
  audio_init,         /* init_func */
  NULL,    /* config_func */
  audio_deinit,       /* deinit_func */
- NULL,    /* send_func */
+ audio_send,      /* send_func */
  audio_rec,          /* rec_func */
  receive_decode,     /* decode_func */
  NULL,               /* ioctl_func */

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Re: [patch] Send IR with hw_audio

by Bob van Loosen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Bob van Loosen wrote:
Christoph Bartelmus wrote:
Hi!

Bob van Loosen bob.loosen@... wrote:
[...]
  
That part is ok, but I'm not sure you can guarantee synchronization
between the data written/read from sendPipe and the assignment of the
carrier frequency.
    

Wait, this whole thing won't work anyway. fork() creates a new process  
with its own address space. You can change any variable in the parent  
process as you like, it won't affect the child.
Shared memory seems like overkill, so maybe your first idea with using  
sendPipe to pass the carrier frequency to the child is the best option.
Latest patch assigns carrier frequency, I also added a usleep to give portaudio some time to set up properly, otherwise the first transmission might fail.

Bob.

Attached updated documentation.

Bob.

Using the audio driver for transmitting.

Pros:

Very simple circuit.
No need for a kernel module.

Cons:

Doesn't transmit very far without an amplifier (about 3 meters when directly aiming).
A reasonably good soundcard is required (cheap cards might not provide enough voltage, or might not be able to output a correct 18 kHz sine).

It takes some time to set up (50 ms or so) so when no clients are connected to lircd the first transmission will have some higher latency.
A workaround for this is to keep irw running with a bash script like this:

#!/bin/bash
while [ true ]; do
irw || true
sleep 1
done

How it works:

The audio driver can send IR signals using a (reasonably good) soundcard and a very simple circuit.
It does this by outputting a 18 kHz sine, which after rectification becomes a 36 kHz carrier wave.
The wave is inverted on the right channel, so the left and right channels can be used to double the voltage.


The top wave is how the wave looks when it comes out the soundcard,
the bottom wave is how it looks after rectification, as you can see the frequency is doubled.

The rectification is done using the following circuit:



LED1 and LED2 are 950nm infrared leds, R1 is a 0.25 watt resistor.

Because leds are diodes, they only conduct one way.
Since the soundcard outputs a wave that goes both positive and negative, two leds are placed antiparallel,
that way infrared is emitted on both the positive and negative cycles.

R1 is used to limit the current, this presents a load to the soundcard that is roughly the same as a pair of 32 ohms headphones.
To make the transmission more powerful, you can try lowering the value of R1 (or just short it out),
but this might damage your soundcard, the leds, or both. So try at your own risk!

Another way to make the transmission more powerful is to use a small speaker amplifier (5 watts or less),
in this case a 5 watt resistor should be used for safety. The volume should be adjusted so that the amplifier outputs its full voltage without clipping.

Setting up:

Compile lirc with the audio driver (not the IR diode or alsa ones) and install it as usual.
Connect the circuit to the soundcard and set the volume to the maximum level.
Start lircd, the -d flag can be used to select the audio device and/or samplerate, the syntax is api:device[@samplerate] or @samplerate.
Examples:

lircd -d ALSA:default
lircd -d ALSA:default@48000
lircd -d @48000.

Use irsend to test if it works.

Known issues:

The audio driver uses portaudio to interface with the soundcard, there seems to be a bug in some later versions
that makes portaudio hang completely, lircd becomes unresponsive and you have to kill it with killall -9 lircd.
To get around this use the portaudio stable release from December 7, 2007.



------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

transmitter.png (1K) Download Attachment
wave.png (2K) Download Attachment

Re: [patch] Send IR with hw_audio

by Christoph Bartelmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi!

Bob van Loosen "bob.loosen@..." wrote:
[...]
> Latest patch assigns carrier frequency, I also added a usleep to give
> portaudio some time to set up properly, otherwise the first transmission
> might fail.

Is in CVS now. I've done some slight modifications (e.g. default carrier  
is 38kHz in LIRC).
Please check if everything is still working as expected.

Christoph

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
< Prev | 1 - 2 | Next >