File: sboard.c 1 /* 2 The MIT License (MIT) 3 4 Copyright © 2020-2025 pacman64 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy of 7 this software and associated documentation files (the “Software”), to deal 8 in the Software without restriction, including without limitation the rights to 9 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 of the Software, and to permit persons to whom the Software is furnished to do 11 so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in all 14 copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 SOFTWARE. 23 */ 24 25 /* 26 You can build this command-line app by running 27 28 sudo apt install libpulse-dev 29 cc -s -O3 -march=native -mtune=native -flto -o ./sboard ./sboard.c \ 30 -lm -lpulse -lpulse-simple 31 */ 32 33 #include <errno.h> 34 #include <math.h> 35 #include <stdbool.h> 36 #include <stdint.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #ifdef _WIN32 42 #include <fcntl.h> 43 #include <windows.h> 44 #endif 45 46 #include <pulse/simple.h> 47 48 #ifdef RED_ERRORS 49 #define ERROR_STYLE "\x1b[38;2;204;0;0m" 50 #ifdef __APPLE__ 51 #define ERROR_STYLE "\x1b[31m" 52 #endif 53 #define RESET_STYLE "\x1b[0m" 54 #else 55 #define ERROR_STYLE 56 #define RESET_STYLE 57 #endif 58 59 #define ERROR_LINE(MSG) (ERROR_STYLE MSG RESET_STYLE "\n") 60 61 const char* info = "" 62 "sboard [options...] [sound...] [duration...] [volume...]\n" 63 "\n" 64 "\n" 65 "Sound BOARD plays the sound given by name, lasting the number of seconds\n" 66 "given, or 1 second by default. The audio output is 16-bit samples at 48khz.\n" 67 "There's also an optional volume argument, which is 1 by default.\n" 68 "\n" 69 "Options, all of which can start with either 1 or 2 dashes:\n" 70 "\n" 71 " -h show this help message\n" 72 " -help show this help message\n" 73 "\n" 74 " -o output ringtone to standard output as WAV-format data\n" 75 ""; 76 77 const double tau = 2 * M_PI; 78 79 const uint64_t sample_rate = 48000; 80 const double dt = 1.0 / sample_rate; 81 82 double beeper(double t) { 83 return sin(2000 * tau * t) * (fmod(t, 0.5) < 0.0625); 84 } 85 86 double bell(double t) { 87 const double u = fmod(t, 1); 88 return sin(880 * tau * u) * exp(-10 * u); 89 } 90 91 // busy-phone tone 92 double busy(double t) { 93 const double u = fmod(t, 1); 94 const double k = fmin(1, exp(-90 * (u - 0.5))); 95 return k * (sin(480 * tau * t) + sin(620 * tau * t)) / 2; 96 } 97 98 double door_ajar(double t) { 99 const double u = fmod(t, 1); 100 return sin(660 * tau * u) * exp(-10 * u); 101 } 102 103 double heartbeat(double t) { 104 double sum = 0.0; 105 double u = fmod(t, 1); 106 sum += sin(12 * tau * exp(-20 * u)) * exp(-2 * u); 107 u = fmax(0, u - 0.25); 108 sum += sin(8 * tau * exp(-20 * u)) * exp(-2 * u); 109 return 0.5 * sum; 110 } 111 112 // a once-a-seconds stereotypical laser sound 113 double laser(double t) { 114 const double u = fmod(t, 1); 115 return sin(100 * tau * exp(-40 * u)); 116 } 117 118 // flat/uniform random noise 119 double noise(double t) { 120 const double v = (double)rand() / RAND_MAX; 121 return 2 * v - 1; 122 } 123 124 // ready-phone tone 125 double ready(double t) { 126 return 0.5 * sin(350 * tau * t) + 0.5 * sin(450 * tau * t); 127 } 128 129 // annoying ringtone 130 double ringtone(double t) { 131 const double u = fmod(t, 0.1); 132 return sin(2048 * tau * t) * exp(-50 * u); 133 } 134 135 double thud(double t) { 136 double u = fmod(t, 1); 137 return sin(12 * tau * exp(-20 * u)) * exp(-2 * u); 138 } 139 140 // 440hz tuning tone 141 double tone(double t) { 142 return sin(440 * tau * t); 143 } 144 145 double woo_woah_wow(double t) { 146 // const double period = 1.3; 147 const double period = 1.25; 148 const double u = fmod(t, period); 149 return sin(tau * (260 * sin(tau * u)) * u); 150 } 151 152 // functions used by fancier tunes 153 154 double kick(double t, double f, double k, double p) { 155 return sin(tau * f * pow(p, t)) * exp(-k * t); 156 } 157 158 double default_kick(double t, double f, double k) { 159 return kick(t, f, k, 0.085); 160 } 161 162 // flat/uniform random noise 163 double random_uniform() { 164 const double v = (double)rand() / RAND_MAX; 165 return 2 * v - 1; 166 } 167 168 double hit_hi_hat(double t, double k) { 169 return random_uniform() * exp(-k * t); 170 } 171 172 double schedule(double t, double delay, double period) { 173 return fmod(t + (1 - delay) * period, period); 174 } 175 176 double arp(double x, double y, double z, double k, double t) { 177 const double u = fmod(t / 2, k); 178 return sin(x * (exp(-y * u))) * exp(-z * u); 179 } 180 181 double linterp(double x, double y, double k) { 182 return k * x + (1 - k) * y; 183 } 184 185 double power_synth(double t, double freq) { 186 // function linspace(a, b, n) { 187 // const y = new Array(n); 188 // const incr = (b - a) / (n + 1); 189 // for (let i = 0; i < n; i++) 190 // y[i] = a + incr * i; 191 // return y; 192 // } 193 // bass_powers = linspace(1e-5, 1, 10).map(x => -0.05 * Math.log(x)); 194 195 const double powers[] = { 196 0.5756462732485115, 0.11988976388990187, 197 0.08523515466254475, 0.06496281589095718, 198 0.05057917059158016, 0.03942226802181348, 199 0.030306373513585217, 0.022598970473683477, 200 0.015922499056278294, 0.010033423662119907, 201 }; 202 const size_t n = sizeof(powers) / sizeof(powers[0]); 203 204 double res = 0.0; 205 for (size_t i = 0; i < n; i++) { 206 res += powers[i] * cos(tau * (i + 1) * freq * t); 207 } 208 return res; 209 } 210 211 double bass_envelope(double t) { 212 const double u = fmod(t, 1); 213 return (u < 0.05) ? 15 * u : exp(-7 * u); 214 } 215 216 // fancier tunes 217 218 double bust_a_move(double t) { 219 const double period = 4.09; 220 const double freqs[] = { 221 50, 75, 50, 50, 75, 50, 50, 50, 75, 50, 50, 50, 50, 50, 75, 50 222 }; 223 // const delays = [ 224 // 0, 0.52, 0.77, 1.28, 1.54, 1.79, 225 // 2.04, 2.3, 2.56, 2.67, 226 // 3.05, 3.07, 3.2, 3.45, 3.57, 3.82 227 // ].map(x => x / period); 228 const double delays[] = { 229 0.0000000000000000, 0.1271393643031785, 230 0.1882640586797066, 0.31295843520782396, 231 0.3765281173594132, 0.43765281173594134, 232 0.49877750611246946, 0.5623471882640586, 233 0.6259168704156479, 0.6528117359413202, 234 0.745721271393643, 0.7506112469437652, 235 0.78239608801956, 0.8435207823960881, 236 0.8728606356968215, 0.9339853300733496, 237 }; 238 239 const double u = fmod(t, period); 240 const size_t half = sizeof(freqs) / sizeof(freqs[0]) / 2; 241 const size_t start = u < 2.5 ? 0 : half; 242 243 double kicks = 0.0; 244 for (size_t i = 0; i < half; i++) { 245 const double d = delays[start + i]; 246 const double f = freqs[start + i]; 247 kicks += default_kick(schedule(t, d, period), f, 50); 248 } 249 250 double hi_hats = 0.0; 251 for (size_t i = 0; i < half; i++) { 252 hi_hats += hit_hi_hat(schedule(t, i / half, period / half), 25); 253 } 254 return 0.9 * kicks + 1.0 / 32 * hi_hats; 255 } 256 257 double crazy(double t) { 258 const double snares[] = { 259 0, 0, 1, 0, 0, 0, 1, 0, 260 0, 0, 1, 0, 0, 0, 1, 0, 261 0, 0, 1, 0, 0, 0, 1, 0, 262 0, 0, 1, 0, 0, 0, 1, 0, 263 0, 0, 1, 0, 0, 0, 1, 0, 264 0, 0, 1, 0, 0, 0, 1, 0, 265 0, 0, 1, 0, 0, 0, 1, 0, 266 0, 0, 0, 0, 0, 0, 0, 0, 267 }; 268 269 const double kicks[] = { 270 1, 0, 0, 0, 1, 0, 0, 0, 271 1, 0, 0, 0, 1, 0, 0, 0, 272 1, 0, 0, 0, 1, 0, 0, 0, 273 1, 1, 0, 1, 1, 0, 1, 1, 274 1, 0, 0, 0, 1, 0, 0, 0, 275 1, 0, 0, 0, 1, 0, 0, 0, 276 1, 0, 0, 0, 1, 0, 0, 0, 277 1, 1, 1, 1, 1, 1, 1, 1, 278 }; 279 280 const double bass_speed[] = { 281 2, 2, 2, 2, 2, 2, 2, 2, 282 2, 2, 2, 2, 2, 2, 2, 2, 283 2, 2, 2, 2, 2, 2, 2, 2, 284 2, 2, 2, 2, 2, 2, 2, 2, 285 2, 2, 2, 2, 2, 2, 2, 2, 286 2, 2, 2, 2, 2, 2, 2, 2, 287 2, 2, 2, 2, 2, 2, 2, 2, 288 4, 4, 4, 4, 4, 4, 4, 4, 289 }; 290 291 double seq(const double* d, double s, double t) { 292 return d[(size_t)floor(t / s / 2) % sizeof(kicks)]; 293 } 294 295 t *= 148.0 / 120; 296 // const double period = 6.5; 297 // const double u = fmod(t, period); 298 // const double v = fmod(t, 2*period); 299 const double rand = linterp(random(), 1, 1.0 / 2); 300 const double anticlip = fmax(-log(.75 * fmod(8 * t, 1) + 1.0 / M_E), 0); 301 // const double anticlip = 1; 302 const double k = anticlip * arp(60, 40, 20, 1.0 / 16, t) * 303 seq(kicks, 1.0 / 16, t); 304 const double s = 0.3 * arp(60, 80, 3, 1.0 / 16, t) * rand * 305 seq(snares, 1.0 / 16, t); 306 const double su = seq(bass_speed, 1.0 / 16, t); 307 const double b1 = bass_envelope(su * t) * 308 power_synth(su * t, 50.0 / su); 309 const double b2 = bass_envelope(su * (t + 0.5 * 6.5)) * 310 power_synth(su * (t + 0.5 * 6.5), 60.0 / su); 311 const double b = b1 + b2; 312 // return s; 313 return 0.9 * k + 0.7 * s + 0.8 * b; 314 } 315 316 double piano(double t, double n) { 317 const double p = (n - 49) / 12; 318 const double f = 440 * pow(2, p); 319 return sin(tau * f * t); 320 } 321 322 double piano_loop(double t) { 323 const double period = 1.025; 324 const double cutoff = 12; 325 const double p = period; 326 double y = 0; 327 y += piano(t, 49) * exp(-cutoff * fmod(t, period)); 328 y += piano(t + 0.25 * p, 50) * exp(-cutoff * fmod(t + 0.25 * p, p)); 329 y += piano(t + 0.50 * p, 54) * exp(-cutoff * fmod(t + 0.50 * p, p)); 330 y += piano(t + 0.75 * p, 51) * exp(-cutoff * fmod(t + 0.75 * p, p)); 331 return 0.75 * y; 332 } 333 334 double walk_this_way(double t) { 335 const double period = 2.2; 336 const double freqs[] = {50, 70, 50, 50, 50, 70}; 337 const double delays[] = {0.000, 0.250, 0.450, 0.500, 0.625, 0.750}; 338 const size_t n = sizeof(delays) / sizeof(delays[0]); 339 340 double kicks = 0; 341 for (size_t i = 0; i < n; i++) { 342 kicks += default_kick(schedule(t, delays[i], period), freqs[i], 50); 343 } 344 double hi_hats = 1.2 * hit_hi_hat(schedule(t, 0, period), 7); 345 for (size_t i = 1; i < n; i++) { 346 hi_hats += hit_hi_hat(schedule(t, i / 8, period / 8), 30); 347 } 348 // return 1 * kicks + 1.0 / 24 * hi_hats; 349 return 1 * kicks + 1.0 / 48 * hi_hats; 350 } 351 352 const char* aliases[] = { 353 "beeper", "beeper", 354 "beeps", "beeper", 355 "bell", "bells", 356 "bells", "bells", 357 "busy", "busy", 358 "door-ajar", "door-ajar", 359 "doorajar", "door-ajar", 360 "heart", "heart", 361 "heart-beat", "heart", 362 "heart-beats", "heart", 363 "heartbeat", "heart", 364 "heartbeats", "heart", 365 "laser", "laser", 366 "noise", "noise", 367 "ready", "ready", 368 "ring-tone", "ringtone", 369 "ringtone", "ringtone", 370 "thud", "thud", 371 "440", "tone", 372 "440hz", "tone", 373 "tone", "tone", 374 "woo-woah-wow", "woo-woah-wow", 375 "woo", "woo-woah-wow", 376 "woah", "woo-woah-wow", 377 "wow", "woo-woah-wow", 378 379 "bust-a-move", "bust-a-move", 380 "bustamove", "bust-a-move", 381 "crazy", "crazy", 382 "piano", "piano-loop", 383 "piano-loop", "piano-loop", 384 "pianoloop", "piano-loop", 385 "walk-this-way", "walk-this-way", 386 "walkthisway", "walk-this-way", 387 }; 388 389 const char* dealias(const char* s) { 390 const size_t n = sizeof(aliases) / sizeof(aliases[0]); 391 for (size_t i = 0; i < n; i += 2) { 392 if (strcmp(s, aliases[i]) == 0) { 393 return aliases[i + 1]; 394 } 395 } 396 return NULL; 397 } 398 399 typedef struct lookup_entry { 400 const char* name; 401 double (*func)(double); 402 const char* desc; 403 } lookup_entry; 404 405 lookup_entry lookup[] = { 406 {"beeper", beeper, "beep some TV-related devices used to make"}, 407 {"bells", bell, "a synthetic bell"}, 408 {"busy", busy, "a busy phone"}, 409 {"door-ajar", door_ajar, "door-ajar warning sound"}, 410 {"heart", heartbeat, "pairs of heart-pulses"}, 411 {"laser", laser, "a stereotypical laser sound, once a second"}, 412 {"noise", noise, "uniform random noise (annoying)"}, 413 {"ready", ready, "a ready phone"}, 414 {"ringtone", ringtone, "a slightly annoying ringtone"}, 415 {"thud", thud, "a low beat, similar to those in nightclubs"}, 416 {"tone", tone, "a 440hz tuning tone"}, 417 {"woo-woah-wow", woo_woah_wow, "a crazy sound (loop: 1.25s)"}, 418 419 {"bust-a-move", bust_a_move, "middle part of that song (loop: 4.09s)"}, 420 {"crazy", crazy, "a repeating tune (NOT FULLY WORKING) (loop: 6.5s)"}, 421 {"piano-loop", piano_loop, "a few repeating piano notes (loop: 1.025s)"}, 422 {"walk-this-way", walk_this_way, "drums from that song (loop: 2.2s)"}, 423 }; 424 425 // is_help_option simplifies control-flow for func main 426 bool is_help_option(const char* s) { 427 return (s[0] == '-') && ( 428 strcmp(s, "-h") == 0 || 429 strcmp(s, "-help") == 0 || 430 strcmp(s, "--h") == 0 || 431 strcmp(s, "--help") == 0 432 ); 433 } 434 435 // is_output_option simplifies control-flow for func main 436 bool is_output_option(const char* s) { 437 return (s[0] == '-') && ( 438 strcmp(s, "-o") == 0 || 439 strcmp(s, "--o") == 0 440 ); 441 } 442 443 static inline void write_int16_le(FILE* w, int16_t v) { 444 fputc((v >> 0), w); 445 fputc((v >> 8), w); 446 } 447 448 static inline void write_uint16_le(FILE* w, uint16_t v) { 449 fputc((v >> 0), w); 450 fputc((v >> 8), w); 451 } 452 453 void write_uint32_le(FILE* w, uint32_t v) { 454 fputc((v >> 0), w); 455 fputc((v >> 8), w); 456 fputc((v >> 16), w); 457 fputc((v >> 24), w); 458 } 459 460 static inline void set_int16_le(unsigned char* buf, int16_t v) { 461 buf[0] = (v >> 0); 462 buf[1] = (v >> 8); 463 } 464 465 // is_big_endian checks the platform's endianness; kept for reference; not 466 // used anymore since playback use little-endian samples on all platforms, 467 // via function set_int16_le 468 bool is_big_endian() { 469 const uint8_t pair[2] = {255, 0}; 470 return *((uint16_t*)pair) >= 256; 471 } 472 473 int play(double seconds, double volume, double (*emit)(double)) { 474 if (seconds <= 0) { 475 return 0; 476 } 477 if (volume < 0 || volume > 1 || isnan(seconds) || isinf(seconds)) { 478 volume = 1; 479 } 480 481 const uint64_t rate = 48 * 1000; 482 unsigned char buf[32 * 1024]; 483 pa_simple* pa; 484 pa_sample_spec spec; 485 memset(&spec, 0, sizeof(spec)); 486 // spec.format = is_big_endian() ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; 487 spec.format = PA_SAMPLE_S16LE; 488 spec.rate = rate; 489 spec.channels = 1; 490 491 pa = pa_simple_new( 492 NULL, "ringtone", PA_STREAM_PLAYBACK, NULL, "<<ringtone>>", &spec, 493 NULL, NULL, &errno 494 ); 495 if (pa == NULL) { 496 fprintf(stderr, ERROR_LINE("can't use pulse-audio for playback")); 497 return 1; 498 } 499 500 uint64_t pos = 0; 501 const uint64_t samples = (uint64_t)ceil(seconds * rate); 502 503 for (uint64_t i = 0; i < samples; i++) { 504 const double t = (double)i * dt; 505 const double v = volume * emit(t); 506 set_int16_le(&buf[pos], (int16_t)(32767 * v)); 507 508 pos += 2; 509 if (pos == sizeof(buf)) { 510 if (pa_simple_write(pa, buf, pos, NULL) < 0) { 511 pa_simple_drain(pa, NULL); 512 pa_simple_free(pa); 513 return 1; 514 } 515 pos = 0; 516 } 517 } 518 519 if (pos > 0) { 520 if (pa_simple_write(pa, buf, pos, NULL) < 0) { 521 pa_simple_drain(pa, NULL); 522 pa_simple_free(pa); 523 return 1; 524 } 525 } 526 527 pa_simple_drain(pa, NULL); 528 pa_simple_free(pa); 529 return 0; 530 } 531 532 // write_wav_intro starts a wave-format data-stream declaring a mono 16-bit 533 // integer PCM sound for the number of samples given 534 void write_wav_intro(FILE* w, uint64_t samples) { 535 const uint16_t integer_pcm = 1; 536 const uint32_t fmt_chunk_size = 16; 537 538 const uint32_t channels = 1; 539 const uint32_t bytes_per_sample = 2; 540 const uint32_t bits_per_sample = 8 * bytes_per_sample; 541 const uint32_t byte_rate = sample_rate * bytes_per_sample * channels; 542 543 uint64_t data_size = byte_rate * samples; 544 uint64_t total_size = data_size + 44; 545 if (data_size > UINT32_MAX) { 546 data_size = UINT32_MAX; 547 total_size = UINT32_MAX; 548 } 549 550 fprintf(w, "RIFF"); 551 write_uint32_le(w, total_size); 552 fprintf(w, "WAVEfmt "); 553 554 write_uint32_le(w, fmt_chunk_size); 555 write_uint16_le(w, integer_pcm); 556 write_uint16_le(w, channels); 557 write_uint32_le(w, sample_rate); 558 write_uint32_le(w, byte_rate); 559 write_uint16_le(w, bytes_per_sample * channels); 560 write_uint16_le(w, bits_per_sample); 561 562 fprintf(w, "data"); 563 write_uint32_le(w, data_size); 564 } 565 566 int emit_wav(double seconds, double volume, double (*emit)(double)) { 567 if (seconds <= 0) { 568 write_wav_intro(stdout, 0); 569 return 0; 570 } 571 572 if (volume < 0 || volume > 1 || isnan(seconds) || isinf(seconds)) { 573 volume = 1; 574 } 575 576 const uint64_t samples = (uint64_t)(seconds * sample_rate); 577 if (samples >= UINT32_MAX) { 578 fprintf(stderr, ERROR_LINE("duration given exceeds WAV-format max")); 579 return 1; 580 } 581 582 write_wav_intro(stdout, samples); 583 584 for (uint64_t i = 0; i < samples; i++) { 585 const double t = (double)i * dt; 586 const double v = volume * emit(t); 587 write_int16_le(stdout, (int16_t)(32767 * v)); 588 589 // check if the standard output was closed only occasionally 590 if ((i % (32 * 1024) == 0) && feof(stdout)) { 591 return 0; 592 } 593 } 594 595 return 0; 596 } 597 598 void show_help(FILE* w) { 599 fprintf(w, "%s", info); 600 fprintf(w, "\n\nSound names available\n\n"); 601 602 for (size_t i = 0; i < sizeof(aliases) / sizeof(aliases[0]); i += 2) { 603 const char* name = aliases[i]; 604 const char* dealiased = aliases[i + 1]; 605 606 for (size_t j = 0; j < sizeof(lookup) / sizeof(lookup[0]); j++) { 607 if (strcmp(lookup[j].name, dealiased) == 0) { 608 fprintf(w, " %-14s %s\n", name, lookup[j].desc); 609 break; 610 } 611 } 612 } 613 } 614 615 int main(int argc, char** argv) { 616 #ifdef _WIN32 617 setmode(fileno(stdin), O_BINARY); 618 // ensure output lines end in LF instead of CRLF on windows 619 setmode(fileno(stdout), O_BINARY); 620 setmode(fileno(stderr), O_BINARY); 621 #endif 622 623 if (argc < 2 || is_help_option(argv[1])) { 624 const bool no_args = argc < 2; 625 show_help(no_args ? stderr : stdout); 626 return no_args ? 1 : 0; 627 } 628 629 size_t start_args = 1; 630 bool raw_output = false; 631 if (is_output_option(argv[1])) { 632 raw_output = true; 633 start_args++; 634 635 // enable full/block-buffering for standard output 636 setvbuf(stdout, NULL, _IOFBF, 0); 637 } 638 639 const char* sound_name = ""; 640 sound_name = argv[start_args]; 641 start_args++; 642 643 double seconds = 1.0; 644 if (argc > start_args) { 645 char* end; 646 const double n = strtod(argv[start_args], &end); 647 if (*end == 0 && argv[start_args] != end) { 648 seconds = n >= 0 ? n : 1.0; 649 start_args++; 650 } 651 } 652 653 double volume = 1.0; 654 if (argc > start_args) { 655 char* end; 656 const double n = strtod(argv[start_args], &end); 657 if (*end == 0 && argv[start_args] != end) { 658 volume = n >= 0 ? n : 1.0; 659 } 660 } 661 662 if (sound_name[0] == 0) { 663 fprintf(stderr, "not given a sound name\n"); 664 return 1; 665 } 666 667 const char* dealiased = dealias(sound_name); 668 if (dealiased == NULL) { 669 fprintf(stderr, "sound named \"%s\" not available\n", sound_name); 670 return 1; 671 } 672 673 const double s = seconds; 674 const double v = volume; 675 for (size_t i = 0; i < sizeof(lookup) / sizeof(lookup[0]); i++) { 676 if (strcmp(lookup[i].name, dealiased) == 0) { 677 srand(0); 678 return (raw_output ? emit_wav : play)(s, v, lookup[i].func); 679 } 680 } 681 682 fprintf(stderr, "sound named \"%s\" not available\n", sound_name); 683 return 1; 684 }