2014-12-10 11:44:55 -05:00
/* g++ -o sftest sftest.cc `pkg-config --cflags --libs sndfile` `pkg-config --cflags --libs glibmm-2.4` */
# include <vector>
# include <iostream>
# include <iomanip>
# include <sstream>
# include <cstdlib>
# include <unistd.h>
# include <sndfile.h>
# include <stdint.h>
# include <string.h>
# include <getopt.h>
2014-12-10 13:35:57 -05:00
# include <fcntl.h>
2014-12-10 11:44:55 -05:00
# include <glibmm/miscutils.h>
using namespace std ;
SF_INFO format_info ;
float * data = 0 ;
bool with_sync = false ;
int
write_one ( SNDFILE * sf , uint32_t nframes )
{
if ( sf_write_float ( sf , ( float * ) data , nframes ) ! = nframes ) {
return - 1 ;
}
if ( with_sync ) {
sf_write_sync ( sf ) ;
}
return 0 ;
}
void
usage ( )
{
cout < < " sftest [ -f HEADER-FORMAT ] [ -F DATA-FORMAT ] [ -r SAMPLERATE ] [ -n NFILES ] [ -b BLOCKSIZE ] [ -s ] " < < endl ;
cout < < " \t HEADER-FORMAT is one of: " < < endl
< < " \t \t WAV " < < endl
< < " \t \t CAF " < < endl
< < " \t \t W64 " < < endl ;
cout < < " \t DATA-FORMAT is one of: " < < endl
< < " \t \t FLOAT " < < endl
< < " \t \t 32 " < < endl
< < " \t \t 24 " < < endl
< < " \t \t 16 " < < endl ;
}
int
main ( int argc , char * argv [ ] )
{
vector < SNDFILE * > sndfiles ;
uint32_t sample_size ;
2014-12-10 13:35:57 -05:00
char optstring [ ] = " f:r:F:n:c:b:sD " ;
2014-12-10 11:44:55 -05:00
int channels , samplerate ;
char const * suffix = " .wav " ;
char const * header_format = " wav " ;
char const * data_format = " float " ;
uint32_t block_size = 64 * 1024 ;
uint32_t nfiles = 100 ;
2014-12-10 13:35:57 -05:00
bool direct = false ;
2014-12-10 11:44:55 -05:00
const struct option longopts [ ] = {
{ " header-format " , 1 , 0 , ' f ' } ,
{ " data-format " , 1 , 0 , ' F ' } ,
{ " rate " , 1 , 0 , ' r ' } ,
{ " nfiles " , 1 , 0 , ' n ' } ,
{ " blocksize " , 1 , 0 , ' b ' } ,
{ " channels " , 1 , 0 , ' c ' } ,
{ " sync " , 0 , 0 , ' s ' } ,
2014-12-10 13:35:57 -05:00
{ " direct " , 0 , 0 , ' D ' } ,
2014-12-10 11:44:55 -05:00
{ 0 , 0 , 0 , 0 }
} ;
int option_index = 0 ;
int c = 0 ;
while ( 1 ) {
if ( ( c = getopt_long ( argc , argv , optstring , longopts , & option_index ) ) = = - 1 ) {
break ;
}
switch ( c ) {
case ' f ' :
header_format = optarg ;
break ;
case ' F ' :
data_format = optarg ;
break ;
case ' r ' :
samplerate = atoi ( optarg ) ;
break ;
case ' n ' :
nfiles = atoi ( optarg ) ;
break ;
case ' c ' :
channels = atoi ( optarg ) ;
case ' b ' :
block_size = atoi ( optarg ) ;
break ;
case ' s ' :
with_sync = true ;
break ;
2014-12-10 13:35:57 -05:00
case ' D ' :
direct = true ;
break ;
2014-12-10 11:44:55 -05:00
default :
usage ( ) ;
return 0 ;
}
}
/* setup file format */
memset ( & format_info , 0 , sizeof ( format_info ) ) ;
if ( samplerate = = 0 | | nfiles = = 0 | | block_size = = 0 | | channels = = 0 ) {
usage ( ) ;
return 1 ;
}
format_info . samplerate = samplerate ;
format_info . channels = channels ;
if ( strcasecmp ( header_format , " wav " ) = = 0 ) {
format_info . format | = SF_FORMAT_WAV ;
suffix = " .wav " ;
} else if ( strcasecmp ( header_format , " caf " ) = = 0 ) {
format_info . format | = SF_FORMAT_CAF ;
suffix = " .caf " ;
} else if ( strcasecmp ( header_format , " w64 " ) = = 0 ) {
format_info . format | = SF_FORMAT_W64 ;
suffix = " .w64 " ;
} else {
usage ( ) ;
return 0 ;
}
if ( strcasecmp ( data_format , " float " ) = = 0 ) {
format_info . format | = SF_FORMAT_FLOAT ;
sample_size = sizeof ( float ) ;
} else if ( strcasecmp ( data_format , " 32 " ) = = 0 ) {
format_info . format | = SF_FORMAT_PCM_32 ;
sample_size = 4 ;
} else if ( strcasecmp ( data_format , " 24 " ) = = 0 ) {
format_info . format | = SF_FORMAT_PCM_24 ;
sample_size = 3 ;
} else if ( strcasecmp ( data_format , " 16 " ) = = 0 ) {
format_info . format | = SF_FORMAT_PCM_16 ;
sample_size = 2 ;
} else {
usage ( ) ;
return 0 ;
}
char tmpdirname [ ] = " sftest-XXXXXX " ;
g_mkdtemp ( tmpdirname ) ;
for ( uint32_t n = 0 ; n < nfiles ; + + n ) {
SNDFILE * sf ;
string path ;
stringstream ss ;
ss < < " sf- " ;
ss < < n ;
ss < < suffix ;
path = Glib : : build_filename ( tmpdirname , ss . str ( ) ) ;
2014-12-10 13:35:57 -05:00
int flags = O_RDWR | O_CREAT | O_TRUNC ;
# ifndef __APPLE__
if ( direct ) {
flags | = O_DIRECT ;
}
# endif
int fd = open ( path . c_str ( ) , flags , 0644 ) ;
if ( fd < 0 ) {
cerr < < " Could not open file # " < < n < < " @ " < < path < < endl ;
return 1 ;
}
# ifdef __APPLE__
if ( direct ) {
/* Apple man pages say only that it returns "a value other than -1 on success",
which probably means zero , but you just can ' t be too careful with
those guys .
*/
if ( fcntl ( fd , F_NOCACHE , 1 ) = = - 1 ) {
cerr < < " Cannot set F_NOCACHE on file # " < < n < < endl ;
}
}
# endif
if ( ( sf = sf_open_fd ( fd , SFM_RDWR , & format_info , true ) ) = = 0 ) {
2014-12-10 11:44:55 -05:00
cerr < < " Could not open file # " < < n < < " @ " < < path < < endl ;
return 1 ;
}
sndfiles . push_back ( sf ) ;
}
2014-12-10 13:35:57 -05:00
cout < < nfiles < < " files are in " < < tmpdirname < < " all used " < < ( direct ? " without " : " with " ) < < " OS buffer cache " < < endl ;
2014-12-10 11:44:55 -05:00
cout < < " Format is " < < suffix < < ' ' < < channels < < " channel " < < ( channels > 1 ? " s " : " " ) < < " written in chunks of " < < block_size < < " frames, synced ? " < < ( with_sync ? " yes " : " no " ) < < endl ;
data = new float [ block_size * channels ] ;
uint64_t written = 0 ;
while ( true ) {
gint64 before ;
before = g_get_monotonic_time ( ) ;
for ( vector < SNDFILE * > : : iterator s = sndfiles . begin ( ) ; s ! = sndfiles . end ( ) ; + + s ) {
if ( write_one ( * s , block_size ) ) {
cerr < < " Write failed for file # " < < distance ( sndfiles . begin ( ) , s ) < < endl ;
return 1 ;
}
}
written + = block_size ;
gint64 elapsed = g_get_monotonic_time ( ) - before ;
double bandwidth = ( sndfiles . size ( ) * block_size * channels * sample_size ) / ( elapsed / 1000000.0 ) ;
double data_minutes = written / ( double ) ( 60.0 * 48000.0 ) ;
const double data_rate = sndfiles . size ( ) * channels * sample_size * samplerate ;
stringstream ds ;
ds < < setprecision ( 1 ) < < data_minutes ;
cout < < " BW @ " < < written < < " frames ( " < < ds . str ( ) < < " minutes) = " < < ( bandwidth / 1048576.0 ) < < " MB/sec " < < bandwidth / data_rate < < " x faster than necessary " < < endl ;
}
return 0 ;
}