|
View:
New views
20 Messages
—
Rating Filter:
Alert me
|
| < Prev | 1 - 2 | Next > |
|
|
[patch] Send IR with hw_audioHi,
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_audioHi!
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
Christoph Bartelmus wrote:
Will do.Hi! Bob van Loosen bob.loosen@... wrote: 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_audioHi!
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_audioLatest patch and documentation.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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 |
|
|
Re: [patch] Send IR with hw_audioHi!
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_audioOn 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_audioHi!
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_audioA 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.Hi! Bob van Loosen bob.loosen@... wrote: 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_audioHi!
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
Christoph Bartelmus wrote:
Sure, all that's needed is to replace the SAMPLE_RATE constant with a variable that's set somewhere.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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_audioAttached patch should do it.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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_audioHi!
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_audioShould be done with the pipe instead, otherwise it's not threadsafe.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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_audioHi!
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_audioThat 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.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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_audioHi!
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_audioLatest patch assigns carrier frequency, I also added a usleep to give portaudio some time to set up properly, otherwise the first transmission might fail.Hi! Bob van Loosen bob.loosen@... wrote: [...] 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_audioAttached 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
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
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 |
|
|
Re: [patch] Send IR with hw_audioHi!
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 > |
| Free embeddable forum powered by Nabble | Forum Help |