Пример: воспроизведение (wave.c)

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

Приложение проигрывает .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 <sys/slogcodes.h>
#include <ctype.h>
#include <limits.h>
#include <sys/asoundlib.h>
const char *kRiffId = "RIFF";
const char *kWaveId = "WAVE";
typedef struct {
char tag[4];
long length;
} RiffTag;
typedef struct {
char Riff[4];
long Size;
char Wave[4];
} RiffHdr;
typedef struct {
short FormatTag;
short Channels;
long SamplesPerSec;
long AvgBytesPerSec;
short BlockAlign;
short BitsPerSample;
} WaveHdr;
int err( char *msg )
{
perror( msg );
return (-1);
}
int FindTag( FILE *fp, const char *tag )
{
int retVal = 0;
RiffTag tagBfr = { "", 0 };
// Keep reading until we find the tag or hit the EOF.
while ( fread( (unsigned char *)&tagBfr, sizeof( tagBfr ), 1, fp ) )
{
// If this is our tag, set the length and break.
if ( strncmp( tag, tagBfr.tag, sizeof tagBfr.tag ) == 0 )
{
retVal = ENDIAN_LE32( tagBfr.length );
break;
}
// Skip ahead the specified number of bytes in the stream
fseek( fp, tagBfr.length, SEEK_CUR );
}
// Return the result of our operation
return (retVal);
}
int CheckHdr( FILE *fp )
{
RiffHdr riffHdr = { "", 0 };
// Read the header and, if successful, play the file file or WAVE file.
if ( fread( (unsigned char *)&riffHdr, sizeof( RiffHdr ), 1, fp ) == 0 )
return (0);
if ( strncmp( riffHdr.Riff, kRiffId, strlen( kRiffId ) ) ||
strncmp( riffHdr.Wave, kWaveId, strlen( kWaveId ) ) )
return (-1);
return (0);
}
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:
-a[card#:]<dev#> the card & device number to play out on
-f<frag_size> requested fragment size
-v verbose
-c<args>[,args ..] voice matrix configuration
-n<num_frags> requested number of fragments
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;
WaveHdr wavHdr1;
int mSamples;
int mSampleRate;
int mSampleChannels;
int mSampleBits;
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;
fd_set rfds,
wfds;
uint32_t voice_mask[] = { 0, 0, 0, 0 };
snd_pcm_voice_conversion_t voice_conversion;
int voice_override = 0;
int num_frags = -1;
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 };
while ( (c = getopt( argc, argv, "a:f:vc:n:" )) != EOF )
{
switch ( c )
{
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 '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;
case 'n':
num_frags = atoi( optarg ) - 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_PLAYBACK )) < 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_PLAYBACK )) < 0 )
return err( "device open" );
} else {
if ( (rtn = snd_pcm_open( &pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK )) < 0 )
return err( "device open" );
}
}
if ( argc < 2 )
return err( "no file specified" );
if ( (file1 = fopen( argv[optind], "r" )) == 0 )
return err( "file open #1" );
if ( CheckHdr( file1 ) == -1 )
return err( "CheckHdr #1" );
mSamples = FindTag( file1, "fmt " );
fread( &wavHdr1, sizeof( wavHdr1 ), 1, file1 );
fseek( file1, (mSamples - sizeof( WaveHdr )), SEEK_CUR );
mSampleRate = ENDIAN_LE32( wavHdr1.SamplesPerSec );
mSampleChannels = ENDIAN_LE16( wavHdr1.Channels );
mSampleBits = ENDIAN_LE16( wavHdr1.BitsPerSample );
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_PLAYBACK;
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_PLAYBACK;
pp.start_mode = SND_PCM_START_FULL;
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 = num_frags;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
pp.format.rate = mSampleRate;
pp.format.voices = mSampleChannels;
if ( ENDIAN_LE16( wavHdr1.FormatTag ) == 6 )
pp.format.format = SND_PCM_SFMT_A_LAW;
else if ( ENDIAN_LE16( wavHdr1.FormatTag ) == 7 )
pp.format.format = SND_PCM_SFMT_MU_LAW;
else if ( mSampleBits == 8 )
pp.format.format = SND_PCM_SFMT_U8;
else if ( mSampleBits == 24 )
pp.format.format = SND_PCM_SFMT_S24;
else
pp.format.format = SND_PCM_SFMT_S16_LE;
strcpy ( pp.sw_mixer_subchn_name, "Wave playback channel" );
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_PLAYBACK )) < 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_PLAYBACK, &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_PLAYBACK, &voice_conversion );
}
memset( &setup, 0, sizeof( setup ) );
memset( &group, 0, sizeof( group ) );
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
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( "Total Frags %d \n", setup.buf.block.frags );
printf( "Rate %d \n", setup.format.rate );
printf( "Voices %d \n", setup.format.voices );
bsize = setup.buf.block.frag_size;
if ( group.gid.name[0] == 0 )
{
printf( "Mixer Pcm Group [%s] Not Set \n", group.gid.name );
exit( -1 );
}
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);
}
mSamples = FindTag( file1, "data" );
mSampleBfr1 = malloc( bsize );
FD_ZERO( &rfds );
FD_ZERO( &wfds );
n = 1;
while ( N < mSamples && n > 0 )
{
if ( tcgetpgrp( 0 ) == getpid() )
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_PLAYBACK ), &wfds );
rtn = max( snd_mixer_file_descriptor( mixer_handle ),
snd_pcm_file_descriptor( pcm_handle, SND_PCM_CHANNEL_PLAYBACK ) );
if ( select( rtn + 1, &rfds, &wfds, NULL, NULL ) == -1 )
return err( "select" );
if ( FD_ISSET( STDIN_FILENO, &rfds ) )
{
if ( (rtn = snd_mixer_group_read( mixer_handle, &group )) < 0 )
fprintf( stderr, "snd_mixer_group_read failed: %s\n", snd_strerror( rtn ) );
dev_raw( fileno( stdin ) );
c = getc( stdin );
dev_unraw( fileno( stdin ) );
if ( c != EOF )
{
switch ( c )
{
case 'q':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
group.volume.names.front_left += 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
group.volume.names.rear_left += 10;
if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
group.volume.names.woofer += 10;
break;
case 'a':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
group.volume.names.front_left -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
group.volume.names.rear_left -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
group.volume.names.woofer -= 10;
break;
case 'w':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
group.volume.names.front_left += 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
group.volume.names.rear_left += 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER )
group.volume.names.front_center += 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT )
group.volume.names.front_right += 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT )
group.volume.names.rear_right += 10;
if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
group.volume.names.woofer += 10;
break;
case 's':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
group.volume.names.front_left -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
group.volume.names.rear_left -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER )
group.volume.names.front_center -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT )
group.volume.names.front_right -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT )
group.volume.names.rear_right -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
group.volume.names.woofer -= 10;
break;
case 'e':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT )
group.volume.names.front_right += 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT )
group.volume.names.rear_right += 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER )
group.volume.names.front_center += 10;
break;
case 'd':
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT )
group.volume.names.front_right -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT )
group.volume.names.rear_right -= 10;
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER )
group.volume.names.front_center -= 10;
break;
}
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
{
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.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
{
if ( group.volume.names.rear_left > group.max )
group.volume.names.rear_left = group.max;
if ( group.volume.names.rear_left < group.min )
group.volume.names.rear_left = group.min;
}
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER )
{
if ( group.volume.names.front_center > group.max )
group.volume.names.front_center = group.max;
if ( group.volume.names.front_center < group.min )
group.volume.names.front_center = group.min;
}
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT )
{
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 ( group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT )
{
if ( group.volume.names.rear_right > group.max )
group.volume.names.rear_right = group.max;
if ( group.volume.names.rear_right < group.min )
group.volume.names.rear_right = group.min;
}
if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
{
if ( group.volume.names.woofer > group.max )
group.volume.names.woofer = group.max;
if ( group.volume.names.woofer < group.min )
group.volume.names.woofer = group.min;
}
if ( (rtn = snd_mixer_group_write( mixer_handle, &group )) < 0 )
fprintf( stderr, "snd_mixer_group_write failed: %s\n", snd_strerror( rtn ) );
} else
exit( 0 );
if ( group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT )
{
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 if ( group.channels & SND_MIXER_CHN_MASK_REAR_LEFT )
{
printf( "Volume Now at %d:%d \n",
100 * (group.volume.names.rear_left - group.min) / (group.max - group.min),
100 * (group.volume.names.rear_right - group.min) / (group.max - group.min) );
}
else if ( group.channels & SND_MIXER_CHN_MASK_WOOFER )
{
printf( "Volume Now at %d:%d \n",
100 * (group.volume.names.woofer - group.min) / (group.max - group.min),
100 * (group.volume.names.front_center - group.min) / (group.max - group.min));
} else {
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));
}
}
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_PLAYBACK ), &wfds ) )
{
snd_pcm_channel_status_t status;
int written = 0;
if ( (n = fread( mSampleBfr1, 1, min( mSamples - N, bsize ), file1 )) <= 0 )
continue;
written = snd_pcm_plugin_write( pcm_handle, mSampleBfr1, n );
if ( verbose )
printf( "bytes written = %d \n", written );
if ( written < n )
{
memset( &status, 0, sizeof( status ) );
status.channel = SND_PCM_CHANNEL_PLAYBACK;
if ( snd_pcm_plugin_status( pcm_handle, &status ) < 0 )
{
fprintf( stderr, "underrun: playback channel status error\n" );
exit( 1 );
}
if ( status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN )
{
if ( snd_pcm_plugin_prepare( pcm_handle, SND_PCM_CHANNEL_PLAYBACK ) < 0 )
{
fprintf( stderr, "underrun: playback channel prepare error\n" );
exit( 1 );
}
}
if ( written < 0 )
written = 0;
written += snd_pcm_plugin_write( pcm_handle, mSampleBfr1 + written, n - written );
}
N += written;
}
}
n = snd_pcm_plugin_flush( pcm_handle, SND_PCM_CHANNEL_PLAYBACK );
rtn = snd_mixer_close( mixer_handle );
rtn = snd_pcm_close( pcm_handle );
fclose( file1 );
return (0);
}




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