/* Spectroscopic Toolkit version 1.97 by Pieter Suurmond, november 13, 2011. Copyright (c) 2000-2011 - Pieter Suurmond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is requested to send the modifications to the original developer so that they can be incorporated into the canonical version. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This sourcefile contains ST's main(). Under UNIX (OSX, LINUX, IRIX, etc.), one may use the provided makefile to compile ST. Under Mac OS (from 7.6 up to 9), MS Windows, DOS, etc., put the following sourcefiles in a standard ANSI/ISO C-project: 1) All .c files in the current directory. 2) All .c files in subdirectory comp/. 3) Only sourcefile gt.c in subdirectory gt/ (nothing else!). File comp/c00_example.c demonstrates the use of the addWAVELET() function but doesn't use any spectroscopic data yet. Most other files in subdir comp/ expect the spectroscopic database file of Robert L. Kurucz to be present. */ #include /* Standard headers. */ #include #include #include /* For time_t, time() and ctime(). */ #include "ST_wavelets.h" /* For ENGINE object and WAVELET functions. */ #include "ST_score.h" /* To produce graphic scores in JPEG format. */ #include "c00.h" /* Prototypes for all composition functions. */ #include "ST_export.h" /* To export (text)data to Csound, SC, etc. */ #include "ST_pconv.h" /*----------------------------------------------------------------------------*/ typedef struct /* Each test, study and work of art has a unique opus number. */ { long number; const char* title; short (*fun)(ENGINEp, FILE*); /* Pointer to one of the functions. */ } OPUS; /* Returns NULL or a pointer to a valid OPUS structure. When 0, -1, or any other invalid composition number is specified as 'number', all choices will be listed. Argument 'dest' may be stdout, stderr, NULL, or some textfile pointer. The function is called by main() once. */ static const OPUS* lookup_opus(long number, FILE* dest) { static const OPUS oeuvre[] = /* Collection of output products: */ { /* Synthesis examples and tests: */ { 1000, "Example 0: Gaussians (using no database) 00:21", c00_gaussTest }, { 1001, "Example 1: Clip test (using no database) 00:04", c00_clipTest }, { 1002, "Example 2: Overtones (using no database) 00:23", c00_overtoneTest }, { 1003, "Example 3: Phase test (using no database) 00:19", c00_phaseTest }, { 1004, "Example 4: Exponent test (using no database) 01:31", c00_expTest }, /* From 13 studies in nanotonality: */ { 1301, "'Mendelejev progression H to S' (study 2001) 06:55", c04_Mendelejev }, { 1302, "'Two Weeks - alchemist metals' (study 2001) 06:22", c01_twoWeeks }, { 1307, "'Hydrogen horizontally smeared' (study 2001) 01:04", c03_H }, /* Separate clouds (oscillator sync): */ { 10100, "Hydrogen (H) cloud (oscillator sync) 01:46", c01_os_cloud_H }, { 10200, "Helium (He) cloud (oscillator sync) 01:46", c01_os_cloud_He }, { 10201, "Helium+ (He+ ion) cloud (oscillator sync) 01:46", c01_os_cloud_HeP }, { 10300, "Lithium (Li) cloud (oscillator sync) 01:46", c01_os_cloud_Li }, { 10400, "Beryllium (Be) cloud (oscillator sync) 01:46", c01_os_cloud_Be }, { 10500, "Boron (B) cloud (oscillator sync) 01:46", c01_os_cloud_B }, { 10600, "Carbon (C) cloud (oscillator sync) 01:46", c01_os_cloud_C }, { 10700, "Nitrogen (N) cloud (oscillator sync) 01:46", c01_os_cloud_N }, { 10800, "Oxygen (O) cloud (oscillator sync) 01:46", c01_os_cloud_O }, { 10900, "Fluorine (F) cloud (oscillator sync) 01:46", c01_os_cloud_F }, { 11000, "Neon (Ne) cloud (oscillator sync) 01:46", c01_os_cloud_Ne }, { 11100, "Sodium (Na) cloud (oscillator sync) 01:46", c01_os_cloud_Na }, { 11200, "Magnesium (Mg) cloud (oscillator sync) 01:46", c01_os_cloud_Mg }, { 11300, "Aluminium (Al) cloud (oscillator sync) 01:46", c01_os_cloud_Al }, { 11400, "Silicon (Si) cloud (oscillator sync) 01:46", c01_os_cloud_Si }, { 11500, "Phosphorus (P) cloud (oscillator sync) 01:46", c01_os_cloud_P }, { 11600, "Sulfur (S) cloud (oscillator sync) 01:46", c01_os_cloud_S }, { 11700, "Chlorine (Cl) cloud (oscillator sync) 01:46", c01_os_cloud_Cl }, { 11800, "Argon (Ar) cloud (oscillator sync) 01:46", c01_os_cloud_Ar }, { 12600, "Iron (Fe) cloud (42806 freqs, oscill.sync) 01:46", c01_os_cloud_Fe }, { 12700, "Cobalt (Co) cloud (oscillator sync) 01:46", c01_os_cloud_Co }, { 12800, "Nickel (Ni) cloud (oscillator sync) 01:46", c01_os_cloud_Ni }, { 12900, "Copper (Cu) cloud (oscillator sync) 01:46", c01_os_cloud_Cu }, { 13000, "Zinc (Zn) cloud (oscillator sync) 01:46", c01_os_cloud_Zn }, { 14700, "Silver (Ag) cloud (oscillator sync) 01:46", c01_os_cloud_Ag }, { 15000, "Tin (Sn) cloud (oscillator sync) 01:46", c01_os_cloud_Sn }, { 15500, "Cesium (Cs) cloud (oscillator sync) 01:46", c01_os_cloud_Cs }, { 17900, "Gold (Au) cloud (oscillator sync) 01:46", c01_os_cloud_Au }, { 18000, "Mercury (Hg) cloud (oscillator sync) 01:46", c01_os_cloud_Hg }, { 18200, "Lead (Pb) cloud (oscillator sync) 01:46", c01_os_cloud_Pb }, { 19000, "Thorium (Th) cloud (oscillator sync) 01:46", c01_os_cloud_Th }, { 19200, "Uranium (U) cloud (oscillator sync) 01:46", c01_os_cloud_U }, /* Same, but with cosine-phases aligned at the envelope tops. */ { 20100, "Hydrogen (H) cloud 01:46", c01_cloud_H }, { 20200, "Helium (He) cloud 01:46", c01_cloud_He }, { 20201, "Helium+ (He+ ion) cloud 01:46", c01_cloud_HeP }, { 20300, "Lithium (Li) cloud 01:46", c01_cloud_Li }, { 20600, "Carbon (C) cloud 01:46", c01_cloud_C }, { 20700, "Nitrogen (N) cloud 01:46", c01_cloud_N }, { 20800, "Oxygen (O) cloud 01:46", c01_cloud_O }, { 20900, "Fluorine (F) cloud 01:46", c01_cloud_F }, { 21400, "Silicon (Si) cloud 01:46", c01_cloud_Si }, { 21600, "Sulfur (S) cloud 01:46", c01_cloud_S }, { 27900, "Gold (Au) cloud 01:46", c01_cloud_Au }, /* From Composition 2003: */ { 20030001, "'Boog' (composition 2003 part 1) 05:09", c07_comp2003_1 }, { 20030002, "'Vogelbellen' (composition 2003 part 2) 07:59", c07_comp2003_2 }, { 20030102, "'Hydrogen Birds' (2002) 08:10", c05_HydrogenBirds }, { 20030201, "'Hydrogen Bells' (2003) 01:13", c06_Hbells }, { 20030202, "'Hydrogen and Helium+ Bells' (2003) 02:43", c06_HHe_bells }, /* 2004 experiments: */ #if (0) { 200401, "'Mendelejev regress...' xx:xx", c04_Regress }, #endif /* Composition 2011: */ { 20110001, "'Rossi clouds' cold fusion H + Ni -> Cu. ??:??", c08_rossi2011 }, /* Terminate with NULL: */ { 0, "", /* TOTAL= 41:00*/ NULL } }; const OPUS* opus = oeuvre; while (opus->fun) { if (opus->number == number) { if (dest != NULL) fprintf(dest, "RENDERING #%ld: %s.\n", opus->number, opus->title); return opus; } opus++; } if (dest != NULL) /* List all choices when number is not found. */ { if ((number != -1L) && (number != 0L)) fprintf(dest, "Number %ld has not been assigned yet!\n", number); fprintf(dest, "USAGE: ST [bits] [samplerate] number \n"); fprintf(dest, "Bits may be 8, 16 or 24. Samplerate must be in Hertz.\n"); if (number == 0L) fprintf(dest, "Numbers of examples, studies, and other works of art:\n"); else fprintf(dest, "Use one of the following numbers as (last) parameter:\n"); fprintf(dest, "%10ld: Print usage and list numbers.\n", 0L); opus = oeuvre; while (opus->fun != NULL) { fprintf(dest, "%10ld: %s.\n", opus->number, opus->title); opus++; } } return NULL; } /*----------------------------------------------------------------------------*/ /* Called by main() once to report the total calculation time. */ static void print_duration(long seconds, FILE* dest) { fprintf(dest, "Calculation took about "); if (seconds > 172800) fprintf(dest, "%ld days", (seconds + 43200L) / 86400L); else if (seconds > 7200) fprintf(dest, "%ld hours", (seconds + 1800L) / 3600L); else if (seconds > 120) fprintf(dest, "%ld minutes", (seconds + 30L) / 60L); else fprintf(dest, "%ld seconds", seconds); fprintf(dest, ".\n"); } /*----------------------------------------------------------------------------*/ int main(int argc, char** argv) /* Number of args and array of args */ { /* (first is name of the program). */ const char* version = "ST1.97"; /* Update whenever you change things. */ FILE* consoleMSG = stdout; /* Make NULL to suppress verbosity. */ int bits = 16; /* Default rendering settings, may be */ long samplerate = 44100L; /* overruled by optional arguments. */ const long dumpframes = 16384L; /* Number of frames per transfer. */ long number = -1L; /* -1 and 0 are invalid opus numbers. */ int e = 0; /* Error return code. */ long something; const OPUS* opus; if (consoleMSG != NULL) fprintf(consoleMSG, "%s by Pieter Suurmond, november 13, 2011.\n", version); if (argc < 2) { if (consoleMSG != NULL) fprintf(consoleMSG, "Parameter(s) missing!\n"); } else if (argc > 4) { if (consoleMSG != NULL) fprintf(consoleMSG, "Too many parameters!\n"); } else /* Ok, parse parameters, syntax is 'ST [bits] [samplerate] number'. */ { number = atol(argv[--argc]); /* Read last arg. */ if (argc > 1) { something = atol(argv[--argc]); /* Read middle or first arg. */ if (argc > 1) { bits = atol(argv[--argc]); /* Read first arg. */ samplerate = something; } else /* Try to be clever. */ { /* Resolve: ambiguous */ if (something < 64L) /* number of arguments. */ bits = (int)something; else samplerate = something; } } } /* Prevent failure later on. */ if ((bits & 7) || (bits > 24) || (bits < 8)) { number = -1L; /* Make number invalid to show usage. */ if (consoleMSG != NULL) fprintf(consoleMSG, "Invalid number of bits: %d!\n", bits); } if ((samplerate > 96000L) || (samplerate < 64L)) { number = -1L; /* Make number invalid to show usage. */ if (consoleMSG != NULL) fprintf(consoleMSG, "Invalid samplerate: %ld Hz!\n", samplerate); } opus = lookup_opus(number, consoleMSG); if (opus != NULL) { char fname[128]; char stamp[64]; FILE* logfileMSG; time_t t1, t2; sprintf(fname, "%s-%ld.log", version, number); logfileMSG = fopen(fname, "wt"); /* Create or overwrite. */ if (logfileMSG != NULL) /* Below, we trust logfileMSG not NULL. */ { if (consoleMSG != NULL) fprintf(consoleMSG, "Writing to logfile '%s'.\n", fname); fprintf(logfileMSG, "-------- LOG OUTPUT FROM '%s' --------\n", version); time(&t1); fprintf(logfileMSG, "Started on %s", ctime(&t1)); fprintf(logfileMSG, "Composition #%ld: %s (mm:ss)\n", opus->number, opus->title); /*------------------------------------ CONVERT DATABASE: ----------------------*/ if (opus->number >= 1300) /* Only if necessary. */ e = pack_data(consoleMSG); /* Argument may be NULL. */ else if (consoleMSG != NULL) fprintf(consoleMSG, "Expecting no need for spectroscopic data.\n"); if (e >= 0) /* 0 means OK, +1 means already packed. */ { /*------------------------------------ INIT DSP ENGINE: -----------------------*/ ENGINEp E = newENGINE(samplerate, dumpframes, consoleMSG); /* SR and blocksize.*/ if (E != NULL) { #if 0 /* You don't need to do this all the time (see copies in html subdir). */ visualiseTables(E, 250, 160, consoleMSG); /* Engine, width, height, FILE*. */ #endif /*------------------------------ COMPOSE: ---------------------------------*/ e = opus->fun(E, logfileMSG); /* e = composition2003_SuperColliderExport(E, logfileMSG); Not included yet. */ if (e) { if (consoleMSG != NULL) fprintf(consoleMSG, "Composing failure %d! See '%s'.\n", e, fname); } else { fflush(logfileMSG); /* Handy for the impatient. */ if (consoleMSG != NULL) fflush(consoleMSG); /*--------------------- GENERATE GRAPHIC SCORES: ----------------*/ sprintf(stamp, "%s #%ld", version, number); sprintf(fname, "%s-%ldlarge.jpg", version, number); /* engine: path: width: height: envTrack: title: msg: */ e = scoreWAVELETS(E, fname, 3200, 1600, 0, stamp, consoleMSG); if (e) fprintf(logfileMSG, "scoreWAVELETS(big)=%d!\n", e); sprintf(fname, "%s-%ldsmall.jpg", version, number); e = scoreWAVELETS(E, fname, 2000, 1000, 1, stamp, consoleMSG); if (e) fprintf(logfileMSG, "scoreWAVELETS(small)=%d!\n", e); /*--------------------- RENDER AUDIO TO AIFF: ---------------*/ sprintf(fname, "%s-%ld.aiff", version, number); /* engine: filename: bits: composeMSG: releaseMSG: */ e = playWAVELETS(E, fname, bits, logfileMSG, consoleMSG); } /* Inherit SR from ENGINE object. */ releaseENGINE(&E); /* Frees memory and wavelets if any remained. */ } } time(&t2); /* Report total working time. */ fprintf(logfileMSG, "Finished on %s", ctime(&t2)); print_duration((long)(t2-t1), logfileMSG); fprintf(logfileMSG, "-------- END OF LOG OUTPUT FROM '%s' --------\n", version); fclose(logfileMSG); } else { if (consoleMSG != NULL) fprintf(consoleMSG, "Unable to create/overwrite logfile '%s'!\n", fname); e = 10; } } else e = 123; /* Opus not found. */ return e; }