Пример: звукозапись (waverec.c)

Исходный код утилиты waverec

Приложение захватывает аудио данные и записывате их в .wav файл:

/*
* (c) 2014, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
#include <errno.h>
#include <fcntl.h>
#include <gulliver.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/termio.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <sys/asoundlib.h>
struct {
char riff_id[4];
char wave_len[4];
struct {
char fmt_id[8];
char fmt_len[4];
struct {
char format_tag[2];
char voices[2];
char rate[4];
char char_per_sec[4];
char block_align[2];
char bits_per_sample[2];
} fmt;
struct {
char data_id[4];
char data_len[4];
} data;
} wave;
} riff_hdr = { { 'R', 'I', 'F', 'F' },
{ sizeof( riff_hdr.wave ), 0, 0, 0 },
{ {'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' },
{ sizeof( riff_hdr.wave.fmt ), 0, 0, 0 },
{ { 1, 0 },
{ 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 } },
{ { 'd', 'a', 't', 'a' },
{ 0, 0, 0, 0 } } } };
int err( char *msg )
{
perror( msg );
return (-1);
}
int dev_raw( int fd )
{
struct termios termios_p;
if ( tcgetattr( fd, &termios_p ) )
return (-1);
termios_p.c_cc[VMIN] = 1;
termios_p.c_cc[VTIME] = 0;
termios_p.c_lflag &= ~(ECHO | ICANON | ISIG | ECHOE | ECHOK | ECHONL);
termios_p.c_oflag &= ~(OPOST);
return (tcsetattr( fd, TCSANOW, &termios_p ));
}
int dev_unraw( int fd )
{
struct termios termios_p;
if ( tcgetattr( fd, &termios_p ) )
return (-1);
termios_p.c_lflag |= (ECHO | ICANON | ISIG | ECHOE | ECHOK | ECHONL);
termios_p.c_oflag |= (OPOST);
return (tcsetattr( fd, TCSAFLUSH, &termios_p ));
}
//*****************************************************************************
#ifdef __USAGE
%C[Options] *
Options:
-8 use 8 bit mode (16 bit default)
-a[card#:]<dev#> the card & device number to record from
-m record in mono (stereo default)
-r <rate> record at rate (44100 default | 48000 44100 22050 11025)
-t <sec> seconds to record (5 seconds default)
-f <frag_size> requested fragment size
-v verbosity
-c <args>[,args ...] voice matrix configuration
Args:
1=<hw_channel_bitmask> hardware channel bitmask for application voice 1
2=<hw_channel_bitmask> hardware channel bitmask for application voice 2
3=<hw_channel_bitmask> hardware channel bitmask for application voice 3
4=<hw_channel_bitmask> hardware channel bitmask for application voice 4
#endif
//*****************************************************************************
int main( int argc, char **argv )
{
int card = -1;
int dev = 0;
snd_pcm_t *pcm_handle;
FILE *file1;
int mSamples;
int mSampleRate;
int mSampleChannels;
int mSampleBits;
int mSampleTime;
char *mSampleBfr1;
int fragsize = -1;
int verbose = 0;
int rtn;
snd_pcm_channel_info_t pi;
snd_mixer_t *mixer_handle;
snd_mixer_group_t group;
snd_pcm_channel_params_t pp;
snd_pcm_channel_setup_t setup;
int bsize,
n,
N = 0,
c;
uint32_t voice_mask[] = { 0, 0, 0, 0 };
snd_pcm_voice_conversion_t voice_conversion;
int voice_override = 0;
char *sub_opts,
*value;
char *dev_opts[] = { "1",
#define CHN1 0
"2",
#define CHN2 1
"3",
#define CHN3 2
"4",
#define CHN4 3
NULL };
char name[_POSIX_PATH_MAX] = { 0 };
fd_set rfds;
mSampleRate = 44100;
mSampleChannels = 2;
mSampleBits = 16;
mSampleTime = 5;
while ( (c = getopt( argc, argv, "8a:f:mr:t:vc:" )) != EOF )
{
switch ( c )
{
case '8':
mSampleBits = 8;
break;
case 'a':
if ( strchr( optarg, ':' ) )
{
card = atoi( optarg );
dev = atoi( strchr( optarg, ':' ) + 1 );
} else
if ( isalpha( optarg[0] ) )
strcpy( name, optarg );
else
dev = atoi( optarg );
if ( name[0] != '\0' )
printf( "Using device /dev/snd/%s\n", name );
else
printf( "Using card %d device %d \n", card, dev );
break;
case 'f':
fragsize = atoi( optarg );
break;
case 'm':
mSampleChannels = 1;
break;
case 'r':
mSampleRate = atoi( optarg );
break;
case 't':
mSampleTime = atoi( optarg );
break;
case 'v':
verbose = 1;
break;
case 'c':
sub_opts = strdup( optarg );
while ( *sub_opts != '\0' )
{
switch ( getsubopt( &sub_opts, dev_opts, &value ) )
{
case CHN1:
voice_mask[0] = strtoul( value, NULL, 0 );
break;
case CHN2:
voice_mask[1] = strtoul( value, NULL, 0 );
break;
case CHN3:
voice_mask[2] = strtoul( value, NULL, 0 );
break;
case CHN4:
voice_mask[3] = strtoul( value, NULL, 0 );
break;
default:
break;
}
}
voice_override = 1;
break;
default:
return (1);
}
}
setvbuf( stdin, NULL, _IONBF, 0 );
if ( name[0] != '\0' )
{
snd_pcm_info_t info;
if ( (rtn = snd_pcm_open_name( &pcm_handle, name, SND_PCM_OPEN_CAPTURE )) < 0 )
return err( "open_name" );
rtn = snd_pcm_info( pcm_handle, &info );
card = info.card;
} else {
if ( card == -1 )
{
if ( (rtn = snd_pcm_open_preferred( &pcm_handle, &card, &dev, SND_PCM_OPEN_CAPTURE )) < 0 )
return err( "device open" );
} else {
if ( (rtn = snd_pcm_open( &pcm_handle, card, dev, SND_PCM_OPEN_CAPTURE )) < 0 )
return err( "device open" );
}
}
if ( argc < 2 )
return err( "no file specified" );
if ( (file1 = fopen( argv[optind], "w" )) == 0 )
return err( "file open #1" );
mSamples = mSampleRate * mSampleChannels * mSampleBits / 8 * mSampleTime;
*(short *)riff_hdr.wave.fmt.voices = ENDIAN_LE16( mSampleChannels );
*(long *)riff_hdr.wave.fmt.rate = ENDIAN_LE32( mSampleRate );
*(long *)riff_hdr.wave.fmt.char_per_sec = ENDIAN_LE32( mSampleRate * mSampleChannels * mSampleBits / 8 );
*(short *)riff_hdr.wave.fmt.block_align = ENDIAN_LE16( mSampleChannels * mSampleBits / 8 );
*(short *)riff_hdr.wave.fmt.bits_per_sample = ENDIAN_LE16( mSampleBits );
*(long *)riff_hdr.wave.data.data_len = ENDIAN_LE32( mSamples );
*(long *)riff_hdr.wave_len = ENDIAN_LE32( mSamples + sizeof( riff_hdr ) - 8 );
fwrite( &riff_hdr, 1, sizeof( riff_hdr ), file1 );
printf( "SampleRate = %d, Channels = %d, SampleBits = %d\n", mSampleRate, mSampleChannels, mSampleBits );
/* disabling mmap is not actually required in this example but it is included to
* demonstrate how it is used when it is required. */
if ( (rtn = snd_pcm_plugin_set_disable( pcm_handle, PLUGIN_DISABLE_MMAP )) < 0 )
{
fprintf( stderr, "snd_pcm_plugin_set_disable failed: %s\n", snd_strerror( rtn ) );
return (-1);
}
memset( &pi, 0, sizeof( pi ) );
pi.channel = SND_PCM_CHANNEL_CAPTURE;
if ( (rtn = snd_pcm_plugin_info( pcm_handle, &pi )) < 0 )
{
fprintf( stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror( rtn ) );
return (-1);
}
memset( &pp, 0, sizeof( pp ) );
pp.mode = SND_PCM_MODE_BLOCK;
pp.channel = SND_PCM_CHANNEL_CAPTURE;
pp.start_mode = SND_PCM_START_DATA;
pp.stop_mode = SND_PCM_STOP_STOP;
pp.buf.block.frag_size = pi.max_fragment_size;
if ( fragsize != -1 )
pp.buf.block.frag_size = fragsize;
pp.buf.block.frags_max = -1;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
pp.format.rate = mSampleRate;
pp.format.voices = mSampleChannels;
if ( mSampleBits == 8 )
pp.format.format = SND_PCM_SFMT_U8;
else
pp.format.format = SND_PCM_SFMT_S16_LE;
if ( (rtn = snd_pcm_plugin_params( pcm_handle, &pp )) < 0 )
{
fprintf( stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror( rtn ) );
return (-1);
}
if ( (rtn = snd_pcm_plugin_prepare( pcm_handle, SND_PCM_CHANNEL_CAPTURE )) < 0 )
fprintf( stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror( rtn ) );
if ( voice_override )
{
snd_pcm_plugin_get_voice_conversion( pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion );
voice_conversion.matrix[0] = voice_mask[0];
voice_conversion.matrix[1] = voice_mask[1];
voice_conversion.matrix[2] = voice_mask[2];
voice_conversion.matrix[3] = voice_mask[3];
snd_pcm_plugin_set_voice_conversion( pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion );
}
memset( &setup, 0, sizeof( setup ) );
memset( &group, 0, sizeof( group ) );
setup.channel = SND_PCM_CHANNEL_CAPTURE;
setup.mixer_gid = &group.gid;
if ( (rtn = snd_pcm_plugin_setup( pcm_handle, &setup )) < 0 )
{
fprintf( stderr, "snd_pcm_plugin_setup failed: %s\n", snd_strerror( rtn ) );
return (-1);
}
printf( "Format %s \n", snd_pcm_get_format_name( setup.format.format ) );
printf( "Frag Size %d \n", setup.buf.block.frag_size );
printf( "Rate %d \n", setup.format.rate );
bsize = setup.buf.block.frag_size;
if ( group.gid.name[0] == 0 )
{
printf( "Mixer Pcm Group [%s] Not Set \n", group.gid.name );
printf( "***>>>> Input Gain Controls Disabled <<<<*** \n" );
} else
printf( "Mixer Pcm Group [%s]\n", group.gid.name );
if ( (rtn = snd_mixer_open( &mixer_handle, card, setup.mixer_device )) < 0 )
{
fprintf( stderr, "snd_mixer_open failed: %s\n", snd_strerror( rtn ) );
return (-1);
}
mSampleBfr1 = malloc( bsize );
FD_ZERO( &rfds );
n = 1;
while ( N < mSamples && n > 0 )
{
FD_SET( STDIN_FILENO, &rfds );
FD_SET( snd_mixer_file_descriptor( mixer_handle ), &rfds );
FD_SET( snd_pcm_file_descriptor( pcm_handle, SND_PCM_CHANNEL_CAPTURE ), &rfds );
rtn = max( snd_mixer_file_descriptor( mixer_handle ),
snd_pcm_file_descriptor( pcm_handle, SND_PCM_CHANNEL_CAPTURE ) );
if ( select( rtn + 1, &rfds, NULL, NULL, NULL ) == -1 )
return err( "select" );
if ( FD_ISSET( STDIN_FILENO, &rfds ) )
{
dev_raw( fileno( stdin ) );
c = getc( stdin );
dev_unraw( fileno( stdin ) );
if ( c != EOF )
{
if ( group.gid.name[0] != 0 )
{
if ( (rtn = snd_mixer_group_read( mixer_handle, &group )) < 0 )
fprintf( stderr, "snd_mixer_group_read failed: %s\n", snd_strerror( rtn ) );
switch ( c )
{
case 'q':
group.volume.names.front_left += 1;
break;
case 'a':
group.volume.names.front_left -= 1;
break;
case 'w':
group.volume.names.front_left += 1;
group.volume.names.front_right += 1;
break;
case 's':
group.volume.names.front_left -= 1;
group.volume.names.front_right -= 1;
break;
case 'e':
group.volume.names.front_right += 1;
break;
case 'd':
group.volume.names.front_right -= 1;
break;
}
if ( group.volume.names.front_left > group.max )
group.volume.names.front_left = group.max;
if ( group.volume.names.front_left < group.min )
group.volume.names.front_left = group.min;
if ( group.volume.names.front_right > group.max )
group.volume.names.front_right = group.max;
if ( group.volume.names.front_right < group.min )
group.volume.names.front_right = group.min;
if ( (rtn = snd_mixer_group_write( mixer_handle, &group )) < 0 )
fprintf( stderr, "snd_mixer_group_write failed: %s\n", snd_strerror( rtn ) );
printf ( "Volume Now at %d:%d \n",
100 * (group.volume.names.front_left - group.min) / (group.max - group.min),
100 * (group.volume.names.front_right - group.min) / (group.max - group.min));
}
} else
exit( 0 );
}
if ( FD_ISSET( snd_mixer_file_descriptor( mixer_handle ), &rfds ) )
{
snd_mixer_callbacks_t callbacks = { 0, 0, 0, 0 };
snd_mixer_read( mixer_handle, &callbacks );
}
if ( FD_ISSET( snd_pcm_file_descriptor( pcm_handle, SND_PCM_CHANNEL_CAPTURE ), &rfds ) )
{
snd_pcm_channel_status_t status;
int read = 0;
read = snd_pcm_plugin_read( pcm_handle, mSampleBfr1, bsize );
if ( verbose )
printf( "bytes read = %d \n", read );
if ( read < n )
{
memset( &status, 0, sizeof( status ) );
status.channel = SND_PCM_CHANNEL_CAPTURE;
if ( snd_pcm_plugin_status( pcm_handle, &status ) < 0 )
{
fprintf( stderr, "overrun: capture channel status error\n" );
exit( 1 );
}
if ( status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN )
{
if ( snd_pcm_plugin_prepare( pcm_handle, SND_PCM_CHANNEL_CAPTURE ) < 0 )
{
fprintf( stderr, "overrun: capture channel prepare error\n" );
exit( 1 );
}
}
}
fwrite( mSampleBfr1, 1, read, file1 );
N += read;
}
}
n = snd_pcm_plugin_flush( pcm_handle, SND_PCM_CHANNEL_CAPTURE );
rtn = snd_mixer_close( mixer_handle );
rtn = snd_pcm_close( pcm_handle );
fclose( file1 );
return (0);
}




Предыдущий раздел: Библиотека libasound