roleoroleo / qr_scanner

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sound feedback

ricardojlrufino opened this issue · comments

It would be cool has sound feedback for reading.
I tried to put a beep, but logic works only on some terminals.

fprintf(stdout, "\a" ); // BEEP !!!

commented

The beep doesn't work.
In the past, I tried to use alsa but no success.
I'm not an alsa expert.
The hw device is busy because rmm opens it and doesn't release it.
I don't know how to solve this problem.
Maybe using mixer? Reconfiguring alsa?
I don't know.

How microphone audio is captured?

commented

Thanks to another user, alsa-lib is hacked.
The pcm audio is extracted and sent to a fifo.

Its possible play audio without rmm running

// A simple C example to play a mono or stereo, 16-bit 44KHz
// WAVE file using ALSA. This goes directly to the first
// audio card (ie, its first set of audio out jacks). It
// uses the snd_pcm_writei() mode of outputting waveform data,
// blocking.
//
// Compile as so to create "alsawave":
// gcc -o alsawave alsawave.c -lasound
//
// Run it from a terminal, specifying the name of a WAVE file to play:
// ./alsawave MyWaveFile.wav

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>


// Include the ALSA .H file that defines ALSA functions/data
#include <alsa/asoundlib.h>


#pragma pack (1)
/////////////////////// WAVE File Stuff /////////////////////
// An IFF file header looks like this
typedef struct _FILE_head
{
	unsigned char	ID[4];	// could be {'R', 'I', 'F', 'F'} or {'F', 'O', 'R', 'M'}
	unsigned int	Length;	// Length of subsequent file (including remainder of header). This is in
									// Intel reverse byte order if RIFF, Motorola format if FORM.
	unsigned char	Type[4];	// {'W', 'A', 'V', 'E'} or {'A', 'I', 'F', 'F'}
} FILE_head;


// An IFF chunk header looks like this
typedef struct _CHUNK_head
{
	unsigned char ID[4];	// 4 ascii chars that is the chunk ID
	unsigned int	Length;	// Length of subsequent data within this chunk. This is in Intel reverse byte
							// order if RIFF, Motorola format if FORM. Note: this doesn't include any
							// extra byte needed to pad the chunk out to an even size.
} CHUNK_head;

// WAVE fmt chunk
typedef struct _FORMAT {
	short				wFormatTag;
	unsigned short	wChannels;
	unsigned int	dwSamplesPerSec;
	unsigned int	dwAvgBytesPerSec;
	unsigned short	wBlockAlign;
	unsigned short	wBitsPerSample;
  // Note: there may be additional fields here, depending upon wFormatTag
} FORMAT;
#pragma pack()






// Size of the audio card hardware buffer. Here we want it
// set to 1024 16-bit sample points. This is relatively
// small in order to minimize latency. If you have trouble
// with underruns, you may need to increase this, and PERIODSIZE
// (trading off lower latency for more stability)
#define BUFFERSIZE	(2*1024)

// How many sample points the ALSA card plays before it calls
// our callback to fill some more of the audio card's hardware
// buffer. Here we want ALSA to call our callback after every
// 64 sample points have been played
#define PERIODSIZE	(2*64)

// Handle to ALSA (audio card's) playback port
snd_pcm_t				*PlaybackHandle;

// Handle to our callback thread
snd_async_handler_t	*CallbackHandle;

// Points to loaded WAVE file's data
unsigned char			*WavePtr;

// Size (in frames) of loaded WAVE file's data
snd_pcm_uframes_t		WaveSize;

// Sample rate
unsigned short			WaveRate;

// Bit resolution
unsigned char			WaveBits;

// Number of channels in the wave file
unsigned char			WaveChannels;

// The name of the ALSA port we output to. In this case, we're
// directly writing to hardware card 0,0 (ie, first set of audio
// outputs on the first audio card)
static const char		SoundCardPortName[] = "plughw:0,0";

// For WAVE file loading
static const unsigned char Riff[4]	= { 'R', 'I', 'F', 'F' };
static const unsigned char Wave[4] = { 'W', 'A', 'V', 'E' };
static const unsigned char Fmt[4] = { 'f', 'm', 't', ' ' };
static const unsigned char Data[4] = { 'd', 'a', 't', 'a' };





/********************** compareID() *********************
 * Compares the passed ID str (ie, a ptr to 4 Ascii
 * bytes) with the ID at the passed ptr. Returns TRUE if
 * a match, FALSE if not.
 */

static unsigned char compareID(const unsigned char * id, unsigned char * ptr)
{
	register unsigned char i = 4;

	while (i--)
	{
		if ( *(id)++ != *(ptr)++ ) return(0);
	}
	return(1);
}





/********************** waveLoad() *********************
 * Loads a WAVE file.
 *
 * fn =			Filename to load.
 *
 * RETURNS: 0 if success, non-zero if not.
 *
 * NOTE: Sets the global "WavePtr" to an allocated buffer
 * containing the wave data, and "WaveSize" to the size
 * in sample points.
 */

static unsigned char waveLoad(const char *fn)
{
	const char				*message;
	FILE_head				head;
	register int			inHandle;

	if ((inHandle = open(fn, O_RDONLY)) == -1)
		message = "didn't open";

	// Read in IFF File header
	else
	{
		if (read(inHandle, &head, sizeof(FILE_head)) == sizeof(FILE_head))
		{
			// Is it a RIFF and WAVE?
			if (!compareID(&Riff[0], &head.ID[0]) || !compareID(&Wave[0], &head.Type[0]))
			{
				message = "is not a WAVE file";
				goto bad;
			}

			// Read in next chunk header
			while (read(inHandle, &head, sizeof(CHUNK_head)) == sizeof(CHUNK_head))
			{
				// ============================ Is it a fmt chunk? ===============================
				if (compareID(&Fmt[0], &head.ID[0]))
				{
					FORMAT	format;

					// Read in the remainder of chunk
					if (read(inHandle, &format.wFormatTag, sizeof(FORMAT)) != sizeof(FORMAT)) break;

					// Can't handle compressed WAVE files
					if (format.wFormatTag != 1)
					{
						message = "compressed WAVE not supported";
						goto bad;
					}

					WaveBits = (unsigned char)format.wBitsPerSample;
					WaveRate = (unsigned short)format.dwSamplesPerSec;
					WaveChannels = format.wChannels;
				}

				// ============================ Is it a data chunk? ===============================
				else if (compareID(&Data[0], &head.ID[0]))
				{
					// Size of wave data is head.Length. Allocate a buffer and read in the wave data
					if (!(WavePtr = (unsigned char *)malloc(head.Length)))
					{
						message = "won't fit in RAM";
						goto bad;
					}

					if (read(inHandle, WavePtr, head.Length) != head.Length)
					{
						free(WavePtr);
						break;
					}

					// Store size (in frames)
					WaveSize = (head.Length * 8) / ((unsigned int)WaveBits * (unsigned int)WaveChannels);

					close(inHandle);
					return(0);
				}

				// ============================ Skip this chunk ===============================
				else
				{
					if (head.Length & 1) ++head.Length;  // If odd, round it up to account for pad byte
					lseek(inHandle, head.Length, SEEK_CUR);
				}
			}
		}

		message = "is a bad WAVE file";
bad:	close(inHandle);
	}

	printf("%s %s\n", fn, message);
	return(1);
}









/********************** play_audio() **********************
 * Plays the loaded waveform.
 *
 * NOTE: ALSA sound card's handle must be in the global
 * "PlaybackHandle". A pointer to the wave data must be in
 * the global "WavePtr", and its size of "WaveSize".
 */

static void play_audio(void)
{
	register snd_pcm_uframes_t		count, frames;

	// Output the wave data
	count = 0;
	do
	{
		frames = snd_pcm_writei(PlaybackHandle, WavePtr + count, WaveSize - count);

		// If an error, try to recover from it
		if (frames < 0)
			frames = snd_pcm_recover(PlaybackHandle, frames, 0);
		if (frames < 0)
		{
			printf("Error playing wave: %s\n", snd_strerror(frames));
			break;
		}

		// Update our pointer
		count += frames;

	} while (count < WaveSize);

	// Wait for playback to completely finish
	if (count == WaveSize)
		snd_pcm_drain(PlaybackHandle);
}





/*********************** free_wave_data() *********************
 * Frees any wave data we loaded.
 *
 * NOTE: A pointer to the wave data be in the global
 * "WavePtr".
 */

static void free_wave_data(void)
{
	if (WavePtr) free(WavePtr);
	WavePtr = 0;
}


void list_devices(){
	char **hints;
	/* Enumerate sound devices */
	int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
	if (err != 0)
	   return;//Error! Just return

	char** n = hints;
	while (*n != NULL) {

	    char *name = snd_device_name_get_hint(*n, "NAME");

	    if (name != NULL && 0 != strcmp("null", name)) {
	        //Copy name to another buffer and then free it
	    	fprintf(stderr, "Name: %s \n", name);
	        free(name);
	    }
	    n++;
	}//End of while

	//Free hint buffer too
	snd_device_name_free_hint((void**)hints);
}


int main(int argc, char **argv)
{
	// No wave data loaded yet
	WavePtr = 0;

	list_devices();

	if (argc < 2)
		printf("You must supply the name of a 16-bit mono WAVE file to play\n");

	// Load the wave file
	else if (!waveLoad(argv[1]))
	{
		register int		err;

		// Open audio card we wish to use for playback
		if ((err = snd_pcm_open(&PlaybackHandle, &SoundCardPortName[0], SND_PCM_STREAM_PLAYBACK, 0)) < 0)
			printf("Can't open audio %s: %s\n", &SoundCardPortName[0], snd_strerror(err));
		else
		{
			switch (WaveBits)
			{
				case 8:
					err = SND_PCM_FORMAT_U8;
					break;

				case 16:
					err = SND_PCM_FORMAT_S16;
					break;

				case 24:
					err = SND_PCM_FORMAT_S24;
					break;

				case 32:
					err = SND_PCM_FORMAT_S32;
					break;
			}

			// Set the audio card's hardware parameters (sample rate, bit resolution, etc)
			if ((err = snd_pcm_set_params(PlaybackHandle, err, SND_PCM_ACCESS_RW_INTERLEAVED, WaveChannels, WaveRate, 1, 500000)) < 0)
				printf("Can't set sound parameters: %s\n", snd_strerror(err));

			// Play the waveform
			else
				play_audio();

			// Close sound card
			snd_pcm_close(PlaybackHandle);
		}
	}

	// Free the WAVE data
	free_wave_data();

	return(0);
}

http://freewavesamples.com/files/Roland-JD-990-Windchimes.wav

Rmm running i get
root@yi-cam-01:/tmp/sd/my# ./audioplay /tmp/sd/record/Roland-JD-990-Windchimes.wav
Name: default:CARD=sun8iw19codec
Name: sysdefault:CARD=sun8iw19codec
Name: default:CARD=snddaudio0
Name: sysdefault:CARD=snddaudio0
Can't open audio plughw:0,0: Resource busy

commented

If you stop rmm you can play audio.
For example with aplay or speaker-test from alsa-utils.
But if you stop rmm you can't receive h264 stream.

its possible to create a virtual alsa device ?!

In my frist attempt to decompile a binary, i found the alsa "CARD NUMBER", and changed to 2 ..

image

but I don’t know how to create a virtual device ... I don’t even know if it’s possible to do this on the current platform

changin rmm, to use: hw:0,0 , get this erro:


E0326 19:03:24.987167   443 alsa_interface.c:447]           <alsaOpenPcm> audio open error: Resource busy
E0326 19:03:24.989010   443 audio_hw.c:1761]                <audioHw_AO_Enable> audioHw_AO_Enable,l:1761,open_pcm failed
rmm_init_adec_ao(3731) MPI_AO_Enable ao_dev:0 fail! ret:0xffffffff

but my example only works with hw: 0.0 ..

PS: I'm not running at the same time ...

commented

Use this new hacked library:
libasound.so.2.0.0.gz

It will create a new fifo /tmp/audio_in_fifo
If you send to this fifo a raw pcm the cam should play it.

The file must be a raw pcm (you can export it with audacity) with the following parameters:

  • 16 KHz
  • 16 bit
  • mono
  • signed

Copy to the fifo with a normal cat.

commented

Ah...
Change system.sh to run rmm with this library:

...
if [[ $(get_config DISABLE_CLOUD) == "no" ]] ; then
    (
        cd /home/app
        LD_LIBRARY_PATH="/home/yi-hack/lib:/lib:/usr/lib:/home/lib:/home/qigan/lib:/home/app/locallib" ./rmm &
        sleep 4
        dd if=/tmp/audio_fifo of=/dev/null bs=1 count=8192
#        dd if=/dev/zero of=/tmp/audio_in_fifo bs=1 count=1024
        ./mp4record &
        ./cloud &
        ./p2p_tnp &
        ./oss &
        if [ -f ./oss_fast ]; then
            ./oss_fast &
        fi
        if [ -f ./oss_lapse ]; then
            ./oss_lapse &
        fi
        ./watch_process &
    )
else
    (
        cd /home/app
        LD_LIBRARY_PATH="/home/yi-hack/lib:/lib:/usr/lib:/home/lib:/home/qigan/lib:/home/app/locallib" ./rmm &
        sleep 4
        dd if=/tmp/audio_fifo of=/dev/null bs=1 count=8192
#        dd if=/dev/zero of=/tmp/audio_in_fifo bs=1 count=1024
        # Trick to start circular buffer filling
        start_buffer
        if [[ $(get_config REC_WITHOUT_CLOUD) == "yes" ]] ; then
            ./mp4record &
        fi

        mkdir /tmp/etc
        cp -R /etc/* /tmp/etc
        mount --bind /tmp/etc /etc
        echo "127.0.0.1    api.eu.xiaoyi.com" >> /etc/hosts
    )
fi
...

I tested it and the sound is very distorted, it seems that it is kind of accelerated
Im using this file
StarWars3.zip

I started a big wav music using:

root@yi-cam-01:/tmp/sd/my# ./aplay OficinaG3.wav 
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
Playing WAVE 'OficinaG3.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
underrun!!! (at least 0.079 ms long)
underrun!!! (at least 0.086 ms long)

then:
cat StarWars3.wav > /tmp/audio_in_fifo

sorry, running RMM works... !!! amazing !!

There are only a few minor distortions when I went to test on notification sounds, which would be nice for confirming the reading of the qrcode

I'm sending you to test ..
notifications-sounds.zip
.

Sometimes i get: Segmentation fault (core dumped), running aplay
PS: RMM application (STOPPED) to test sound quality without it

root@yi-cam-01:/tmp/sd/my# ./aplay job-done-501-16khz.wav 
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
Playing WAVE 'job-done-501-16khz.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
root@yi-cam-01:/tmp/sd/my# ./aplay job-done-501-16khz.wav 
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
mode 0 - bits 0 - channels 0
Playing WAVE 'job-done-501-16khz.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
Segmentation fault (core dumped)