1 /*
2 ** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
3 **
4 ** This program is free software; you can redistribute it and/or modify
5 ** it under the terms of the GNU General Public License as published by
6 ** the Free Software Foundation; either version 2 of the License, or
7 ** (at your option) any later version.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ** GNU General Public License for more details.
13 **
14 ** You should have received a copy of the GNU General Public License
15 ** along with this program; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18 
19 #include "sfconfig.h"
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 
28 #if (HAVE_DECL_S_IRGRP == 0)
29 #include <sf_unistd.h>
30 #endif
31 
32 #include <string.h>
33 #include <math.h>
34 #include <time.h>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 
38 #include <sndfile.h>
39 
40 #ifndef		M_PI
41 #define		M_PI		3.14159265358979323846264338
42 #endif
43 
44 /*
45 **	Neat solution to the Win32/OS2 binary file flage requirement.
46 **	If O_BINARY isn't already defined by the inclusion of the system
47 **	headers, set it to zero.
48 */
49 #ifndef O_BINARY
50 #define O_BINARY 0
51 #endif
52 
53 #define	WRITE_FLAGS	(O_WRONLY | O_CREAT | O_TRUNC | O_BINARY)
54 #define	READ_FLAGS	(O_RDONLY | O_BINARY)
55 
56 #if (defined (WIN32) || defined (_WIN32) || defined (__OS2__))
57 	#define WRITE_PERMS	0777
58 #else
59 	#define WRITE_PERMS	(S_IRUSR | S_IWUSR | S_IRGRP)
60 #endif
61 
62 #define	BUFFER_SIZE		(1 << 18)
63 #define	BLOCK_COUNT		(30)
64 #define	TEST_DURATION	(5)		/* 5 Seconds. */
65 
66 typedef struct
67 {	double	write_rate ;
68 	double	read_rate ;
69 } PERF_STATS ;
70 
71 static void	*data = NULL ;
72 
73 static void calc_raw_performance (PERF_STATS *stats) ;
74 
75 static void	calc_short_performance (int format, double read_rate, double write_rate) ;
76 static void	calc_int_performance (int format, double read_rate, double write_rate) ;
77 static void	calc_float_performance (int format, double read_rate, double write_rate) ;
78 
79 
80 static int cpu_is_big_endian (void) ;
81 
82 static const char* get_subtype_str (int subtype) ;
83 
84 int
main(int argc,char * argv[])85 main (int argc, char *argv [])
86 {	PERF_STATS	stats ;
87 	char		buffer [256] = "Benchmarking " ;
88 	int			format_major ;
89 
90 	if (! (data = malloc (BUFFER_SIZE * sizeof (double))))
91 	{	perror ("Error : malloc failed") ;
92 		exit (1) ;
93 		} ;
94 
95 	sf_command (NULL, SFC_GET_LIB_VERSION, buffer + strlen (buffer), sizeof (buffer) - strlen (buffer)) ;
96 
97 	puts (buffer) ;
98 	memset (buffer, '-', strlen (buffer)) ;
99 	puts (buffer) ;
100 	printf ("Each test takes a little over %d seconds.\n\n", TEST_DURATION) ;
101 
102 	calc_raw_performance (&stats) ;
103 
104 	if (argc < 2 || strcmp ("--native-only", argv [1]) == 0)
105 	{	puts ("\nNative endian I/O :") ;
106 		format_major = cpu_is_big_endian () ? SF_FORMAT_AIFF : SF_FORMAT_WAV ;
107 
108 		calc_short_performance	(format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ;
109 		calc_int_performance	(format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ;
110 		calc_int_performance	(format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ;
111 		calc_float_performance	(format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ;
112 		calc_float_performance	(format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ;
113 		calc_float_performance	(format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ;
114 		calc_float_performance	(format_major | SF_FORMAT_FLOAT , stats.read_rate, stats.write_rate) ;
115 		} ;
116 
117 	if (argc < 2 || strcmp ("--swap-only", argv [1]) == 0)
118 	{	puts ("\nEndian swapped I/O :") ;
119 		format_major = cpu_is_big_endian () ? SF_FORMAT_WAV : SF_FORMAT_AIFF ;
120 
121 		calc_short_performance	(format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ;
122 		calc_int_performance	(format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ;
123 		calc_int_performance	(format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ;
124 		calc_float_performance	(format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ;
125 		calc_float_performance	(format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ;
126 		calc_float_performance	(format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ;
127 		calc_float_performance	(format_major | SF_FORMAT_FLOAT , stats.read_rate, stats.write_rate) ;
128 		} ;
129 
130 	puts ("") ;
131 
132 	free (data) ;
133 
134 	return 0 ;
135 } /* main */
136 
137 /*==============================================================================
138 */
139 
140 static void
calc_raw_performance(PERF_STATS * stats)141 calc_raw_performance (PERF_STATS *stats)
142 {	clock_t start_clock, clock_time ;
143 	int fd, k, byte_count, retval, op_count ;
144 	const char *filename ;
145 
146 	filename = "benchmark.dat" ;
147 
148 	byte_count = BUFFER_SIZE * sizeof (short) ;
149 
150 	/* Collect write stats */
151 	printf ("    Raw write PCM_16  : ") ;
152 	fflush (stdout) ;
153 
154 	clock_time = 0 ;
155 	op_count = 0 ;
156 	start_clock = clock () ;
157 
158 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
159 	{	if ((fd = open (filename, WRITE_FLAGS, WRITE_PERMS)) < 0)
160 		{	printf ("Error : not able to open file : %s\n", filename) ;
161 			perror ("") ;
162 			exit (1) ;
163 			} ;
164 
165 		for (k = 0 ; k < BLOCK_COUNT ; k++)
166 		{	if ((retval = write (fd, data, byte_count)) != byte_count)
167 			{	printf ("Error : write returned %d (should have been %d)\n", retval, byte_count) ;
168 				exit (1) ;
169 				} ;
170 			} ;
171 
172 		close (fd) ;
173 
174 		clock_time = clock () - start_clock ;
175 		op_count ++ ;
176 		} ;
177 
178 	stats->write_rate = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ;
179 	stats->write_rate *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
180 	printf ("%10.0f samples per sec\n", stats->write_rate) ;
181 
182 	/* Collect read stats */
183 	printf ("    Raw read  PCM_16  : ") ;
184 	fflush (stdout) ;
185 
186 	clock_time = 0 ;
187 	op_count = 0 ;
188 	start_clock = clock () ;
189 
190 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
191 	{	if ((fd = open (filename, READ_FLAGS)) < 0)
192 		{	printf ("Error : not able to open file : %s\n", filename) ;
193 			perror ("") ;
194 			exit (1) ;
195 			} ;
196 
197 		for (k = 0 ; k < BLOCK_COUNT ; k++)
198 		{	if ((retval = read (fd, data, byte_count)) != byte_count)
199 			{	printf ("Error : write returned %d (should have been %d)\n", retval, byte_count) ;
200 				exit (1) ;
201 				} ;
202 			} ;
203 
204 		close (fd) ;
205 
206 		clock_time = clock () - start_clock ;
207 		op_count ++ ;
208 		} ;
209 
210 	stats->read_rate = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ;
211 	stats->read_rate *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
212 	printf ("%10.0f samples per sec\n", stats->read_rate) ;
213 
214 	unlink (filename) ;
215 } /* calc_raw_performance */
216 
217 /*------------------------------------------------------------------------------
218 */
219 
220 static void
calc_short_performance(int format,double read_rate,double write_rate)221 calc_short_performance (int format, double read_rate, double write_rate)
222 {	SNDFILE *file ;
223 	SF_INFO	sfinfo ;
224 	clock_t start_clock, clock_time ;
225 	double	performance ;
226 	int k, item_count, retval, op_count ;
227 	const char* subtype ;
228 	short *short_data ;
229 	const char *filename ;
230 
231 	filename = "benchmark.dat" ;
232 	subtype = get_subtype_str (format & SF_FORMAT_SUBMASK) ;
233 
234 	short_data = data ;
235 	item_count = BUFFER_SIZE ;
236 	for (k = 0 ; k < item_count ; k++)
237 		short_data [k] = 32700.0 * sin (2 * M_PI * k / 32000.0) ;
238 
239 	/* Collect write stats */
240 	printf ("    Write %-5s   to  %s : ", "short", subtype) ;
241 	fflush (stdout) ;
242 
243 	sfinfo.channels = 1 ;
244 	sfinfo.format = format ;
245 	sfinfo.frames = 1 ;
246 	sfinfo.samplerate = 32000 ;
247 
248 	clock_time = 0 ;
249 	op_count = 0 ;
250 	start_clock = clock () ;
251 
252 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
253 	{	if (! (file = sf_open (filename, SFM_WRITE, &sfinfo)))
254 		{	printf ("Error : not able to open file : %s\n", filename) ;
255 			perror ("") ;
256 			exit (1) ;
257 			} ;
258 
259 		/* Turn off the addition of a PEAK chunk. */
260 		sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
261 
262 		for (k = 0 ; k < BLOCK_COUNT ; k++)
263 		{	if ((retval = sf_write_short (file, short_data, item_count)) != item_count)
264 			{	printf ("Error : sf_write_short returned %d (should have been %d)\n", retval, item_count) ;
265 				exit (1) ;
266 				} ;
267 			} ;
268 
269 		sf_close (file) ;
270 
271 		clock_time = clock () - start_clock ;
272 		op_count ++ ;
273 		} ;
274 
275 	performance = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ;
276 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
277 	printf ("%6.2f%% of raw write\n", 100.0 * performance / write_rate) ;
278 
279 	/* Collect read stats */
280 	printf ("    Read  %-5s  from %s : ", "short", subtype) ;
281 	fflush (stdout) ;
282 
283 	clock_time = 0 ;
284 	op_count = 0 ;
285 	start_clock = clock () ;
286 
287 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
288 	{	if (! (file = sf_open (filename, SFM_READ, &sfinfo)))
289 		{	printf ("Error : not able to open file : %s\n", filename) ;
290 			perror ("") ;
291 			exit (1) ;
292 			} ;
293 
294 		for (k = 0 ; k < BLOCK_COUNT ; k++)
295 		{	if ((retval = sf_read_short (file, short_data, item_count)) != item_count)
296 			{	printf ("Error : write returned %d (should have been %d)\n", retval, item_count) ;
297 				exit (1) ;
298 				} ;
299 			} ;
300 
301 		sf_close (file) ;
302 
303 		clock_time = clock () - start_clock ;
304 		op_count ++ ;
305 		} ;
306 
307 	performance = (1.0 * item_count) * BLOCK_COUNT * op_count ;
308 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
309 	printf ("%6.2f%% of raw read\n", 100.0 * performance / read_rate) ;
310 
311 	unlink (filename) ;
312 
313 } /* calc_short_performance */
314 static void
calc_int_performance(int format,double read_rate,double write_rate)315 calc_int_performance (int format, double read_rate, double write_rate)
316 {	SNDFILE *file ;
317 	SF_INFO	sfinfo ;
318 	clock_t start_clock, clock_time ;
319 	double	performance ;
320 	int k, item_count, retval, op_count ;
321 	const char* subtype ;
322 	int *int_data ;
323 	const char *filename ;
324 
325 	filename = "benchmark.dat" ;
326 	subtype = get_subtype_str (format & SF_FORMAT_SUBMASK) ;
327 
328 	int_data = data ;
329 	item_count = BUFFER_SIZE ;
330 	for (k = 0 ; k < item_count ; k++)
331 		int_data [k] = 32700.0 * (1 << 16) * sin (2 * M_PI * k / 32000.0) ;
332 
333 	/* Collect write stats */
334 	printf ("    Write %-5s   to  %s : ", "int", subtype) ;
335 	fflush (stdout) ;
336 
337 	sfinfo.channels = 1 ;
338 	sfinfo.format = format ;
339 	sfinfo.frames = 1 ;
340 	sfinfo.samplerate = 32000 ;
341 
342 	clock_time = 0 ;
343 	op_count = 0 ;
344 	start_clock = clock () ;
345 
346 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
347 	{	if (! (file = sf_open (filename, SFM_WRITE, &sfinfo)))
348 		{	printf ("Error : not able to open file : %s\n", filename) ;
349 			perror ("") ;
350 			exit (1) ;
351 			} ;
352 
353 		/* Turn off the addition of a PEAK chunk. */
354 		sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
355 
356 		for (k = 0 ; k < BLOCK_COUNT ; k++)
357 		{	if ((retval = sf_write_int (file, int_data, item_count)) != item_count)
358 			{	printf ("Error : sf_write_short returned %d (should have been %d)\n", retval, item_count) ;
359 				exit (1) ;
360 				} ;
361 			} ;
362 
363 		sf_close (file) ;
364 
365 		clock_time = clock () - start_clock ;
366 		op_count ++ ;
367 		} ;
368 
369 	performance = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ;
370 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
371 	printf ("%6.2f%% of raw write\n", 100.0 * performance / write_rate) ;
372 
373 	/* Collect read stats */
374 	printf ("    Read  %-5s  from %s : ", "int", subtype) ;
375 	fflush (stdout) ;
376 
377 	clock_time = 0 ;
378 	op_count = 0 ;
379 	start_clock = clock () ;
380 
381 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
382 	{	if (! (file = sf_open (filename, SFM_READ, &sfinfo)))
383 		{	printf ("Error : not able to open file : %s\n", filename) ;
384 			perror ("") ;
385 			exit (1) ;
386 			} ;
387 
388 		for (k = 0 ; k < BLOCK_COUNT ; k++)
389 		{	if ((retval = sf_read_int (file, int_data, item_count)) != item_count)
390 			{	printf ("Error : write returned %d (should have been %d)\n", retval, item_count) ;
391 				exit (1) ;
392 				} ;
393 			} ;
394 
395 		sf_close (file) ;
396 
397 		clock_time = clock () - start_clock ;
398 		op_count ++ ;
399 		} ;
400 
401 	performance = (1.0 * item_count) * BLOCK_COUNT * op_count ;
402 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
403 	printf ("%6.2f%% of raw read\n", 100.0 * performance / read_rate) ;
404 
405 	unlink (filename) ;
406 
407 } /* calc_int_performance */
408 static void
calc_float_performance(int format,double read_rate,double write_rate)409 calc_float_performance (int format, double read_rate, double write_rate)
410 {	SNDFILE *file ;
411 	SF_INFO	sfinfo ;
412 	clock_t start_clock, clock_time ;
413 	double	performance ;
414 	int k, item_count, retval, op_count ;
415 	const char* subtype ;
416 	float *float_data ;
417 	const char *filename ;
418 
419 	filename = "benchmark.dat" ;
420 	subtype = get_subtype_str (format & SF_FORMAT_SUBMASK) ;
421 
422 	float_data = data ;
423 	item_count = BUFFER_SIZE ;
424 	for (k = 0 ; k < item_count ; k++)
425 		float_data [k] = 1.0 * sin (2 * M_PI * k / 32000.0) ;
426 
427 	/* Collect write stats */
428 	printf ("    Write %-5s   to  %s : ", "float", subtype) ;
429 	fflush (stdout) ;
430 
431 	sfinfo.channels = 1 ;
432 	sfinfo.format = format ;
433 	sfinfo.frames = 1 ;
434 	sfinfo.samplerate = 32000 ;
435 
436 	clock_time = 0 ;
437 	op_count = 0 ;
438 	start_clock = clock () ;
439 
440 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
441 	{	if (! (file = sf_open (filename, SFM_WRITE, &sfinfo)))
442 		{	printf ("Error : not able to open file : %s\n", filename) ;
443 			perror ("") ;
444 			exit (1) ;
445 			} ;
446 
447 		/* Turn off the addition of a PEAK chunk. */
448 		sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
449 
450 		for (k = 0 ; k < BLOCK_COUNT ; k++)
451 		{	if ((retval = sf_write_float (file, float_data, item_count)) != item_count)
452 			{	printf ("Error : sf_write_short returned %d (should have been %d)\n", retval, item_count) ;
453 				exit (1) ;
454 				} ;
455 			} ;
456 
457 		sf_close (file) ;
458 
459 		clock_time = clock () - start_clock ;
460 		op_count ++ ;
461 		} ;
462 
463 	performance = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ;
464 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
465 	printf ("%6.2f%% of raw write\n", 100.0 * performance / write_rate) ;
466 
467 	/* Collect read stats */
468 	printf ("    Read  %-5s  from %s : ", "float", subtype) ;
469 	fflush (stdout) ;
470 
471 	clock_time = 0 ;
472 	op_count = 0 ;
473 	start_clock = clock () ;
474 
475 	while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION))
476 	{	if (! (file = sf_open (filename, SFM_READ, &sfinfo)))
477 		{	printf ("Error : not able to open file : %s\n", filename) ;
478 			perror ("") ;
479 			exit (1) ;
480 			} ;
481 
482 		for (k = 0 ; k < BLOCK_COUNT ; k++)
483 		{	if ((retval = sf_read_float (file, float_data, item_count)) != item_count)
484 			{	printf ("Error : write returned %d (should have been %d)\n", retval, item_count) ;
485 				exit (1) ;
486 				} ;
487 			} ;
488 
489 		sf_close (file) ;
490 
491 		clock_time = clock () - start_clock ;
492 		op_count ++ ;
493 		} ;
494 
495 	performance = (1.0 * item_count) * BLOCK_COUNT * op_count ;
496 	performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ;
497 	printf ("%6.2f%% of raw read\n", 100.0 * performance / read_rate) ;
498 
499 	unlink (filename) ;
500 
501 } /* calc_float_performance */
502 
503 
504 /*==============================================================================
505 */
506 
507 static int
cpu_is_big_endian(void)508 cpu_is_big_endian (void)
509 {	unsigned char 	*cptr ;
510 	int 			endtest ;
511 
512 	endtest = 0x12345678 ;
513 
514 	cptr = (unsigned char*) (&endtest) ;
515 
516 	if (cptr [0] == 0x12 && cptr [1] == 0x34 && cptr [3] == 0x78)
517 		return SF_TRUE ;
518 
519 	return SF_FALSE ;
520 } /* cpu_is_big_endian */
521 
522 static const char*
get_subtype_str(int subtype)523 get_subtype_str (int subtype)
524 {	switch (subtype)
525 	{	case SF_FORMAT_PCM_16 :
526 				return "PCM_16" ;
527 
528 		case SF_FORMAT_PCM_24 :
529 				return "PCM_24" ;
530 
531 		case SF_FORMAT_PCM_32 :
532 				return "PCM_32" ;
533 
534 		case SF_FORMAT_FLOAT :
535 				return "FLOAT " ;
536 
537 		case SF_FORMAT_DOUBLE :
538 				return "DOUBLE" ;
539 
540 		default : break ;
541 		} ;
542 
543 	return "UNKNOWN" ;
544 } /* get_subtype_str */
545 
546