From 2d583214ac6cd42616695fb6795995bd8bfce6d7 Mon Sep 17 00:00:00 2001 From: Jonathan Wilkes <jon.w.wilkes@gmail.com> Date: Mon, 2 Nov 2020 22:48:35 -0500 Subject: [PATCH] add right outlet for soundfiler, improve error messages for it and writesf~ --- pd/doc/5.reference/soundfiler-help.pd | 65 +- pd/src/d_soundfile.c | 597 ++++++++++++------ scripts/regression_tests.pd | 8 +- .../soundfiler_read_coverage.pd | 56 ++ .../soundfiler_write_coverage.pd | 56 ++ .../writesf~_open_coverage.pd | 37 ++ scripts/utils/method-error.pd | 20 + 7 files changed, 610 insertions(+), 229 deletions(-) create mode 100644 scripts/regression_tests/soundfiler_read_coverage.pd create mode 100644 scripts/regression_tests/soundfiler_write_coverage.pd create mode 100644 scripts/regression_tests/writesf~_open_coverage.pd create mode 100644 scripts/utils/method-error.pd diff --git a/pd/doc/5.reference/soundfiler-help.pd b/pd/doc/5.reference/soundfiler-help.pd index 3fa80f90a..f24e1a5d9 100644 --- a/pd/doc/5.reference/soundfiler-help.pd +++ b/pd/doc/5.reference/soundfiler-help.pd @@ -1,10 +1,10 @@ -#N canvas 429 34 555 619 10; +#N canvas 75 64 555 619 10; #X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0 -14 -228856 -66577 0; +14 #dcdcdc #404040 0; #X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header soundfiler 3 12 -0 18 -204280 -1 0; -#X obj 0 387 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13 --228856 -1 0; +0 18 #c4dcdc #000000 0; +#X obj 0 352 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13 +#dcdcdc #000000 0; #N canvas 490 283 494 344 META 0; #X text 12 105 LIBRARY internal; #X text 12 145 WEBSITE http://crca.ucsd.edu/~msp/; @@ -19,16 +19,16 @@ Wilkes revised the patch to conform to the PDDP template for Pd version #X text 12 5 KEYWORDS control array filesystem; #X text 12 165 RELEASE_DATE 1997; #X restore 500 597 pd META; -#X obj 0 487 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0 -13 -228856 -1 0; +#X obj 0 452 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0 +13 #dcdcdc #000000 0; #X obj 0 529 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12 -0 13 -228856 -1 0; +0 13 #dcdcdc #000000 0; #X obj 0 554 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12 -0 13 -228856 -1 0; +0 13 #dcdcdc #000000 0; #X text 98 533 (none); #N canvas 217 519 428 106 Related_objects 0; #X obj 0 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0 -14 -204280 -1 0; +14 #c4dcdc #000000 0; #X text 7 2 [soundfiler] Related Objects; #X obj 22 43 tabwrite~; #X obj 22 69 tabread4~; @@ -36,16 +36,16 @@ Wilkes revised the patch to conform to the PDDP template for Pd version #X obj 143 69 writesf~; #X obj 87 69 readsf~; #X restore 102 597 pd Related_objects; -#X obj 78 396 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856 --162280 0; -#X text 98 495 float; -#X obj 78 496 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856 --162280 0; +#X obj 78 361 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 #dcdcdc +#9c9c9c 0; +#X text 98 460 float; +#X obj 78 461 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 #dcdcdc +#9c9c9c 0; #X obj 477 10 soundfiler; #X text 11 23 read and write soundfiles to arrays; -#X text 98 395 read; -#X text 98 412 write; -#X text 168 495 - the output specifies the total number of samples +#X text 98 360 read; +#X text 98 377 write; +#X text 168 460 - the output specifies the total number of samples that have been read or written.; #X obj 20 293 soundfiler; #X floatatom 20 317 0 0 0 0 - - -; @@ -54,16 +54,16 @@ that have been read or written.; #X text 322 224 write a file; #X text 358 268 write stereo; #N canvas 0 0 450 300 (subpatch) 0; -#X array sf-array1 77971 float 0; -#X coords 0 1 77971 -1 130 70 1; -#X restore 135 306 graph; +#X array sf-array1 77971 float 0 black black; +#X coords 0 1 77970 -1 130 50 1; +#X restore 185 296 graph; #N canvas 0 0 450 300 (subpatch) 0; -#X array sf-array2 77971 float 0; -#X coords 0 1 77971 -1 130 70 1; -#X restore 288 306 graph; +#X array sf-array2 77971 float 0 black black; +#X coords 0 1 77970 -1 130 50 1; +#X restore 338 296 graph; #N canvas 110 93 428 434 flags 0; #X obj 0 0 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0 -14 -204280 -1 0; +14 #c4dcdc #000000 0; #X text 19 37 When reading you can leave soundfiler to figure out which of the three known soundfile formats the file belongs to or override all header information using the "-raw" flag.; @@ -88,9 +88,9 @@ prefers.; #X text 17 400 The number of channels is limited to 64; #X text 37 371 -rate <sample rate>; #X text 7 1 [soundfiler] Flags; -#X restore 172 459 pd flags; -#X text 168 412 - write a soundfile.; -#X text 169 428 The "read" and "write" messages accept flags. See the +#X restore 172 424 pd flags; +#X text 168 377 - write a soundfile.; +#X text 169 393 The "read" and "write" messages accept flags. See the subpatch below for details:; #X msg 20 138 read ../sound/bell.aiff sf-array2; #X msg 20 161 read -resize ../sound/bell.aiff sf-array2; @@ -102,7 +102,7 @@ subpatch below for details:; ; #X text 399 197 overriding everything; #X text 398 183 ...or even; -#X text 168 395 - read a soundfile.; +#X text 168 360 - read a soundfile.; #X text 17 41 The [soundfiler] object reads and writes floating point arrays to binary soundfiles which may contain 2 or 3 byte fixed point or 4 byte floating point samples in wave \, aiff \, or next formats @@ -113,7 +113,14 @@ and unsupplied channels are zeroed out).; #X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide ; #X obj 98 575 pddp/pddplink all_about_arrays.pd; +#X obj 78 497 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 #dcdcdc +#9c9c9c 0; +#X text 98 496 list; +#X obj 77 318 print sf_stats; +#X text 168 496 - stats for the file being read or written: <samplerate> +<headersize> <nchannels> <bytespersample> <endianness>; #X connect 17 0 18 0; +#X connect 17 1 43 0; #X connect 28 0 17 0; #X connect 29 0 17 0; #X connect 30 0 17 0; diff --git a/pd/src/d_soundfile.c b/pd/src/d_soundfile.c index a8a1e1962..f0720e7c3 100644 --- a/pd/src/d_soundfile.c +++ b/pd/src/d_soundfile.c @@ -675,201 +675,293 @@ static void soundfile_xferin_float(int sfchannels, int nvecs, t_float **vecs, */ -static void argerror(void *obj, const char *fmt, ...) +static void argerror(void *obj, t_symbol *s, int argc, t_atom *argv, + const char *fmt, ...) { - char msg[MAXPDSTRING]; + char error_str[MAXPDSTRING]; + char *user_msg; /* message from user that triggered the error */ + int len; char *classname = class_getname(pd_class((t_pd *)obj)); va_list ap; + t_binbuf *b = binbuf_new(); va_start(ap, fmt); - vsnprintf(msg, MAXPDSTRING-1, fmt, ap); + vsnprintf(error_str, MAXPDSTRING-1, fmt, ap); va_end(ap); - pd_error(obj, "%s: %s", classname, msg); + + binbuf_addv(b, "s", s); + binbuf_add(b, argc, argv); + binbuf_gettext(b, &user_msg, &len); /* not null-terminated! */ + + pd_error(obj, "%s: '%.*s': %s", + classname, + len < MAXPDSTRING ? len : MAXPDSTRING, + user_msg, + error_str); } -static int verify_flag(void *obj, char *flag) +static int flag_missing_floatarg(void *obj, t_symbol *s, int argc, + t_atom *argv, char *flag, int flagc, t_atom *flagv) { - if (!flag) + /* First check if our flag has an arg at all. If not, error out. + (flagc includes the flag itself.) */ + if (flagc < 2) { - argerror(obj, "empty symbol detected"); - return 0; + argerror(obj, s, argc, argv, + "'%s' flag expects a float argument", flag); + return 1; } - if (flag[0] != '-') + if (flagv[1].a_type != A_FLOAT) { - argerror(obj, "expected '-%s' flag but got '%s'", flag, flag); - return 0; + argerror(obj, s, argc, argv, "'%s' flag expects a float but got '%s'", + flag, + flagv[1].a_type == A_SYMBOL ? + flagv[1].a_w.w_symbol->s_name : + "unexpected arg type"); + return 1; } - return 1; + return 0; } -static int flag_and_floatarg(void *obj, char *flag, int argc, t_atom *argv) +static int flag_has_unexpected_floatarg(void *obj, t_symbol *s, + int argc, t_atom *argv, char *flag, int flagc, t_atom *flagv) { - if (!verify_flag(obj, flag)) - return 0; - if (argc < 2) - { - argerror(obj, "need a float argument for '-%s' flag", flag); - return 0; - } - if (argv[1].a_type != A_FLOAT) + if (flagc < 2) return 0; + if (flagv[1].a_type == A_FLOAT) { - argerror(obj, "'-%s' flag expects float but got '%s'", - flag, - argv[1].a_type == A_SYMBOL ? - argv[1].a_w.w_symbol->s_name : - "unexpected arg type"); - return 0; + argerror(obj, s, argc, argv, + "'%s' flag does not accept a float argument", flag); + return 1; } - return 1; + return 0; } -static int matchstring(char *str, char *flag) + /* catch filenames that are flags, with and without pre-fixed '-' */ +static int file_is_a_flag_name(t_symbol *sym, int *nodash) { - return (!strcmp(str, flag) || !strcmp(str+1, flag)); + char *s = sym->s_name; + if (s[0] == '-') + { + s++; + *nodash = 1; + } + else + *nodash = 0; + + if (!strcmp(s, "skip") + || !strcmp(s, "nframes") + || !strcmp(s, "bytes") + || !strcmp(s, "normalize") + || !strcmp(s, "wave") + || !strcmp(s, "nextstep") + || !strcmp(s, "aiff") + || !strcmp(s, "big") + || !strcmp(s, "little") + || !strcmp(s, "r") + || !strcmp(s, "rate")) + { + return 1; + } + else + return 0; } /* the routine which actually does the work should LATER also be called from garray_write16. */ /* Parse arguments for writing. The "obj" argument is only for flagging - errors. For streaming to a file the "normalize", "onset" and "nframes" + errors. For streaming to a file the "normalize", "skip" and "nframes" arguments shouldn't be set but the calling routine flags this. */ -static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, + /* Also note that streaming objects like writesf~ don't take args after + the filename, while soundfiler does to specify the source tables */ +static int soundfiler_writeargparse(void *obj, t_symbol *s, + int *p_argc, t_atom **p_argv, t_symbol **p_filesym, int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian, int *p_normalize, long *p_onset, long *p_nframes, t_float *p_rate) { + /* copies for convenience, and for the ruthless mutation below. :) */ int argc = *p_argc; + int ac = argc; t_atom *argv = *p_argv; + t_atom *av = argv; int bytespersamp = 2, bigendian = 0, endianness = -1, swap, filetype = -1, normalize = 0; long onset = 0, nframes = 0x7fffffff; t_symbol *filesym; t_float rate = -1; - while (argc > 0 && argv->a_type == A_SYMBOL) + while (ac > 0 && atom_getsymbol(av)->s_name[0] == '-') { - char *flag = argv->a_w.w_symbol->s_name; - if (matchstring(flag, "skip")) + char *flag = av->a_w.w_symbol->s_name; + if (!strcmp(flag, "-skip")) { - if (!flag_and_floatarg(obj, flag, argc, argv)) + if (flag_missing_floatarg(obj, s, argc, argv, flag, ac, av)) goto usage; - if ((onset = argv[1].a_w.w_float) < 0) + if ((onset = av[1].a_w.w_float) < 0) { - argerror(obj, "'-skip' flag does not allow a negative number"); + argerror(obj, s, argc, argv, + "'-skip' flag does not allow a negative number"); goto usage; } - argc -= 2; argv += 2; + ac -= 2; av += 2; } - else if (matchstring(flag, "nframes")) + else if (!strcmp(flag, "-nframes")) { - if (!flag_and_floatarg(obj, flag, argc, argv)) + if (flag_missing_floatarg(obj, s, argc, argv, flag, ac, av)) goto usage; - if ((nframes = argv[1].a_w.w_float) < 0) + if ((nframes = av[1].a_w.w_float) < 0) { - argerror(obj, "'-nframes' flag does not allow a negative " - "number"); + argerror(obj, s, argc, argv, + "'-nframes' flag does not allow a negative number"); goto usage; } - argc -= 2; argv += 2; + ac -= 2; av += 2; } - else if (matchstring(flag, "bytes")) + else if (!strcmp(flag, "-bytes")) { - if (!flag_and_floatarg(obj, flag, argc, argv)) + if (flag_missing_floatarg(obj, s, argc, argv, flag, ac, av)) goto usage; - if ((bytespersamp = argv[1].a_w.w_float) < 2 || + if ((bytespersamp = av[1].a_w.w_float) < 2 || bytespersamp > 4) { - argerror(obj, "'-bytes' flag requires a number " - "between 2 and 4"); + argerror(obj, s, argc, argv, + "'-bytes' flag requires a number between 2 and 4"); goto usage; } - argc -= 2; argv += 2; + ac -= 2; av += 2; } - else if (matchstring(flag, "normalize")) + else if (!strcmp(flag, "-normalize")) { - if (!verify_flag(obj, flag)) + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { goto usage; + } normalize = 1; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "wave")) + else if (!strcmp(flag, "-wave")) { - if (!verify_flag(obj, flag)) + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { goto usage; + } filetype = FORMAT_WAVE; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "nextstep")) + else if (!strcmp(flag, "-nextstep")) { - if (!verify_flag(obj, flag)) + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { goto usage; + } filetype = FORMAT_NEXT; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "aiff")) + else if (!strcmp(flag, "-aiff")) { - if (!verify_flag(obj, flag)) - goto usage; + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { + goto usage; + } filetype = FORMAT_AIFF; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "big")) + else if (!strcmp(flag, "-big")) { - if (!verify_flag(obj, flag)) - goto usage; - + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { + goto usage; + } endianness = 1; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "little")) + else if (!strcmp(flag, "-little")) { - if (!verify_flag(obj, flag)) + if (flag_has_unexpected_floatarg(obj, s, argc, argv, + flag, ac, av)) + { goto usage; + } endianness = 0; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "r") || matchstring(flag, "rate")) + else if (!strcmp(flag, "-r") || !strcmp(flag, "-rate")) { - if (!flag_and_floatarg(obj, flag, argc, argv)) + if (flag_missing_floatarg(obj, s, argc, argv, + flag, ac, av)) + { goto usage; - if ((rate = argv[1].a_w.w_float) <= 0) + } + if ((rate = av[1].a_w.w_float) <= 0) { - argerror(obj, "'%s' flag must have a float arg greater than " - "zero", flag); + argerror(obj, s, argc, argv, + "'%s' flag must have a float arg greater than zero", flag); goto usage; } - argc -= 2; argv += 2; + ac -= 2; av += 2; } else { - argerror(obj, "unknown flag '%s'", flag); + argerror(obj, s, argc, argv, "unknown flag '%s'", flag); goto usage; } } - if (!argc || argv->a_type != A_SYMBOL) + if (ac < 1) + { + /* a bit tricky-- writesf~ "open" method doesn't need table args */ + argerror(obj, s, argc, argv, "%s", + s == gensym("open") ? "need a filename" : + "need a filename and table argument(s)"); goto usage; - filesym = argv->a_w.w_symbol; - + } + /* Now that we know we have at least one arg, let's make sure it's + a symbol. */ + if (av->a_type != A_SYMBOL) + { + argerror(obj, s, argc, argv, "filename must be a symbol"); + goto usage; + } + filesym = av->a_w.w_symbol; + /* check if filesym is a flag name, and warn if so. */ + int nodash; + if (file_is_a_flag_name(filesym, &nodash)) + { + post("warning: filename '%s' looks like a flag%s", + filesym->s_name, nodash ? " name" : ""); + } /* check if format not specified and fill in */ if (filetype < 0) { if (strlen(filesym->s_name) >= 5 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF"))) - filetype = FORMAT_AIFF; + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF"))) + { + filetype = FORMAT_AIFF; + } if (strlen(filesym->s_name) >= 6 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF"))) - filetype = FORMAT_AIFF; + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF"))) + { + filetype = FORMAT_AIFF; + } if (strlen(filesym->s_name) >= 5 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND"))) - filetype = FORMAT_NEXT; + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND"))) + { + filetype = FORMAT_NEXT; + } if (strlen(filesym->s_name) >= 4 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU"))) - filetype = FORMAT_NEXT; + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU"))) + { + filetype = FORMAT_NEXT; + } if (filetype < 0) filetype = FORMAT_WAVE; } @@ -878,7 +970,8 @@ static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, { if (filetype == FORMAT_AIFF) { - pd_error(obj, "AIFF floating-point file format unavailable"); + argerror(obj, s, argc, argv, + "AIFF floating-point file format unavailable"); goto usage; } } @@ -902,10 +995,10 @@ static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, else bigendian = endianness; swap = (bigendian != garray_ambigendian()); - argc--; argv++; + ac--; av++; - *p_argc = argc; - *p_argv = argv; + *p_argc = ac; + *p_argv = av; *p_filesym = filesym; *p_filetype = filetype; *p_bytespersamp = bytespersamp; @@ -941,8 +1034,8 @@ static int create_soundfile(t_canvas *canvas, const char *filename, if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd")) strcat(filenamebuf, ".snd"); if (bigendian) - strncpy(nexthdr->ns_fileid, ".snd", 4); - else strncpy(nexthdr->ns_fileid, "dns.", 4); + memcpy(nexthdr->ns_fileid, ".snd", 4); + else memcpy(nexthdr->ns_fileid, "dns.", 4); nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap); nexthdr->ns_length = 0; nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 : @@ -960,18 +1053,18 @@ static int create_soundfile(t_canvas *canvas, const char *filename, if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") && strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) strcat(filenamebuf, ".aif"); - strncpy(aiffhdr->a_fileid, "FORM", 4); + memcpy(aiffhdr->a_fileid, "FORM", 4); aiffhdr->a_chunksize = swap4((uint32_t)(datasize + sizeof(*aiffhdr) + 4), swap); - strncpy(aiffhdr->a_aiffid, "AIFF", 4); - strncpy(aiffhdr->a_fmtid, "COMM", 4); + memcpy(aiffhdr->a_aiffid, "AIFF", 4); + memcpy(aiffhdr->a_fmtid, "COMM", 4); aiffhdr->a_fmtchunksize = swap4(18, swap); aiffhdr->a_nchannels = swap2(nchannels, swap); longtmp = swap4((uint32_t)nframes, swap); memcpy(&aiffhdr->a_nframeshi, &longtmp, 4); aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap); makeaiffsamprate(samplerate, aiffhdr->a_samprate); - strncpy(((char *)(&aiffhdr->a_samprate))+10, "SSND", 4); + memcpy(((char *)(&aiffhdr->a_samprate))+10, "SSND", 4); longtmp = swap4((uint32_t)(datasize + 8), swap); memcpy(((char *)(&aiffhdr->a_samprate))+14, &longtmp, 4); memset(((char *)(&aiffhdr->a_samprate))+18, 0, 8); @@ -982,11 +1075,11 @@ static int create_soundfile(t_canvas *canvas, const char *filename, long datasize = nframes * nchannels * bytespersamp; if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) strcat(filenamebuf, ".wav"); - strncpy(wavehdr->w_fileid, "RIFF", 4); + memcpy(wavehdr->w_fileid, "RIFF", 4); wavehdr->w_chunksize = swap4((uint32_t)(datasize + sizeof(*wavehdr) - 8), swap); - strncpy(wavehdr->w_waveid, "WAVE", 4); - strncpy(wavehdr->w_fmtid, "fmt ", 4); + memcpy(wavehdr->w_waveid, "WAVE", 4); + memcpy(wavehdr->w_fmtid, "fmt ", 4); wavehdr->w_fmtchunksize = swap4(16, swap); wavehdr->w_fmttag = swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap); @@ -996,11 +1089,10 @@ static int create_soundfile(t_canvas *canvas, const char *filename, swap4((int)(samplerate * nchannels * bytespersamp), swap); wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap); wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap); - strncpy(wavehdr->w_datachunkid, "data", 4); + memcpy(wavehdr->w_datachunkid, "data", 4); wavehdr->w_datachunksize = swap4((uint32_t)datasize, swap); headersize = sizeof(t_wave); } - canvas_makefilename(canvas, filenamebuf, buf2, FILENAME_MAX); sys_bashfilename(buf2, buf2); if ((fd = sys_open(buf2, BINCREATE, 0666)) < 0) @@ -1383,9 +1475,17 @@ static void soundfiler_readascii(t_soundfiler *x, char *filename, -maxsize <max-size> */ +#define RAWSYNTAX "'-raw' flag syntax: " \ + "<headersize> <channels> <bytespersample> " \ + "<endianness: 'b' for big, 'l' for little, 'n' for auto>" + static void soundfiler_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + /* copies of argc, argv so we can iterate without mutating + the originals (needed for error reporting below) */ + int ac = argc; + t_atom *av = argv; t_soundfile_info info; int resize = 0, i, j; long skipframes = 0, finalsize = 0, @@ -1404,48 +1504,100 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, info.headersize = -1; info.bigendian = 0; info.bytelimit = 0x7fffffff; - while (argc > 0 && argv->a_type == A_SYMBOL) + while (ac > 0 && atom_getsymbol(av)->s_name[0] == '-') { - char *flag = argv->a_w.w_symbol->s_name; - if (matchstring(flag, "skip")) + char *flag = av->a_w.w_symbol->s_name; + if (!strcmp(flag, "-skip")) { - if (!flag_and_floatarg(x, flag, argc, argv)) - goto usage; - if ((skipframes = argv[1].a_w.w_float) < 0) + if (flag_missing_floatarg(x, s, argc, argv, flag, ac, av)) + goto done; + if ((skipframes = av[1].a_w.w_float) < 0) { - argerror(x, "'-skip' flag does not allow a negative number"); - goto usage; + argerror(x, s, argc, argv, + "'-skip' flag does not allow a negative number"); + goto done; } - argc -= 2; argv += 2; + ac -= 2; av += 2; } - else if (matchstring(flag, "ascii")) + else if (!strcmp(flag, "-ascii")) { - if (!verify_flag(x, flag)) - goto usage; if (info.headersize >= 0) post("soundfiler_read: '-raw' overidden by '-ascii'"); ascii = 1; - argc--; argv++; + ac--; av++; } - else if (matchstring(flag, "raw")) + else if (!strcmp(flag, "-raw")) { - if (!verify_flag(x, flag)) - goto usage; if (ascii) post("soundfiler_read: '-raw' overridden by '-ascii'"); - if (argc < 5 || - argv[1].a_type != A_FLOAT || - ((info.headersize = argv[1].a_w.w_float) < 0) || - argv[2].a_type != A_FLOAT || - ((info.channels = argv[2].a_w.w_float) < 1) || - (info.channels > MAXSFCHANS) || - argv[3].a_type != A_FLOAT || - ((info.bytespersample = argv[3].a_w.w_float) < 2) || - (info.bytespersample > 4) || - argv[4].a_type != A_SYMBOL || - ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b' - && endianness != 'l' && endianness != 'n')) - goto usage; + if (ac < 5) + { + argerror(x, s, argc, argv, + "'-raw' flag needs four arguments\n" RAWSYNTAX); + goto done; + } + if (av[1].a_type != A_FLOAT) + { + argerror(x, s, argc, argv, + "'-raw' flag needs a float for the headersize\n" RAWSYNTAX); + goto done; + } + info.headersize = av[1].a_w.w_float; + if (info.headersize < 0) + { + argerror(x, s, argc, argv, + "'-raw' headersize cannot be less than zero\n"RAWSYNTAX); + goto done; + } + if (av[2].a_type != A_FLOAT) + { + argerror(x, s, argc, argv, + "'-raw' flag needs a float to specify channels\n"RAWSYNTAX); + goto done; + } + info.channels = av[2].a_w.w_float; + if (info.channels < 1) + { + argerror(x, s, argc, argv, + "'-raw' flag needs at least one channel\n" RAWSYNTAX); + goto done; + } + if (info.channels > MAXSFCHANS) + { + argerror(x, s, argc, argv, + "'-raw' channels value %d exceeds " + "maximum of %d channels\n" RAWSYNTAX, + info.channels, MAXSFCHANS); + goto done; + } + if (av[3].a_type != A_FLOAT) + { + argerror(x, s, argc, argv, + "'-raw' flag needs a float to specify " + "bytes per sample\n" RAWSYNTAX); + goto done; + } + info.bytespersample = av[3].a_w.w_float; + if (info.bytespersample < 2) + { + argerror(x, s, argc, argv, + "'-raw' bytes per sample must be at least 2\n" RAWSYNTAX); + goto done; + } + if (info.bytespersample > 4) + { + argerror(x, s, argc, argv, + "'-raw' bytes per sample must be less than 4\n" RAWSYNTAX); + goto done; + } + if (av[4].a_type != A_SYMBOL || + ((endianness = av[4].a_w.w_symbol->s_name[0]) != 'b' + && endianness != 'l' && endianness != 'n')) + { + argerror(x, s, argc, argv, + "'-raw' endianness must be 'l' or 'b' or 'n'\n" RAWSYNTAX); + goto done; + } if (endianness == 'b') info.bigendian = 1; else if (endianness == 'l') @@ -1453,61 +1605,85 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, else info.bigendian = garray_ambigendian(); info.samplerate = sys_getsr(); - argc -= 5; argv += 5; + ac -= 5; av += 5; } - else if (matchstring(flag, "resize")) + else if (!strcmp(flag, "-resize")) { - if (!verify_flag(x, flag)) - goto usage; + if (flag_has_unexpected_floatarg(x, s, argc, argv, + flag, ac, av)) + { + goto done; + } resize = 1; - argc -= 1; argv += 1; + ac -= 1; av += 1; } - else if (matchstring(flag, "maxsize")) + else if (!strcmp(flag, "-maxsize")) { - if (!flag_and_floatarg(x, flag, argc, argv)) - goto usage; - maxsize = argv[1].a_w.w_float; - if (maxsize > LONG_MAX) + if (flag_missing_floatarg(x, s, argc, argv, flag, ac, av)) + goto done; + t_float fmax = av[1].a_w.w_float; + if (fmax > (t_float)LONG_MAX) { - argerror(x, "'-maxsize' flag cannot be greater than %d. " - "Setting '-maxsize' to %d and continuing", LONG_MAX, - LONG_MAX); + argerror(x, s, argc, argv, + "'-maxsize' overflow detected. " + "Setting '-maxsize' to maximum legal value (%g) and " + "continuing...", (t_float)LONG_MAX); maxsize = LONG_MAX; } - if (maxsize < 0) + else if (fmax < 0.) { - argerror(x, "'-maxsize' flag cannot be less than zero"); - goto usage; + argerror(x, s, argc, argv, + "'-maxsize' flag cannot be less than zero"); + goto done; + } + else + { + maxsize = (long)fmax; } resize = 1; /* maxsize implies resize. */ - argc -= 2; argv += 2; + ac -= 2; av += 2; } else { - argerror(x, "unknown flag '%s'", flag); - goto usage; + argerror(x, s, argc, argv, "unknown flag '%s'", flag); + goto done; } } - if (argc < 1 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) - goto usage; - filename = argv[0].a_w.w_symbol->s_name; - argc--; argv++; + if (ac < 1) + { + argerror(x, s, argc, argv, "need filename and table argument(s)"); + goto done; + } + if (ac > MAXSFCHANS + 1) + { + argerror(x, s, argc, argv, + "cannot write more than %d channels", MAXSFCHANS); + goto done; + } + if (av->a_type != A_SYMBOL) + { + argerror(x, s, argc, argv, "filename must be a symbol"); + goto done; + } + filename = av[0].a_w.w_symbol->s_name; + ac--; av++; - for (i = 0; i < argc; i++) + for (i = 0; i < ac; i++) { int vecsize; - if (argv[i].a_type != A_SYMBOL) - goto usage; + if (av[i].a_type != A_SYMBOL) + goto done; if (!(garrays[i] = - (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) + (t_garray *)pd_findbyclass(av[i].a_w.w_symbol, garray_class))) { - pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name); + argerror(x, s, argc, argv, "%s: no such table", + av[i].a_w.w_symbol->s_name); goto done; } else if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i])) error("%s: bad template for tabwrite", - argv[i].a_w.w_symbol->s_name); + av[i].a_w.w_symbol->s_name); if (finalsize && finalsize != vecsize && !resize) { post("soundfiler_read: arrays have different lengths; resizing..."); @@ -1517,7 +1693,7 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, } if (ascii) { - soundfiler_readascii(x, filename, argc, garrays, vecs, resize, + soundfiler_readascii(x, filename, ac, garrays, vecs, resize, finalsize); return; } @@ -1525,21 +1701,33 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, if (fd < 0) { - pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ? + argerror(x, s, argc, argv, "%s: %s", filename, (errno == EIO ? "unknown or bad header format" : strerror(errno))); - goto done; + /* don't bail yet so we can potentially give a warning below */ } + /* check if the filename looks like a flag; if so, post a warning */ + int nodash; + if (file_is_a_flag_name(gensym(filename), &nodash)) + { + post("warning: filename '%s' looks like a flag%s", + filename, nodash ? " name" : ""); + } + + /* Now that we've posted our warning, bail if we couldn't open the file */ + if (fd < 0) + goto done; + if (resize) { /* figure out what to resize to */ long poswas, eofis, framesinfile; - + poswas = lseek(fd, 0, SEEK_CUR); eofis = lseek(fd, 0, SEEK_END); if (poswas < 0 || eofis < 0 || eofis < poswas) { - pd_error(x, "soundfiler_read: lseek failed: %ld..%ld", poswas, + argerror(x, s, argc, argv, "lseek failed: %ld..%ld", poswas, eofis); goto done; } @@ -1547,7 +1735,7 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, framesinfile = (eofis - poswas) / (info.channels * info.bytespersample); if (framesinfile > maxsize) { - pd_error(x, "soundfiler_read: truncated to %ld elements", maxsize); + argerror(x, s, argc, argv, "truncated to %ld elements", maxsize); framesinfile = maxsize; } if (framesinfile > info.bytelimit / @@ -1557,18 +1745,19 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, (info.channels * info.bytespersample); } finalsize = framesinfile; - for (i = 0; i < argc; i++) + for (i = 0; i < ac; i++) { int vecsize; garray_resize_long(garrays[i], finalsize); - /* for sanity's sake let's clear the save-in-patch flag here */ + + /* for sanity's sake let's clear the save-in-patch flag here */ garray_setsaveit(garrays[i], 0); if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i]) || (vecsize != framesinfile)) { /* if the resize failed, garray_resize reported the error */ - pd_error(x, "resize failed"); + argerror(x, s, argc, argv, "resize failed"); goto done; } } @@ -1586,14 +1775,14 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, nitems = fread(sampbuf, info.channels * info.bytespersample, thisread, fp); if (nitems <= 0) break; - soundfile_xferin_float(info.channels, argc, (t_float **)vecs, itemsread, + soundfile_xferin_float(info.channels, ac, (t_float **)vecs, itemsread, (unsigned char *)sampbuf, nitems, info.bytespersample, info.bigendian, sizeof(t_word)/sizeof(t_sample)); itemsread += nitems; } /* zero out remaining elements of vectors */ - for (i = 0; i < argc; i++) + for (i = 0; i < ac; i++) { int vecsize; if (garray_getfloatwords(garrays[i], &vecsize, &vecs[i])) @@ -1601,7 +1790,7 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, vecs[i][j].w_float = 0; } /* zero out vectors in excess of number of channels */ - for (i = info.channels; i < argc; i++) + for (i = info.channels; i < ac; i++) { int vecsize; t_word *foo; @@ -1610,15 +1799,10 @@ static void soundfiler_read(t_soundfiler *x, t_symbol *s, foo[j].w_float = 0; } /* do all graphics updates */ - for (i = 0; i < argc; i++) + for (i = 0; i < ac; i++) garray_redraw(garrays[i]); fclose(fp); fd = -1; - goto done; -usage: - post("usage: read [flags] filename tablename..."); - post("flags: -skip <n> -resize -maxsize <n> ..."); - post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>."); done: if (fd >= 0) sys_close(fd); @@ -1632,6 +1816,9 @@ done: long soundfiler_dowrite(void *obj, t_canvas *canvas, int argc, t_atom *argv, t_soundfile_info *info) { + /* workaround for ruthless argc/argv mutation in writeargparse... */ + int original_argc = argc; + t_atom *original_argv = argv; int swap, filetype, normalize, i; long onset, nframes, itemswritten = 0, j; t_garray *garrays[MAXSFCHANS]; @@ -1643,13 +1830,26 @@ long soundfiler_dowrite(void *obj, t_canvas *canvas, t_float samplerate; t_symbol *filesym; - if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + if (soundfiler_writeargparse(obj, gensym("write"), &argc, &argv, + &filesym, &filetype, &info->bytespersample, &swap, &info->bigendian, &normalize, &onset, &nframes, &samplerate)) goto usage; info->channels = argc; - if (info->channels < 1 || info->channels > MAXSFCHANS) + /* Need at least one table name for a channel to write... */ + if (info->channels < 1) + { + argerror(obj, gensym("write"), original_argc, original_argv, + "argument for table name missing"); + goto usage; + } + /* Can't have more than max number of channels to write */ + if (info->channels > MAXSFCHANS) + { + argerror(obj, gensym("write"), original_argc, original_argv, + "cannot have more than %d channels", MAXSFCHANS); goto usage; + } if (samplerate < 0) info->samplerate = sys_getsr(); else @@ -1658,13 +1858,19 @@ long soundfiler_dowrite(void *obj, t_canvas *canvas, { int vecsize; if (argv[i].a_type != A_SYMBOL) + { + argerror(obj, gensym("write"), original_argc, original_argv, + "table name must be a symbol"); goto usage; + } if (!(garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) { - pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name); + argerror(obj, gensym("write"), original_argc, original_argv, + "%s: no such table", argv[i].a_w.w_symbol->s_name); goto fail; } + /* need to check this one-- */ else if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i])) error("%s: bad template for tabwrite", argv[i].a_w.w_symbol->s_name); @@ -1673,7 +1879,8 @@ long soundfiler_dowrite(void *obj, t_canvas *canvas, } if (nframes <= 0) { - pd_error(obj, "soundfiler_write: no samples at onset %ld", onset); + argerror(obj, gensym("write"), original_argc, original_argv, + "no samples at onset %ld", onset); goto fail; } /* find biggest sample for normalizing */ @@ -1738,10 +1945,6 @@ long soundfiler_dowrite(void *obj, t_canvas *canvas, } return ((float)itemswritten); usage: - post("usage: write [flags] filename tablename..."); - post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ..."); - post("-big -little -normalize"); - post("(defaults to a 16-bit wave file)."); fail: if (fd >= 0) sys_close(fd); @@ -2800,17 +3003,15 @@ static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv) { writesf_stop(x); } - if (soundfiler_writeargparse(x, &argc, + if (soundfiler_writeargparse(x, gensym("open"), &argc, &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes, &samplerate)) { - pd_error(x, - "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ..."); - post("... [-big,-little] [-rate ####] filename"); + /* errors handled above in soundfiler_writeargparse */ return; } if (normalize || onset || (nframes != 0x7fffffff)) - pd_error(x, "normalize/onset/nframes argument to writesf~: ignored"); + pd_error(x, "normalize/skip/nframes argument to writesf~: ignored"); if (argc) pd_error(x, "extra argument(s) to writesf~: ignored"); pthread_mutex_lock(&x->x_mutex); diff --git a/scripts/regression_tests.pd b/scripts/regression_tests.pd index 4b8a8c20c..1e09b6335 100644 --- a/scripts/regression_tests.pd +++ b/scripts/regression_tests.pd @@ -1,4 +1,4 @@ -#N canvas -9 -9 771 392 12; +#N canvas 128 123 771 392 12; #X obj 465 281 r \$0-result; #X obj 212 239 bng 15 250 50 0 empty empty Run_all 17 7 0 10 #fcfcfc #000000 #000000; @@ -25,7 +25,7 @@ is handy for some binbuf tests.; #X obj 198 659 rtest makefilename_double_percent; #X obj 198 710 rtest makefilename_code_coverage; #N canvas 461 242 450 323 (subpatch) 0; -#X restore 201 2605 pd; +#X restore 201 2765 pd; #X obj 198 761 rtest makefilename_default; #X obj 198 812 rtest makefilename_default_bang; #X obj 198 863 rtest makefilename_float; @@ -66,6 +66,8 @@ is handy for some binbuf tests.; #X obj 198 2416 rtest inlet~_fwd_large_message; #X obj 198 2471 rtest pow~_negative_numbers; #X obj 198 2526 rtest encapsulate; +#X obj 198 2581 rtest soundfiler_read_coverage; +#X obj 198 2636 rtest writesf~_open_coverage; #X connect 0 0 27 0; #X connect 1 0 4 0; #X connect 2 0 42 0; @@ -117,3 +119,5 @@ is handy for some binbuf tests.; #X connect 57 0 58 0; #X connect 58 0 59 0; #X connect 59 0 60 0; +#X connect 60 0 61 0; +#X connect 61 0 62 0; diff --git a/scripts/regression_tests/soundfiler_read_coverage.pd b/scripts/regression_tests/soundfiler_read_coverage.pd new file mode 100644 index 000000000..ca022f36b --- /dev/null +++ b/scripts/regression_tests/soundfiler_read_coverage.pd @@ -0,0 +1,56 @@ +#N canvas 106 64 1067 916 12; +#X obj 53 609 ../utils/method-error soundfiler; +#X obj 168 684 route bang; +#X obj 168 713 f 0; +#X obj 235 713 b; +#X obj 235 742 f 1; +#X obj 53 773 list prepend; +#X obj 53 638 list prepend this message should trigger an error:; +#X obj 53 10 inlet; +#X msg 53 89 bang; +#X obj 53 581 receive \$0-; +#N canvas 771 533 450 425 \$0-too-many-channels 0; +#X obj 80 31 inlet; +#X obj 80 60 f \$0; +#X msg 80 110 \; \$1- read z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X restore 169 128 pd \$0-too-many-channels; +#X msg 169 97 bang; +#X obj 53 39 trigger bang bang; +#X text 182 24 This just covers the flags. We probably need to add +file-loading tests at some point.; +#X obj 53 802 outlet; +#X msg 53 158 \; \$1- \$2 -skip \; \$1- \$2 -skip rope \; \$1- \$2 +-skip -1 \; \$1- \$2 -raw \; \$1- \$2 -raw dope \; \$1- \$2 -raw \; +\$1- \$2 -raw 0 \; \$1- \$2 -raw 0 0 \; \$1- \$2 -raw 0 0 0 \; \$1- +\$2 -raw dope 0 0 0 \; \$1- \$2 -raw 0 dope 0 0 \; \$1- \$2 -raw 0 +1 dope 0 \; \$1- \$2 -raw 0 1 2 dope \; \$1- \$2 -raw -1 0 0 0 \; \$1- +\$2 -raw 0 0 2 l \; \$1- \$2 -raw 0 1024 2 n \; \$1- \$2 -raw 0 1 1 +n \; \$1- \$2 -raw 0 1 5 n \; \$1- \$2 -raw 0 1 2 z \; \$1- \$2 -resize +12 \; \$1- \$2 -maxsize \; \$1- \$2 -maxsize 1e+19 \; \$1- \$2 -reginald +\; \$1- \$2; +#X obj 53 118 list \$0 read; +#X connect 0 0 6 0; +#X connect 0 1 1 0; +#X connect 1 0 2 0; +#X connect 1 1 3 0; +#X connect 2 0 5 1; +#X connect 3 0 4 0; +#X connect 4 0 5 1; +#X connect 5 0 14 0; +#X connect 6 0 5 0; +#X connect 7 0 12 0; +#X connect 8 0 16 0; +#X connect 9 0 0 0; +#X connect 11 0 10 0; +#X connect 12 0 8 0; +#X connect 12 1 11 0; +#X connect 16 0 15 0; diff --git a/scripts/regression_tests/soundfiler_write_coverage.pd b/scripts/regression_tests/soundfiler_write_coverage.pd new file mode 100644 index 000000000..aaeee8cfa --- /dev/null +++ b/scripts/regression_tests/soundfiler_write_coverage.pd @@ -0,0 +1,56 @@ +#N canvas 641 120 1067 916 12; +#X obj 53 669 ../utils/method-error soundfiler; +#X obj 168 744 route bang; +#X obj 168 773 f 0; +#X obj 235 773 b; +#X obj 235 802 f 1; +#X obj 53 833 list prepend; +#X obj 53 698 list prepend this message should trigger an error:; +#X obj 53 10 inlet; +#X msg 53 89 bang; +#X obj 53 641 receive \$0-; +#N canvas 771 542 450 425 \$0-too-many-channels 1; +#X obj 80 31 inlet; +#X obj 80 60 f \$0; +#X msg 80 110 \; \$1- write z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z +z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X restore 169 128 pd \$0-too-many-channels; +#X msg 169 97 bang; +#X obj 53 39 trigger bang bang; +#X text 182 24 This just covers the flags. We probably need to add +file-loading tests at some point.; +#X obj 53 862 outlet; +#X obj 53 118 list \$0 write; +#X msg 53 158 \; \$1- \$2 -skip \; \$1- \$2 -skip rope \; \$1- \$2 +-skip -1 \; \$1- \$2 -nframes \; \$1- \$2 -nframes dope \; \$1- \$2 +-nframes -1 \; \$1- \$2 -normalize 12 \; \$1- \$2 -bytes \; \$1- \$2 +-bytes 1 \; \$1- \$2 -bytes 5 \; \$1- \$2 -resize 12 \; \$1- \$2 -wave +1 \; \$1- \$2 -nextstep 1 \; \$1- \$2 -aiff 1 \; \$1- \$2 -big 1 \; +\$1- \$2 -little 1 \; \$1- \$2 -r \; \$1- \$2 -rate \; \$1- \$2 -r +0 \; \$1- \$2 -rate 0 \; \$1- \$2 -reginald \; \$1- \$2 \; \$1- \$2 +12 \; \$1- \$2 foo 12 \; \$1- \$2 -bytes 4 -aiff foo \; \$1- \$2 foo +no_table \;; +#X connect 0 0 6 0; +#X connect 0 1 1 0; +#X connect 1 0 2 0; +#X connect 1 1 3 0; +#X connect 2 0 5 1; +#X connect 3 0 4 0; +#X connect 4 0 5 1; +#X connect 5 0 14 0; +#X connect 6 0 5 0; +#X connect 7 0 12 0; +#X connect 8 0 15 0; +#X connect 9 0 0 0; +#X connect 11 0 10 0; +#X connect 12 0 8 0; +#X connect 12 1 11 0; +#X connect 15 0 16 0; diff --git a/scripts/regression_tests/writesf~_open_coverage.pd b/scripts/regression_tests/writesf~_open_coverage.pd new file mode 100644 index 000000000..70bb25d73 --- /dev/null +++ b/scripts/regression_tests/writesf~_open_coverage.pd @@ -0,0 +1,37 @@ +#N canvas 72 64 1067 916 12; +#X obj 138 754 route bang; +#X obj 138 783 f 0; +#X obj 205 783 b; +#X obj 205 812 f 1; +#X obj 23 843 list prepend; +#X obj 23 708 list prepend this message should trigger an error:; +#X obj 23 20 inlet; +#X msg 23 99 bang; +#X obj 23 651 receive \$0-; +#X text 42 54 This just covers the flags. We probably need to add file-loading +tests at some point.; +#X obj 23 872 outlet; +#X msg 23 168 \; \$1- \$2 -skip \; \$1- \$2 -skip rope \; \$1- \$2 +-skip -1 \; \$1- \$2 -nframes \; \$1- \$2 -nframes dope \; \$1- \$2 +-nframes -1 \; \$1- \$2 -normalize 12 \; \$1- \$2 -bytes \; \$1- \$2 +-bytes 1 \; \$1- \$2 -bytes 5 \; \$1- \$2 -resize 12 \; \$1- \$2 -wave +1 \; \$1- \$2 -nextstep 1 \; \$1- \$2 -aiff 1 \; \$1- \$2 -big 1 \; +\$1- \$2 -little 1 \; \$1- \$2 -r \; \$1- \$2 -rate \; \$1- \$2 -r +0 \; \$1- \$2 -rate 0 \; \$1- \$2 -reginald \; \$1- \$2 \; \$1- \$2 +12 \; \$1- \$2 foo 12 \; \$1- \$2 -bytes 4 -aiff foo \; \$1- \$2 foo +extra_arg \;; +#X obj 23 128 list \$0 open; +#X obj 23 679 ../utils/method-error writesf~; +#X connect 0 0 1 0; +#X connect 0 1 2 0; +#X connect 1 0 4 1; +#X connect 2 0 3 0; +#X connect 3 0 4 1; +#X connect 4 0 10 0; +#X connect 5 0 4 0; +#X connect 6 0 7 0; +#X connect 7 0 12 0; +#X connect 8 0 13 0; +#X connect 12 0 11 0; +#X connect 13 0 5 0; +#X connect 13 1 0 0; diff --git a/scripts/utils/method-error.pd b/scripts/utils/method-error.pd new file mode 100644 index 000000000..bc2664b8a --- /dev/null +++ b/scripts/utils/method-error.pd @@ -0,0 +1,20 @@ +#N canvas 861 254 781 553 12; +#X obj 96 28 inlet; +#X obj 201 125 unpost; +#X obj 176 178 list; +#X obj 176 267 outlet; +#X obj 240 178 \$1; +#X obj 96 267 outlet; +#X obj 96 86 trigger anything bang anything bang; +#X text 143 29 test a method that triggers; +#X text 143 49 an error; +#X text 55 220 the message; +#X text 159 221 the error (formatted as a single symbol); +#X connect 0 0 6 0; +#X connect 1 0 2 1; +#X connect 1 1 4 0; +#X connect 2 0 3 0; +#X connect 6 0 5 0; +#X connect 6 1 2 0; +#X connect 6 2 1 0; +#X connect 6 3 2 1; -- GitLab