summaryrefslogtreecommitdiff
path: root/tools/sftest.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2014-12-10 11:44:55 -0500
committerPaul Davis <paul@linuxaudiosystems.com>2014-12-10 11:44:55 -0500
commitdd6a72dd78594f5f71e2b8fc9b0a88e040bcf0a1 (patch)
tree1f3742de5a4ca23d6ede551fda755224e762ef1c /tools/sftest.cc
parent635578b30ba2e1514e251fa3add98270413dcb6b (diff)
add sndfile I/O test application, used for testing disk bandwidth for writing audio files
Diffstat (limited to 'tools/sftest.cc')
-rw-r--r--tools/sftest.cc210
1 files changed, 210 insertions, 0 deletions
diff --git a/tools/sftest.cc b/tools/sftest.cc
new file mode 100644
index 0000000000..6e019b166c
--- /dev/null
+++ b/tools/sftest.cc
@@ -0,0 +1,210 @@
+/* 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>
+
+#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 << "\tHEADER-FORMAT is one of:" << endl
+ << "\t\tWAV" << endl
+ << "\t\tCAF" << endl
+ << "\t\tW64" << endl;
+ cout << "\tDATA-FORMAT is one of:" << endl
+ << "\t\tFLOAT" << endl
+ << "\t\t32" << endl
+ << "\t\t24" << endl
+ << "\t\t16" << endl;
+}
+
+int
+main (int argc, char* argv[])
+{
+ vector<SNDFILE*> sndfiles;
+ uint32_t sample_size;
+ char optstring[] = "f:r:F:n:c:b:s";
+ 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;
+ 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' },
+ { 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;
+
+ 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());
+
+ if ((sf = sf_open (path.c_str(), SFM_RDWR, &format_info)) == 0) {
+ cerr << "Could not open file #" << n << " @ " << path << endl;
+ return 1;
+ }
+
+ sndfiles.push_back (sf);
+ }
+
+ cout << nfiles << " files are in " << tmpdirname << endl;
+ 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;
+
+ usleep(1e6 * 1024 / samplerate);
+ }
+
+ return 0;
+}
+
+