2005-04-16 15:20:36 -07:00
/*
* PCM Plug - In shared ( kernel / library ) code
2007-10-15 09:50:19 +02:00
* Copyright ( c ) 1999 by Jaroslav Kysela < perex @ perex . cz >
2005-04-16 15:20:36 -07:00
* Copyright ( c ) 2000 by Abramo Bagnara < abramo @ alsa - project . org >
*
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
#if 0
# define PLUGIN_DEBUG
# endif
# include <linux/slab.h>
# include <linux/time.h>
# include <linux/vmalloc.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include "pcm_plugin.h"
# define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
# define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
/*
* because some cards might have rates " very close " , we ignore
* all " resampling " requests within + - 5 %
*/
static int rate_match ( unsigned int src_rate , unsigned int dst_rate )
{
unsigned int low = ( src_rate * 95 ) / 100 ;
unsigned int high = ( src_rate * 105 ) / 100 ;
return dst_rate > = low & & dst_rate < = high ;
}
2005-11-17 14:01:49 +01:00
static int snd_pcm_plugin_alloc ( struct snd_pcm_plugin * plugin , snd_pcm_uframes_t frames )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_format * format ;
2005-04-16 15:20:36 -07:00
ssize_t width ;
size_t size ;
unsigned int channel ;
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_channel * c ;
2005-04-16 15:20:36 -07:00
if ( plugin - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
format = & plugin - > src_format ;
} else {
format = & plugin - > dst_format ;
}
2021-06-08 16:05:29 +02:00
width = snd_pcm_format_physical_width ( format - > format ) ;
if ( width < 0 )
2005-04-16 15:20:36 -07:00
return width ;
size = frames * format - > channels * width ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( size % 8 ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
size / = 8 ;
if ( plugin - > buf_frames < frames ) {
2018-11-09 11:59:45 +01:00
kvfree ( plugin - > buf ) ;
plugin - > buf = kvzalloc ( size , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
plugin - > buf_frames = frames ;
}
if ( ! plugin - > buf ) {
plugin - > buf_frames = 0 ;
return - ENOMEM ;
}
c = plugin - > buf_channels ;
if ( plugin - > access = = SNDRV_PCM_ACCESS_RW_INTERLEAVED ) {
for ( channel = 0 ; channel < format - > channels ; channel + + , c + + ) {
c - > frames = frames ;
c - > enabled = 1 ;
c - > wanted = 0 ;
c - > area . addr = plugin - > buf ;
c - > area . first = channel * width ;
c - > area . step = format - > channels * width ;
}
} else if ( plugin - > access = = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) {
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( size % format - > channels ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
size / = format - > channels ;
for ( channel = 0 ; channel < format - > channels ; channel + + , c + + ) {
c - > frames = frames ;
c - > enabled = 1 ;
c - > wanted = 0 ;
c - > area . addr = plugin - > buf + ( channel * size ) ;
c - > area . first = 0 ;
c - > area . step = width ;
}
} else
return - EINVAL ;
return 0 ;
}
2005-11-17 14:01:49 +01:00
int snd_pcm_plug_alloc ( struct snd_pcm_substream * plug , snd_pcm_uframes_t frames )
2005-04-16 15:20:36 -07:00
{
int err ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! snd_pcm_plug_first ( plug ) ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
if ( snd_pcm_plug_stream ( plug ) = = SNDRV_PCM_STREAM_PLAYBACK ) {
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin = snd_pcm_plug_first ( plug ) ;
2005-04-16 15:20:36 -07:00
while ( plugin - > next ) {
if ( plugin - > dst_frames )
frames = plugin - > dst_frames ( plugin , frames ) ;
2020-03-12 16:57:30 +01:00
if ( ( snd_pcm_sframes_t ) frames < = 0 )
2008-08-08 17:09:09 +02:00
return - ENXIO ;
2005-04-16 15:20:36 -07:00
plugin = plugin - > next ;
err = snd_pcm_plugin_alloc ( plugin , frames ) ;
if ( err < 0 )
return err ;
}
} else {
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin = snd_pcm_plug_last ( plug ) ;
2005-04-16 15:20:36 -07:00
while ( plugin - > prev ) {
if ( plugin - > src_frames )
frames = plugin - > src_frames ( plugin , frames ) ;
2020-03-12 16:57:30 +01:00
if ( ( snd_pcm_sframes_t ) frames < = 0 )
2008-08-08 17:09:09 +02:00
return - ENXIO ;
2005-04-16 15:20:36 -07:00
plugin = plugin - > prev ;
err = snd_pcm_plugin_alloc ( plugin , frames ) ;
if ( err < 0 )
return err ;
}
}
return 0 ;
}
2005-11-17 14:01:49 +01:00
snd_pcm_sframes_t snd_pcm_plugin_client_channels ( struct snd_pcm_plugin * plugin ,
2005-04-16 15:20:36 -07:00
snd_pcm_uframes_t frames ,
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_channel * * channels )
2005-04-16 15:20:36 -07:00
{
* channels = plugin - > buf_channels ;
return frames ;
}
2005-11-17 14:01:49 +01:00
int snd_pcm_plugin_build ( struct snd_pcm_substream * plug ,
2005-04-16 15:20:36 -07:00
const char * name ,
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_format * src_format ,
struct snd_pcm_plugin_format * dst_format ,
2005-04-16 15:20:36 -07:00
size_t extra ,
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * * ret )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin ;
2005-04-16 15:20:36 -07:00
unsigned int channels ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! plug ) )
return - ENXIO ;
if ( snd_BUG_ON ( ! src_format | | ! dst_format ) )
return - ENXIO ;
2005-09-09 14:20:23 +02:00
plugin = kzalloc ( sizeof ( * plugin ) + extra , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( plugin = = NULL )
return - ENOMEM ;
plugin - > name = name ;
plugin - > plug = plug ;
plugin - > stream = snd_pcm_plug_stream ( plug ) ;
plugin - > access = SNDRV_PCM_ACCESS_RW_INTERLEAVED ;
plugin - > src_format = * src_format ;
plugin - > src_width = snd_pcm_format_physical_width ( src_format - > format ) ;
2008-08-08 17:09:09 +02:00
snd_BUG_ON ( plugin - > src_width < = 0 ) ;
2005-04-16 15:20:36 -07:00
plugin - > dst_format = * dst_format ;
plugin - > dst_width = snd_pcm_format_physical_width ( dst_format - > format ) ;
2008-08-08 17:09:09 +02:00
snd_BUG_ON ( plugin - > dst_width < = 0 ) ;
2005-04-16 15:20:36 -07:00
if ( plugin - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
channels = src_format - > channels ;
else
channels = dst_format - > channels ;
plugin - > buf_channels = kcalloc ( channels , sizeof ( * plugin - > buf_channels ) , GFP_KERNEL ) ;
if ( plugin - > buf_channels = = NULL ) {
snd_pcm_plugin_free ( plugin ) ;
return - ENOMEM ;
}
plugin - > client_channels = snd_pcm_plugin_client_channels ;
* ret = plugin ;
return 0 ;
}
2005-11-17 14:01:49 +01:00
int snd_pcm_plugin_free ( struct snd_pcm_plugin * plugin )
2005-04-16 15:20:36 -07:00
{
if ( ! plugin )
return 0 ;
if ( plugin - > private_free )
plugin - > private_free ( plugin ) ;
kfree ( plugin - > buf_channels ) ;
2018-11-09 11:59:45 +01:00
kvfree ( plugin - > buf ) ;
2005-04-16 15:20:36 -07:00
kfree ( plugin ) ;
return 0 ;
}
2020-03-09 19:58:55 +01:00
static snd_pcm_sframes_t calc_dst_frames ( struct snd_pcm_substream * plug ,
2020-04-03 09:38:18 +02:00
snd_pcm_sframes_t frames ,
bool check_size )
2005-04-16 15:20:36 -07:00
{
2020-03-09 19:58:55 +01:00
struct snd_pcm_plugin * plugin , * plugin_next ;
2005-04-16 15:20:36 -07:00
2020-03-09 19:58:55 +01:00
plugin = snd_pcm_plug_first ( plug ) ;
while ( plugin & & frames > 0 ) {
plugin_next = plugin - > next ;
2020-04-24 21:38:43 +02:00
if ( check_size & & plugin - > buf_frames & &
frames > plugin - > buf_frames )
frames = plugin - > buf_frames ;
2020-03-09 19:58:55 +01:00
if ( plugin - > dst_frames ) {
frames = plugin - > dst_frames ( plugin , frames ) ;
if ( frames < 0 )
return frames ;
2005-04-16 15:20:36 -07:00
}
2020-03-09 19:58:55 +01:00
plugin = plugin_next ;
}
return frames ;
}
static snd_pcm_sframes_t calc_src_frames ( struct snd_pcm_substream * plug ,
2020-04-03 09:38:18 +02:00
snd_pcm_sframes_t frames ,
bool check_size )
2020-03-09 19:58:55 +01:00
{
struct snd_pcm_plugin * plugin , * plugin_prev ;
plugin = snd_pcm_plug_last ( plug ) ;
while ( plugin & & frames > 0 ) {
plugin_prev = plugin - > prev ;
if ( plugin - > src_frames ) {
frames = plugin - > src_frames ( plugin , frames ) ;
if ( frames < 0 )
return frames ;
2005-04-16 15:20:36 -07:00
}
2020-04-24 21:38:43 +02:00
if ( check_size & & plugin - > buf_frames & &
frames > plugin - > buf_frames )
frames = plugin - > buf_frames ;
2020-03-09 19:58:55 +01:00
plugin = plugin_prev ;
}
return frames ;
}
snd_pcm_sframes_t snd_pcm_plug_client_size ( struct snd_pcm_substream * plug , snd_pcm_uframes_t drv_frames )
{
if ( snd_BUG_ON ( ! plug ) )
return - ENXIO ;
switch ( snd_pcm_plug_stream ( plug ) ) {
case SNDRV_PCM_STREAM_PLAYBACK :
2020-04-03 09:38:18 +02:00
return calc_src_frames ( plug , drv_frames , false ) ;
2020-03-09 19:58:55 +01:00
case SNDRV_PCM_STREAM_CAPTURE :
2020-04-03 09:38:18 +02:00
return calc_dst_frames ( plug , drv_frames , false ) ;
2020-03-09 19:58:55 +01:00
default :
2005-04-16 15:20:36 -07:00
snd_BUG ( ) ;
2020-03-09 19:58:55 +01:00
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
}
2005-11-17 14:01:49 +01:00
snd_pcm_sframes_t snd_pcm_plug_slave_size ( struct snd_pcm_substream * plug , snd_pcm_uframes_t clt_frames )
2005-04-16 15:20:36 -07:00
{
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! plug ) )
return - ENXIO ;
2020-03-09 19:58:55 +01:00
switch ( snd_pcm_plug_stream ( plug ) ) {
case SNDRV_PCM_STREAM_PLAYBACK :
2020-04-03 09:38:18 +02:00
return calc_dst_frames ( plug , clt_frames , false ) ;
2020-03-09 19:58:55 +01:00
case SNDRV_PCM_STREAM_CAPTURE :
2020-04-03 09:38:18 +02:00
return calc_src_frames ( plug , clt_frames , false ) ;
2020-03-09 19:58:55 +01:00
default :
2005-04-16 15:20:36 -07:00
snd_BUG ( ) ;
2020-03-09 19:58:55 +01:00
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
}
2017-05-17 08:48:19 +09:00
static int snd_pcm_plug_formats ( const struct snd_mask * mask ,
snd_pcm_format_t format )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_mask formats = * mask ;
2005-04-16 15:20:36 -07:00
u64 linfmts = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
2007-08-08 16:49:08 +02:00
SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
2005-04-16 15:20:36 -07:00
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE ) ;
2011-02-14 11:00:47 +01:00
snd_mask_set ( & formats , ( __force int ) SNDRV_PCM_FORMAT_MU_LAW ) ;
2005-04-16 15:20:36 -07:00
2018-07-25 23:00:55 +02:00
if ( formats . bits [ 0 ] & lower_32_bits ( linfmts ) )
formats . bits [ 0 ] | = lower_32_bits ( linfmts ) ;
if ( formats . bits [ 1 ] & upper_32_bits ( linfmts ) )
formats . bits [ 1 ] | = upper_32_bits ( linfmts ) ;
2011-02-14 11:00:47 +01:00
return snd_mask_test ( & formats , ( __force int ) format ) ;
2005-04-16 15:20:36 -07:00
}
2020-01-05 15:47:55 +01:00
static const snd_pcm_format_t preferred_formats [ ] = {
2005-04-16 15:20:36 -07:00
SNDRV_PCM_FORMAT_S16_LE ,
SNDRV_PCM_FORMAT_S16_BE ,
SNDRV_PCM_FORMAT_U16_LE ,
SNDRV_PCM_FORMAT_U16_BE ,
2007-08-08 16:49:08 +02:00
SNDRV_PCM_FORMAT_S24_3LE ,
SNDRV_PCM_FORMAT_S24_3BE ,
SNDRV_PCM_FORMAT_U24_3LE ,
SNDRV_PCM_FORMAT_U24_3BE ,
2005-04-16 15:20:36 -07:00
SNDRV_PCM_FORMAT_S24_LE ,
SNDRV_PCM_FORMAT_S24_BE ,
SNDRV_PCM_FORMAT_U24_LE ,
SNDRV_PCM_FORMAT_U24_BE ,
SNDRV_PCM_FORMAT_S32_LE ,
SNDRV_PCM_FORMAT_S32_BE ,
SNDRV_PCM_FORMAT_U32_LE ,
SNDRV_PCM_FORMAT_U32_BE ,
SNDRV_PCM_FORMAT_S8 ,
SNDRV_PCM_FORMAT_U8
} ;
2011-02-14 11:00:47 +01:00
snd_pcm_format_t snd_pcm_plug_slave_format ( snd_pcm_format_t format ,
2017-05-17 08:48:19 +09:00
const struct snd_mask * format_mask )
2005-04-16 15:20:36 -07:00
{
2007-08-08 16:49:08 +02:00
int i ;
2011-02-14 11:00:47 +01:00
if ( snd_mask_test ( format_mask , ( __force int ) format ) )
2005-04-16 15:20:36 -07:00
return format ;
2011-02-14 11:00:47 +01:00
if ( ! snd_pcm_plug_formats ( format_mask , format ) )
return ( __force snd_pcm_format_t ) - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( snd_pcm_format_linear ( format ) ) {
2007-08-08 16:49:08 +02:00
unsigned int width = snd_pcm_format_width ( format ) ;
int unsignd = snd_pcm_format_unsigned ( format ) > 0 ;
int big = snd_pcm_format_big_endian ( format ) > 0 ;
unsigned int badness , best = - 1 ;
2011-02-14 11:00:47 +01:00
snd_pcm_format_t best_format = ( __force snd_pcm_format_t ) - 1 ;
2007-08-08 16:49:08 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( preferred_formats ) ; i + + ) {
2011-02-14 11:00:47 +01:00
snd_pcm_format_t f = preferred_formats [ i ] ;
2007-08-08 16:49:08 +02:00
unsigned int w ;
2011-02-14 11:00:47 +01:00
if ( ! snd_mask_test ( format_mask , ( __force int ) f ) )
2007-08-08 16:49:08 +02:00
continue ;
w = snd_pcm_format_width ( f ) ;
if ( w > = width )
badness = w - width ;
else
badness = width - w + 32 ;
badness + = snd_pcm_format_unsigned ( f ) ! = unsignd ;
badness + = snd_pcm_format_big_endian ( f ) ! = big ;
if ( badness < best ) {
best_format = f ;
best = badness ;
2005-04-16 15:20:36 -07:00
}
}
2011-02-14 11:00:47 +01:00
if ( ( __force int ) best_format > = 0 )
return best_format ;
else
return ( __force snd_pcm_format_t ) - EINVAL ;
2005-04-16 15:20:36 -07:00
} else {
switch ( format ) {
case SNDRV_PCM_FORMAT_MU_LAW :
for ( i = 0 ; i < ARRAY_SIZE ( preferred_formats ) ; + + i ) {
2011-02-14 11:00:47 +01:00
snd_pcm_format_t format1 = preferred_formats [ i ] ;
if ( snd_mask_test ( format_mask , ( __force int ) format1 ) )
2005-04-16 15:20:36 -07:00
return format1 ;
}
2020-07-08 15:32:36 -05:00
fallthrough ;
2005-04-16 15:20:36 -07:00
default :
2011-02-14 11:00:47 +01:00
return ( __force snd_pcm_format_t ) - EINVAL ;
2005-04-16 15:20:36 -07:00
}
}
}
2005-11-17 14:01:49 +01:00
int snd_pcm_plug_format_plugins ( struct snd_pcm_substream * plug ,
struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_params * slave_params )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_format tmpformat ;
struct snd_pcm_plugin_format dstformat ;
struct snd_pcm_plugin_format srcformat ;
2011-02-14 11:00:47 +01:00
snd_pcm_access_t src_access , dst_access ;
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin = NULL ;
2005-04-16 15:20:36 -07:00
int err ;
int stream = snd_pcm_plug_stream ( plug ) ;
int slave_interleaved = ( params_channels ( slave_params ) = = 1 | |
params_access ( slave_params ) = = SNDRV_PCM_ACCESS_RW_INTERLEAVED ) ;
switch ( stream ) {
case SNDRV_PCM_STREAM_PLAYBACK :
dstformat . format = params_format ( slave_params ) ;
dstformat . rate = params_rate ( slave_params ) ;
dstformat . channels = params_channels ( slave_params ) ;
srcformat . format = params_format ( params ) ;
srcformat . rate = params_rate ( params ) ;
srcformat . channels = params_channels ( params ) ;
src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED ;
dst_access = ( slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) ;
break ;
case SNDRV_PCM_STREAM_CAPTURE :
dstformat . format = params_format ( params ) ;
dstformat . rate = params_rate ( params ) ;
dstformat . channels = params_channels ( params ) ;
srcformat . format = params_format ( slave_params ) ;
srcformat . rate = params_rate ( slave_params ) ;
srcformat . channels = params_channels ( slave_params ) ;
src_access = ( slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) ;
dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED ;
break ;
default :
snd_BUG ( ) ;
return - EINVAL ;
}
tmpformat = srcformat ;
pdprintf ( " srcformat: format=%i, rate=%i, channels=%i \n " ,
srcformat . format ,
srcformat . rate ,
srcformat . channels ) ;
pdprintf ( " dstformat: format=%i, rate=%i, channels=%i \n " ,
dstformat . format ,
dstformat . rate ,
dstformat . channels ) ;
/* Format change (linearization) */
2006-01-13 12:09:12 +01:00
if ( ! rate_match ( srcformat . rate , dstformat . rate ) & &
! snd_pcm_format_linear ( srcformat . format ) ) {
if ( srcformat . format ! = SNDRV_PCM_FORMAT_MU_LAW )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2006-01-13 12:09:12 +01:00
tmpformat . format = SNDRV_PCM_FORMAT_S16 ;
err = snd_pcm_plugin_build_mulaw ( plug ,
& srcformat , & tmpformat ,
& plugin ) ;
2005-04-16 15:20:36 -07:00
if ( err < 0 )
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
/* channels reduction */
if ( srcformat . channels > dstformat . channels ) {
tmpformat . channels = dstformat . channels ;
2006-01-13 12:09:12 +01:00
err = snd_pcm_plugin_build_route ( plug , & srcformat , & tmpformat , & plugin ) ;
2005-04-16 15:20:36 -07:00
pdprintf ( " channels reduction: src=%i, dst=%i returns %i \n " , srcformat . channels , tmpformat . channels , err ) ;
2006-01-13 12:09:12 +01:00
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
/* rate resampling */
if ( ! rate_match ( srcformat . rate , dstformat . rate ) ) {
2006-01-13 12:09:12 +01:00
if ( srcformat . format ! = SNDRV_PCM_FORMAT_S16 ) {
/* convert to S16 for resampling */
tmpformat . format = SNDRV_PCM_FORMAT_S16 ;
err = snd_pcm_plugin_build_linear ( plug ,
& srcformat , & tmpformat ,
& plugin ) ;
if ( err < 0 )
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
2005-04-16 15:20:36 -07:00
tmpformat . rate = dstformat . rate ;
err = snd_pcm_plugin_build_rate ( plug ,
& srcformat , & tmpformat ,
& plugin ) ;
pdprintf ( " rate down resampling: src=%i, dst=%i returns %i \n " , srcformat . rate , tmpformat . rate , err ) ;
2006-01-13 12:09:12 +01:00
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
/* format change */
if ( srcformat . format ! = dstformat . format ) {
tmpformat . format = dstformat . format ;
2006-01-20 17:13:45 +01:00
if ( srcformat . format = = SNDRV_PCM_FORMAT_MU_LAW | |
tmpformat . format = = SNDRV_PCM_FORMAT_MU_LAW ) {
2005-04-16 15:20:36 -07:00
err = snd_pcm_plugin_build_mulaw ( plug ,
& srcformat , & tmpformat ,
& plugin ) ;
}
else if ( snd_pcm_format_linear ( srcformat . format ) & &
snd_pcm_format_linear ( tmpformat . format ) ) {
err = snd_pcm_plugin_build_linear ( plug ,
& srcformat , & tmpformat ,
& plugin ) ;
}
else
return - EINVAL ;
pdprintf ( " format change: src=%i, dst=%i returns %i \n " , srcformat . format , tmpformat . format , err ) ;
if ( err < 0 )
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
2006-01-13 12:09:12 +01:00
/* channels extension */
if ( srcformat . channels < dstformat . channels ) {
tmpformat . channels = dstformat . channels ;
err = snd_pcm_plugin_build_route ( plug , & srcformat , & tmpformat , & plugin ) ;
pdprintf ( " channels extension: src=%i, dst=%i returns %i \n " , srcformat . channels , tmpformat . channels , err ) ;
if ( err < 0 )
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
srcformat = tmpformat ;
src_access = dst_access ;
}
2005-04-16 15:20:36 -07:00
/* de-interleave */
if ( src_access ! = dst_access ) {
err = snd_pcm_plugin_build_copy ( plug ,
& srcformat ,
& tmpformat ,
& plugin ) ;
pdprintf ( " interleave change (copy: returns %i) \n " , err ) ;
if ( err < 0 )
return err ;
err = snd_pcm_plugin_append ( plugin ) ;
if ( err < 0 ) {
snd_pcm_plugin_free ( plugin ) ;
return err ;
}
}
return 0 ;
}
2005-11-17 14:01:49 +01:00
snd_pcm_sframes_t snd_pcm_plug_client_channels_buf ( struct snd_pcm_substream * plug ,
2005-04-16 15:20:36 -07:00
char * buf ,
snd_pcm_uframes_t count ,
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin_channel * * channels )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin ;
struct snd_pcm_plugin_channel * v ;
struct snd_pcm_plugin_format * format ;
2005-04-16 15:20:36 -07:00
int width , nchannels , channel ;
int stream = snd_pcm_plug_stream ( plug ) ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! buf ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
if ( stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
plugin = snd_pcm_plug_first ( plug ) ;
format = & plugin - > src_format ;
} else {
plugin = snd_pcm_plug_last ( plug ) ;
format = & plugin - > dst_format ;
}
v = plugin - > buf_channels ;
* channels = v ;
2021-06-08 16:05:29 +02:00
width = snd_pcm_format_physical_width ( format - > format ) ;
if ( width < 0 )
2005-04-16 15:20:36 -07:00
return width ;
nchannels = format - > channels ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( plugin - > access ! = SNDRV_PCM_ACCESS_RW_INTERLEAVED & &
format - > channels > 1 ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
for ( channel = 0 ; channel < nchannels ; channel + + , v + + ) {
v - > frames = count ;
v - > enabled = 1 ;
v - > wanted = ( stream = = SNDRV_PCM_STREAM_CAPTURE ) ;
v - > area . addr = buf ;
v - > area . first = channel * width ;
v - > area . step = nchannels * width ;
}
return count ;
}
2005-11-17 14:01:49 +01:00
snd_pcm_sframes_t snd_pcm_plug_write_transfer ( struct snd_pcm_substream * plug , struct snd_pcm_plugin_channel * src_channels , snd_pcm_uframes_t size )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin , * next ;
struct snd_pcm_plugin_channel * dst_channels ;
2005-04-16 15:20:36 -07:00
int err ;
snd_pcm_sframes_t frames = size ;
plugin = snd_pcm_plug_first ( plug ) ;
2018-01-04 16:39:27 +01:00
while ( plugin ) {
if ( frames < = 0 )
return frames ;
2021-06-08 16:05:29 +02:00
next = plugin - > next ;
if ( next ) {
2005-04-16 15:20:36 -07:00
snd_pcm_sframes_t frames1 = frames ;
2018-01-04 16:39:27 +01:00
if ( plugin - > dst_frames ) {
2005-04-16 15:20:36 -07:00
frames1 = plugin - > dst_frames ( plugin , frames ) ;
2018-01-04 16:39:27 +01:00
if ( frames1 < = 0 )
return frames1 ;
}
2021-06-08 16:05:29 +02:00
err = next - > client_channels ( next , frames1 , & dst_channels ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
if ( err ! = frames1 ) {
frames = err ;
2018-01-04 16:39:27 +01:00
if ( plugin - > src_frames ) {
2005-04-16 15:20:36 -07:00
frames = plugin - > src_frames ( plugin , frames1 ) ;
2018-01-04 16:39:27 +01:00
if ( frames < = 0 )
return frames ;
}
2005-04-16 15:20:36 -07:00
}
} else
dst_channels = NULL ;
pdprintf ( " write plugin: %s, %li \n " , plugin - > name , frames ) ;
2021-06-08 16:05:29 +02:00
frames = plugin - > transfer ( plugin , src_channels , dst_channels , frames ) ;
if ( frames < 0 )
2005-04-16 15:20:36 -07:00
return frames ;
src_channels = dst_channels ;
plugin = next ;
}
2020-04-03 09:38:18 +02:00
return calc_src_frames ( plug , frames , true ) ;
2005-04-16 15:20:36 -07:00
}
2005-11-17 14:01:49 +01:00
snd_pcm_sframes_t snd_pcm_plug_read_transfer ( struct snd_pcm_substream * plug , struct snd_pcm_plugin_channel * dst_channels_final , snd_pcm_uframes_t size )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:01:49 +01:00
struct snd_pcm_plugin * plugin , * next ;
struct snd_pcm_plugin_channel * src_channels , * dst_channels ;
2005-04-16 15:20:36 -07:00
snd_pcm_sframes_t frames = size ;
int err ;
2020-04-03 09:38:18 +02:00
frames = calc_src_frames ( plug , frames , true ) ;
2005-04-16 15:20:36 -07:00
if ( frames < 0 )
return frames ;
src_channels = NULL ;
plugin = snd_pcm_plug_first ( plug ) ;
while ( plugin & & frames > 0 ) {
2021-06-08 16:05:29 +02:00
next = plugin - > next ;
if ( next ) {
err = plugin - > client_channels ( plugin , frames , & dst_channels ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
return err ;
frames = err ;
} else {
dst_channels = dst_channels_final ;
}
pdprintf ( " read plugin: %s, %li \n " , plugin - > name , frames ) ;
2021-06-08 16:05:29 +02:00
frames = plugin - > transfer ( plugin , src_channels , dst_channels , frames ) ;
if ( frames < 0 )
2005-04-16 15:20:36 -07:00
return frames ;
plugin = next ;
src_channels = dst_channels ;
}
return frames ;
}
2005-11-17 14:01:49 +01:00
int snd_pcm_area_silence ( const struct snd_pcm_channel_area * dst_area , size_t dst_offset ,
2011-02-14 11:00:47 +01:00
size_t samples , snd_pcm_format_t format )
2005-04-16 15:20:36 -07:00
{
/* FIXME: sub byte resolution and odd dst_offset */
unsigned char * dst ;
unsigned int dst_step ;
int width ;
const unsigned char * silence ;
if ( ! dst_area - > addr )
return 0 ;
dst = dst_area - > addr + ( dst_area - > first + dst_area - > step * dst_offset ) / 8 ;
width = snd_pcm_format_physical_width ( format ) ;
if ( width < = 0 )
return - EINVAL ;
if ( dst_area - > step = = ( unsigned int ) width & & width > = 8 )
return snd_pcm_format_set_silence ( format , dst , samples ) ;
silence = snd_pcm_format_silence_64 ( format ) ;
if ( ! silence )
return - EINVAL ;
dst_step = dst_area - > step / 8 ;
if ( width = = 4 ) {
/* Ima ADPCM */
int dstbit = dst_area - > first % 8 ;
int dstbit_step = dst_area - > step % 8 ;
while ( samples - - > 0 ) {
if ( dstbit )
* dst & = 0xf0 ;
else
* dst & = 0x0f ;
dst + = dst_step ;
dstbit + = dstbit_step ;
if ( dstbit = = 8 ) {
dst + + ;
dstbit = 0 ;
}
}
} else {
width / = 8 ;
while ( samples - - > 0 ) {
memcpy ( dst , silence , width ) ;
dst + = dst_step ;
}
}
return 0 ;
}
2005-11-17 14:01:49 +01:00
int snd_pcm_area_copy ( const struct snd_pcm_channel_area * src_area , size_t src_offset ,
const struct snd_pcm_channel_area * dst_area , size_t dst_offset ,
2011-02-14 11:00:47 +01:00
size_t samples , snd_pcm_format_t format )
2005-04-16 15:20:36 -07:00
{
/* FIXME: sub byte resolution and odd dst_offset */
char * src , * dst ;
int width ;
int src_step , dst_step ;
src = src_area - > addr + ( src_area - > first + src_area - > step * src_offset ) / 8 ;
if ( ! src_area - > addr )
return snd_pcm_area_silence ( dst_area , dst_offset , samples , format ) ;
dst = dst_area - > addr + ( dst_area - > first + dst_area - > step * dst_offset ) / 8 ;
if ( ! dst_area - > addr )
return 0 ;
width = snd_pcm_format_physical_width ( format ) ;
if ( width < = 0 )
return - EINVAL ;
if ( src_area - > step = = ( unsigned int ) width & &
dst_area - > step = = ( unsigned int ) width & & width > = 8 ) {
size_t bytes = samples * width / 8 ;
memcpy ( dst , src , bytes ) ;
return 0 ;
}
src_step = src_area - > step / 8 ;
dst_step = dst_area - > step / 8 ;
if ( width = = 4 ) {
/* Ima ADPCM */
int srcbit = src_area - > first % 8 ;
int srcbit_step = src_area - > step % 8 ;
int dstbit = dst_area - > first % 8 ;
int dstbit_step = dst_area - > step % 8 ;
while ( samples - - > 0 ) {
unsigned char srcval ;
if ( srcbit )
srcval = * src & 0x0f ;
else
srcval = ( * src & 0xf0 ) > > 4 ;
if ( dstbit )
* dst = ( * dst & 0xf0 ) | srcval ;
else
* dst = ( * dst & 0x0f ) | ( srcval < < 4 ) ;
src + = src_step ;
srcbit + = srcbit_step ;
if ( srcbit = = 8 ) {
src + + ;
srcbit = 0 ;
}
dst + = dst_step ;
dstbit + = dstbit_step ;
if ( dstbit = = 8 ) {
dst + + ;
dstbit = 0 ;
}
}
} else {
width / = 8 ;
while ( samples - - > 0 ) {
memcpy ( dst , src , width ) ;
src + = src_step ;
dst + = dst_step ;
}
}
return 0 ;
}