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>
2014-12-10 13:55:53 -05:00
# include <cerrno>
2014-12-10 11:44:55 -05:00
# 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>
2015-01-23 12:25:14 -05:00
# include <signal.h>
# include <float.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 ;
2015-01-23 12:25:14 -05:00
bool keep_writing = true ;
void
signal_handler ( int )
{
keep_writing = false ;
}
2014-12-10 11:44:55 -05:00
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 ( )
{
2015-01-23 12:25:14 -05:00
cout < < " sftest [ -f HEADER-FORMAT ] [ -F DATA-FORMAT ] [ -r SAMPLERATE ] [ -n NFILES ] [ -b BLOCKSIZE ] [ -s ] " ;
# ifdef __APPLE__
cout < < " [ -D ] " ;
# endif
cout < < endl ;
2014-12-10 11:44:55 -05:00
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 ;
2015-01-23 12:25:14 -05:00
char optstring [ ] = " f:r:F:n:c:b:sd:qS: "
# ifdef __APPLE__
" D "
# endif
;
int channels = 1 ;
int samplerate = 48000 ;
2014-12-10 11:44:55 -05:00
char const * suffix = " .wav " ;
char const * header_format = " wav " ;
char const * data_format = " float " ;
2015-01-23 12:25:14 -05:00
size_t filesize = 10 * 1048576 ;
2014-12-10 11:44:55 -05:00
uint32_t block_size = 64 * 1024 ;
uint32_t nfiles = 100 ;
2015-01-23 12:25:14 -05:00
string dirname = " /tmp " ;
bool quiet = false ;
# ifdef __APPLE__
2014-12-10 13:35:57 -05:00
bool direct = false ;
2015-01-23 12:25:14 -05:00
# endif
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 ' } ,
2015-01-23 12:25:14 -05:00
{ " dirname " , 1 , 0 , ' d ' } ,
{ " quiet " , 0 , 0 , ' q ' } ,
{ " filesize " , 1 , 0 , ' S ' } ,
# ifdef __APPLE__
2014-12-10 13:35:57 -05:00
{ " direct " , 0 , 0 , ' D ' } ,
2015-01-23 12:25:14 -05:00
# endif
2014-12-10 11:44:55 -05:00
{ 0 , 0 , 0 , 0 }
} ;
int option_index = 0 ;
int c = 0 ;
2015-10-05 10:17:49 -04:00
2014-12-10 11:44:55 -05:00
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 ;
2015-01-23 12:25:14 -05:00
case ' S ' :
filesize = atoi ( optarg ) ;
break ;
# ifdef __APPLE__
2014-12-10 13:35:57 -05:00
case ' D ' :
direct = true ;
break ;
2015-01-23 12:25:14 -05:00
# endif
case ' d ' :
dirname = optarg ;
break ;
case ' q ' :
quiet = 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 ;
2015-10-05 10:17:49 -04:00
2014-12-10 11:44:55 -05:00
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 ;
}
2015-10-05 10:17:49 -04:00
2015-01-23 12:25:14 -05:00
string tmpdirname = Glib : : build_filename ( dirname , " sftest " ) ;
if ( g_mkdir_with_parents ( tmpdirname . c_str ( ) , 0755 ) ) {
cerr < < " Cannot create output directory \n " ;
return 1 ;
}
2014-12-10 11:44:55 -05:00
for ( uint32_t n = 0 ; n < nfiles ; + + n ) {
SNDFILE * sf ;
string path ;
stringstream ss ;
ss < < " sf- " ;
ss < < n ;
ss < < suffix ;
2015-10-05 10:17:49 -04:00
2014-12-10 11:44:55 -05:00
path = Glib : : build_filename ( tmpdirname , ss . str ( ) ) ;
2014-12-10 13:35:57 -05:00
int flags = O_RDWR | O_CREAT | O_TRUNC ;
int fd = open ( path . c_str ( ) , flags , 0644 ) ;
if ( fd < 0 ) {
2014-12-10 13:55:53 -05:00
cerr < < " Could not open file # " < < n < < " @ " < < path < < " ( " < < strerror ( errno ) < < " ) " < < endl ;
2014-12-10 13:35:57 -05:00
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 13:55:53 -05:00
cerr < < " Could not open SNDFILE # " < < n < < " @ " < < path < < " ( " < < sf_strerror ( 0 ) < < " ) " < < endl ;
2014-12-10 11:44:55 -05:00
return 1 ;
}
sndfiles . push_back ( sf ) ;
}
2015-01-23 12:25:14 -05:00
if ( ! quiet ) {
cout < < nfiles < < " files are in " < < tmpdirname ;
# ifdef __APPLE__
cout < < " all used " < < ( direct ? " without " : " with " ) < < " OS buffer cache " ;
# endif
cout < < endl ;
2017-09-18 12:39:17 -04:00
cout < < " Format is " < < suffix < < ' ' < < channels < < " channel " < < ( channels > 1 ? " s " : " " ) < < " written in chunks of " < < block_size < < " samples, synced ? " < < ( with_sync ? " yes " : " no " ) < < endl ;
2015-01-23 12:25:14 -05:00
}
2015-10-05 10:17:49 -04:00
2014-12-10 11:44:55 -05:00
data = new float [ block_size * channels ] ;
uint64_t written = 0 ;
2015-01-23 12:25:14 -05:00
signal ( SIGINT , signal_handler ) ;
signal ( SIGSTOP , signal_handler ) ;
double max_bandwidth = 0 ;
double min_bandwidth = DBL_MAX ;
while ( keep_writing & & written < filesize ) {
2014-12-10 11:44:55 -05:00
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 ;
2015-01-23 12:25:14 -05:00
max_bandwidth = max ( max_bandwidth , bandwidth ) ;
min_bandwidth = min ( min_bandwidth , bandwidth ) ;
2015-10-05 10:17:49 -04:00
2015-01-23 12:25:14 -05:00
if ( ! quiet ) {
2017-09-18 12:39:17 -04:00
cout < < " BW @ " < < written < < " samples ( " < < ds . str ( ) < < " minutes) = " < < ( bandwidth / 1048576.0 ) < < " MB/sec " < < bandwidth / data_rate < < " x faster than necessary " < < endl ;
2015-01-23 12:25:14 -05:00
}
2014-12-10 11:44:55 -05:00
}
2015-01-23 12:25:14 -05:00
cout < < " Max bandwidth = " < < max_bandwidth / 1048576.0 < < " MB/sec " < < endl ;
cout < < " Min bandwidth = " < < min_bandwidth / 1048576.0 < < " MB/sec " < < endl ;
if ( ! quiet ) {
cout < < " Closing files ... \n " ;
for ( vector < SNDFILE * > : : iterator s = sndfiles . begin ( ) ; s ! = sndfiles . end ( ) ; + + s ) {
sf_close ( * s ) ;
}
cout < < " Done. \n " ;
}
2015-10-05 10:17:49 -04:00
2014-12-10 11:44:55 -05:00
return 0 ;
}
2015-10-04 14:51:05 -04:00