1 /* g++ -o sftest sftest.cc `pkg-config --cflags --libs sndfile` `pkg-config --cflags --libs glibmm-2.4` */
19 #include <glibmm/miscutils.h>
25 bool with_sync = false;
26 bool keep_writing = true;
35 write_one (SNDFILE* sf, uint32_t nframes)
37 if (sf_write_float (sf, (float*) data, nframes) != nframes) {
51 cout << "sftest [ -f HEADER-FORMAT ] [ -F DATA-FORMAT ] [ -r SAMPLERATE ] [ -n NFILES ] [ -b BLOCKSIZE ] [ -s ]";
58 cout << "\tHEADER-FORMAT is one of:" << endl
62 cout << "\tDATA-FORMAT is one of:" << endl
63 << "\t\tFLOAT" << endl
70 main (int argc, char* argv[])
72 vector<SNDFILE*> sndfiles;
74 char optstring[] = "f:r:F:n:c:b:sd:qS:"
80 int samplerate = 48000;
81 char const *suffix = ".wav";
82 char const *header_format = "wav";
83 char const *data_format = "float";
84 size_t filesize = 10 * 1048576;
85 uint32_t block_size = 64 * 1024;
86 uint32_t nfiles = 100;
87 string dirname = "/tmp";
92 const struct option longopts[] = {
93 { "header-format", 1, 0, 'f' },
94 { "data-format", 1, 0, 'F' },
95 { "rate", 1, 0, 'r' },
96 { "nfiles", 1, 0, 'n' },
97 { "blocksize", 1, 0, 'b' },
98 { "channels", 1, 0, 'c' },
99 { "sync", 0, 0, 's' },
100 { "dirname", 1, 0, 'd' },
101 { "quiet", 0, 0, 'q' },
102 { "filesize", 1, 0, 'S' },
104 { "direct", 0, 0, 'D' },
109 int option_index = 0;
113 if ((c = getopt_long (argc, argv, optstring, longopts, &option_index)) == -1) {
119 header_format = optarg;
123 data_format = optarg;
127 samplerate = atoi (optarg);
131 nfiles = atoi (optarg);
135 channels = atoi (optarg);
138 block_size = atoi (optarg);
144 filesize = atoi (optarg);
163 /* setup file format */
164 memset (&format_info, 0, sizeof (format_info));
166 if (samplerate == 0 || nfiles == 0 || block_size == 0 || channels == 0) {
171 format_info.samplerate = samplerate;
172 format_info.channels = channels;
174 if (strcasecmp (header_format, "wav") == 0) {
175 format_info.format |= SF_FORMAT_WAV;
177 } else if (strcasecmp (header_format, "caf") == 0) {
178 format_info.format |= SF_FORMAT_CAF;
180 } else if (strcasecmp (header_format, "w64") == 0) {
181 format_info.format |= SF_FORMAT_W64;
188 if (strcasecmp (data_format, "float") == 0) {
189 format_info.format |= SF_FORMAT_FLOAT;
190 sample_size = sizeof (float);
191 } else if (strcasecmp (data_format, "32") == 0) {
192 format_info.format |= SF_FORMAT_PCM_32;
194 } else if (strcasecmp (data_format, "24") == 0) {
195 format_info.format |= SF_FORMAT_PCM_24;
197 } else if (strcasecmp (data_format, "16") == 0) {
198 format_info.format |= SF_FORMAT_PCM_16;
205 string tmpdirname = Glib::build_filename (dirname, "sftest");
206 if (g_mkdir_with_parents (tmpdirname.c_str(), 0755)) {
207 cerr << "Cannot create output directory\n";
211 for (uint32_t n = 0; n < nfiles; ++n) {
220 path = Glib::build_filename (tmpdirname, ss.str());
222 int flags = O_RDWR|O_CREAT|O_TRUNC;
223 int fd = open (path.c_str(), flags, 0644);
226 cerr << "Could not open file #" << n << " @ " << path << " (" << strerror (errno) << ")" << endl;
232 /* Apple man pages say only that it returns "a value other than -1 on success",
233 which probably means zero, but you just can't be too careful with
236 if (fcntl (fd, F_NOCACHE, 1) == -1) {
237 cerr << "Cannot set F_NOCACHE on file # " << n << endl;
241 if ((sf = sf_open_fd (fd, SFM_RDWR, &format_info, true)) == 0) {
242 cerr << "Could not open SNDFILE #" << n << " @ " << path << " (" << sf_strerror (0) << ")" << endl;
246 sndfiles.push_back (sf);
250 cout << nfiles << " files are in " << tmpdirname;
252 cout << " all used " << (direct ? "without" : "with") << " OS buffer cache";
255 cout << "Format is " << suffix << ' ' << channels << " channel" << (channels > 1 ? "s" : "") << " written in chunks of " << block_size << " frames, synced ? " << (with_sync ? "yes" : "no") << endl;
258 data = new float[block_size*channels];
259 uint64_t written = 0;
261 signal (SIGINT, signal_handler);
262 signal (SIGSTOP, signal_handler);
265 double max_bandwidth = 0;
266 double min_bandwidth = DBL_MAX;
268 while (keep_writing && written < filesize) {
270 before = g_get_monotonic_time();
271 for (vector<SNDFILE*>::iterator s = sndfiles.begin(); s != sndfiles.end(); ++s) {
272 if (write_one (*s, block_size)) {
273 cerr << "Write failed for file #" << distance (sndfiles.begin(), s) << endl;
277 written += block_size;
278 gint64 elapsed = g_get_monotonic_time() - before;
279 double bandwidth = (sndfiles.size() * block_size * channels * sample_size) / (elapsed/1000000.0);
280 double data_minutes = written / (double) (60.0 * 48000.0);
281 const double data_rate = sndfiles.size() * channels * sample_size * samplerate;
283 ds << setprecision (1) << data_minutes;
285 max_bandwidth = max (max_bandwidth, bandwidth);
286 min_bandwidth = min (min_bandwidth, bandwidth);
289 cout << "BW @ " << written << " frames (" << ds.str() << " minutes) = " << (bandwidth/1048576.0) << " MB/sec " << bandwidth / data_rate << " x faster than necessary " << endl;
293 cout << "Max bandwidth = " << max_bandwidth / 1048576.0 << " MB/sec" << endl;
294 cout << "Min bandwidth = " << min_bandwidth / 1048576.0 << " MB/sec" << endl;
297 cout << "Closing files ...\n";
298 for (vector<SNDFILE*>::iterator s = sndfiles.begin(); s != sndfiles.end(); ++s) {