/* Spectroscopic Toolkit version 1.93, july 12, 2008. Copyright (c) 2000-2008 - 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. ST translates spectroscopic (i.e. quantumphysical) data to AIFF-file. Uses the binary database-file gfall.pbin, produced by the pconv-program that reads Robert Kurucz's latest data file (see subdirectory pconv). This file never directly printfs to stdout. */ #include /* Standard headers. */ #include #include #include "ST_wavelets.h" /* Prototype addWAVELET(). */ #include "ST_pconv.h" #include "c00.h" /* All the composition-prototypes (just checking). */ /* Both duration and time of the first inflection of the individual wavelet depends on its' audio-frequency. First argument 'selected' in spectroscopic notation: 100 * Atomic number + ionisation level. For example: 100=H, 7900=Au, 4700=Ag. First wavelet's first inflection (up) exactly at argument start. Last wavelet's last inflection (down) exactly at start + duration (except when jj is nonzero, then it may take a little longer (or shorter?)). Argument duration acts as a linear time-stretch/compression parameter, it affects individual wavelet-durations as well as their mutual distance in time (to the same degree). */ /*---------------------------------------------------------------------------------------------*/ short c02_smear(unsigned short selected, /* 100 * Atomic number + ionisation level. */ double strength, /* Linear overall amplitude 3.20 is ok for Au. */ long double start, /* Starttime in seconds. */ long double duration, /* 'effective duration' in seconds. */ short up, /* 0 for down, 1 for up. */ short jj, /* Scrambles TOP times if nonzero (with J levels). */ ENGINEp E, FILE* msg) /* NULL, stdout or some logfile. */ { /* Just 1 element a time / time-smear. */ FILE *pbinDatabaseFP; pieterPacked pbinBuff; derivedData more; /* See ST.gfall.pbin.c. */ unsigned long linesRead = 0, linesUsed = 0, linesSkipped = 0; short e = 0; double a, p, d; long double audioHertz, dur, inflectStart, top; /*------------------------------------------------------------ Frequency transposition: */ long double transpositionRatio = pow(2.0, -40.0); /* 40 octaves down. */ /*------------------------------------------------------------ Smoother frequency windowing: */ double lowestFreq = 20.0; /* (trapeziod instead of brickwall) */ double aboveLowestFreq = 1.25 * lowestFreq; /* Rise to 1 within 1 major third. */ double aboveHertz = aboveLowestFreq - lowestFreq; double nyquist = 0.5*(double)getSamplerateENGINE(E); /* Private data. */ double belowNyquist = nyquist / 1.2; /* Fall to 0 within 1 minor third. */ double belowHertz = nyquist - belowNyquist; /*------------------------------------------------------------ Open binary version of Kurucz's file. */ double loFreq = 9.0e99, /* AUDIO-freq. */ hiFreq = -9.0e99, loAmp = 9.0e99, /* Avalue, not audio-amplitude yet. */ hiAmp = -9.0e99, minJ = 9.0e99, maxJ = -9.0e99; double durA, durB; /* Slope & offset. Lowest freq->1; highest freq->0.5. */ pbinDatabaseFP = fopen(kST_bin_base, "rb"); if (pbinDatabaseFP != NULL) { /*------------------------------ CYCLE #1 (analysis) ------------------*/ while (fread(&pbinBuff, sizeof(pbinBuff), 1, pbinDatabaseFP)) { if (selected == pbinBuff.ELEM) { moreLineData(&pbinBuff, &more); /* Calc some more data. */ audioHertz = more.frequency * transpositionRatio; /* Exclude out-of-range-freqs from analysis. */ /* There could slip through some freqs that are too weak! */ if ((audioHertz > lowestFreq) && (audioHertz < nyquist)) { if (audioHertz < loFreq) loFreq = audioHertz; if (audioHertz > hiFreq) hiFreq = audioHertz; if (more.Avalue < loAmp) loAmp = more.Avalue; if (more.Avalue > hiAmp) hiAmp = more.Avalue; if (pbinBuff.LOW.J < minJ) minJ = pbinBuff.LOW.J; if (pbinBuff.UPP.J < minJ) minJ = pbinBuff.UPP.J; if (pbinBuff.LOW.J > maxJ) maxJ = pbinBuff.LOW.J; if (pbinBuff.UPP.J > maxJ) maxJ = pbinBuff.UPP.J; } } } /*--------------------- Print statistics for the selected element/ion: -------*/ if (msg != NULL) fprintf(msg, "-------- c02_smear() ELEM %d: --------\n\ loAmp = %.8f; hiAmp = %.8f; RATIO=%.2f.\n\ loFreq = %.6f; hiFreq = %.6f; RATIO=%.2f.\n\ minJ = %.2f; maxJ = %.2f.\n", selected, loAmp, hiAmp, hiAmp / loAmp, loFreq, hiFreq, hiFreq / loFreq, minJ, maxJ); /*---------------- Linear duration compensation function: ------------------------------*/ /* d(loF) = 1 1 = a loF + b a = 0.5 / (loF - hiF) */ /* d(hiF) = 0.5 0.5 = a hiF + b - 0.5 = (0.5 / (loF - hiF)) hiF + b */ /* d(F) = a F + b ------------------- b = 0.5 - (0.5 hiF / (loF - hiF)) */ /* 0.5 = a (loF - hiF) */ d = loFreq - hiFreq; durA = 0.5 / d; /* A=slope and B=offset. */ durB = 0.5 - (0.5 * hiFreq / d); /*---------------- Linear bass-boost-filter-function (per element): --------------------*/ /* a(loF) = 1 SAME d as with duration may be used! */ /* d(hiF) = 0.5 (-6dB) 0.5 = a hiF + b - */ /*------------------------------ CYCLE #2 ------------------*/ rewind(pbinDatabaseFP); if (msg != NULL) { fprintf(msg, "1/transpositionRatio = %.1Lf.\n", 1.0 / transpositionRatio); fprintf(msg, "Effective duration = %.6Lf s.\n", duration); fprintf(msg, "Using oscillator-sync. (phase = -1).\n"); } while (fread(&pbinBuff, sizeof(pbinBuff), 1, pbinDatabaseFP)) { if (selected == pbinBuff.ELEM) { moreLineData(&pbinBuff, &more); /* Calc some more data. */ /*----------------------------------------------------------------------*/ audioHertz = more.frequency * transpositionRatio; if ((audioHertz > lowestFreq) && (audioHertz < nyquist)) { /*------------------ Less brickwall-shaped filtering: --------------*/ if (audioHertz < aboveLowestFreq) a = (audioHertz - lowestFreq) / aboveHertz; /* Rise to 1 within 1 minor third. */ else if (audioHertz > belowNyquist) a = (nyquist - audioHertz) / belowHertz; /* Fall to 0 within 1 major third. */ else a = 1.0; /*---------------- Frequency-dependant Compensation per element: ---------------*/ /* Only lowest freq of one metal (section) gets unit-duration and unit-amplit., */ /* all higher frequencies sound shorter. loFreq -> 1, hiFreq -> 0.5. */ d = (durA * audioHertz) + durB; /*------------------ Bass boost-filter (per element): --------------*/ a *= d; /* Highest frequency -6.021 dB. */ /* ----------------- Amplitude: ------------------------------------*/ /* Compress amplitude-range (per section) by approx 4th-power-root. */ a *= strength * pow(1.90e-13 * more.Avalue, 0.26); if (a >= 1.192093E-7) /* Amplitude treshold (23 bit). */ { /*------------------------ Panning: ---------------------------------------*/ if (pbinBuff.LOW.J == pbinBuff.UPP.J) p = 0.0; else if (pbinBuff.LOW.J > pbinBuff.UPP.J) /* Depend upon quantum-states. */ p = +0.5; else p = -0.5; if (linesUsed & 1) /* Alternate between left and right channel. */ p += 0.118; /* Odd ones slighty to the right. */ else p -= 0.118; /* Even ones slighty to the left. */ /*---------------- Duration & starttime: ---------------------------*/ /* Only lowest frequency gets unit-duration. */ dur = 0.062 * d * duration; /* 0.062 defines ratio between duration and spacing of wavelets. */ if (up) inflectStart = start + ((duration - dur) * 2.0 * (1.0 - d)); else /* (down) */ inflectStart = start + ((duration - dur) * 2.0 * (d - 0.5)); if (jj) /* Scramble TOP times (start and end times not exact any longer!). */ top = inflectStart + (pbinBuff.LOW.J * pbinBuff.UPP.J * dur); else top = inflectStart + (0.5 * dur); /* From low to high: */ if (addWAVELET(E, msg, 0, /* Hz */ audioHertz, /* amp */ a, /* pan */ p, /* top */ top, /* Ascending inflection. */ /* dur */ dur, /* env */ kENV_Gaussian, /* (also possible: kENV_ExpDecay with pha=0) */ /* pha */ -1)) /* -1 = sync with all other oscillators. */ { e = 3; goto clFile; } linesUsed++; } else { linesSkipped++; if (msg != NULL) fprintf(msg, "SKIPPED-AMP: Hz=%12.6Lf lggf=%5.2f.\n", audioHertz, pbinBuff.LOGGF); } } else { linesSkipped++; if (msg != NULL) fprintf(msg, "SKIPPED-FRQ: Hz=%12.6Lf lggf=%5.2f.\n", audioHertz, pbinBuff.LOGGF); } printLineEssential(&pbinBuff, msg); /* stdout or logFP. */ printLineDerived(&more, msg); } linesRead++; } if (msg) fprintf(msg, " linesUsed = %ld.\nlinesSkipped = %ld.\n\n", linesUsed, linesSkipped); if (linesRead != kKuruczLinesTotal) { if (msg) fprintf(msg, "Error: expected %ld lines in database!\n", kKuruczLinesTotal); e = 2; } clFile: fclose(pbinDatabaseFP); } else { if (msg) fprintf(msg, "Cannot open binary database-file '%s'!\n", kST_bin_base); e = 1; } return e; }