Goto: [ Program Documentation ]
// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Wed Jun 25 15:33:07 GMT-0800 1997 // Last Modified: Wed Jul 2 07:05:40 GMT-0800 1997 // Filename: ...sig/doc/examples/sigfile/line2sine/line2sine.cpp // Syntax: C++; sig // // Description: convert lines from a Diagram document into sinusoids. // #include "line2sine.h" #include "sig.h" #include <iostream.h> #include <fstream.h> #include <strstream.h> #include <string.h> #include <math.h> #include <ctype.h> #include <stdlib.h> ////////////////////////////// // // Global variables: // double durationSeconds; // time for the output sound in seconds double durationSamples; // time for the output sound in samples double minFreq; // value of the y coordinate at bottom of window double maxFreq; // value of the y coordinate at top of window double globalAmp; // global scaling amplitude double srate; // sampling rate double maxX = -1; // maximum X coordinate of Diagram document double maxY = -1; // maximum Y coordinate of Diagram document const double hardX = 756.0; // maximum X coordinate of Diagram page const double hardY = 576.501;// maximum Y coordinate of Diagram page const int bufferSize = 1000; // max size for temporary line buffers const char* blanks = " \t"; // blank characters for strtok function char yaxis = 'l'; // l=linear scale, g=geometric scale /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { if (argc != 3) exitHelp(argv); SoundHeader header; header.setHighMono(); srate = header.getSrate(); char buffer[bufferSize] = {0}; char* buff; cout << "Enter the duration of the Diagram window x-axis (seconds): "; cin >> durationSeconds; durationSamples = (int)(durationSeconds * header.getSrate() + 0.5); int parameter; cout << "Do you want the y-axis to represent [0=frequencies][1=intervals]: "; cin >> parameter; if (parameter == 1) { yaxis = 'g'; } cout << "Enter the minimum frequency of the Diagram window (Hertz): "; cin >> minFreq; cout << "Enter the maximum frequency of the Diagram window (Hertz): "; cin >> maxFreq; checkMinMax(minFreq, maxFreq); cout << "Enter a global scaling amplitude (1.0 for no scaling): "; cin >> globalAmp; cout << "The default amplitude envelope is " << endl << " " << OscParameter::defaultAmpEnv << endl; cout << "Do you want to change it? [y/n]: "; cin.getline(buffer, bufferSize); cin.getline(buffer, bufferSize); buff = strtok(buffer, blanks); if (buff != NULL && tolower(buff[0]) == 'y') { cout << "Enter a new default amplitude envelope: " << endl << " "; cin.getline(buffer, bufferSize); strncpy(OscParameter::defaultAmpEnv, buffer, 10000); cout << "New envelope is: " << OscParameter::defaultAmpEnv << endl; } cout << "Do you want a printout of the oscillator parameters? " << "[0=No, 1=Yes]: "; cin >> parameter; OscParameters sines; inputParameters(argv[1], sines); if (parameter) { sines.print(); } int numSines = sines.data.getSize(); cout << "Total of : " << numSines << " line segments." << endl; // Elements: SoundFileOut outsound(argv[2], header); Add add; Envelope freqEnvs[numSines]; Envelope ampEnvs[numSines]; Scale ampScales[numSines]; Osc *oscs; // Had to do this way: Segmentation oscs = new Osc[numSines]; // Fault otherwise for large numSines // Connections: outsound.connect(add); int i; for (i=0; i<numSines; i++) { ampScales[i].connect(ampEnvs[i]); oscs[i].connect(freqEnvs[i]); oscs[i].connect(ampScales[i]); } // Set paramters for sines: for (i=0; i<numSines; i++) { ampScales[i].setScale(sines.data[i].getAmp() * globalAmp); freqEnvs[i].setEnvelope(sines.data[i].getFreqEnv()); freqEnvs[i].setDurationSamples(sines.data[i].getDuration()); ampEnvs[i].setEnvelope(sines.data[i].getAmpEnv()); ampEnvs[i].setDurationSamples(sines.data[i].getDuration()); } int startIndex = 0; int endIndex = 0; cout << "\nSeconds of sound processed: "; for (i=0; i<durationSamples; i++) { while (startIndex < numSines && sines.start[startIndex].x <= i) { // cout << "Connecting oscillator " << sines.start[startIndex].y // << " at sample " << i << endl; add.connect(oscs[(int)(sines.start[startIndex].y)]); startIndex++; } while (endIndex < numSines && sines.end[endIndex].x <= i) { // cout << "Disconnecting oscillator " << sines.end[endIndex].y // << " at sample " << i << endl; add.disconnect(oscs[(int)(sines.end[endIndex].y)]); endIndex++; } if (i % 11024 == 0) { if (i % 44096 == 0) { cout << i/44096; } else { cout << "." << flush; } } Tick(outsound); } cout << endl; return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // checkMinMax -- check minfreq and maxfreq for correct input // void checkMinMax(double& min, double& max) { if (min < 0 || max < 0) { cerr << "Error: cannot have negative frequencies" << endl; exit(1); } if (min > max) { double temp; temp = min; min = max; max = temp; } if (max > srate/2.0) { cerr << "Warning: aliasing will occur with frequencies greater than " << srate/2.0 << " Hertz." << endl; } if (min < 20 && yaxis == 'g') { cerr << "Warning: minimum frequency on interval scale cannot be less" << " than 20 Hz." << endl << "Resetting the minimum to 20." << endl; min = 20; } if (max < 20 && yaxis == 'g') { cerr << "Warning: maximum frequency on interval scale cannot be less" << "than 20 Hz." << endl << "Resetting the maximum to 20." << endl; max = 20; } } ////////////////////////////// // // exitHelp -- prints help for program, then exits the program // void exitHelp(char* args[]) { cout << "Usage: " << args[0] << " input.diagram2 output.snd" << endl; cout << endl; // more help to go here. exit(1); } ////////////////////////////// // // extractAmpEnvelope -- creates the amplitude envelope for // the current line in the input file. // void extractAmpEnvelope(fstream& infile, OscParameter& output) { static char buffer[1000] = {0}; int index = 0; char ch = 'a'; char och = 'a'; char ooch = 'a'; do { ooch = och; och = ch; if (infile.eof()) { cerr << "Error: unexpected end of file when searching for" << " amplitude envelope!" << endl; exit(1); } infile.get(ch); if (ch == 'd' && och == 'n' && ooch == 'e') return; } while (ch != '('); buffer[index++] = ch; do { infile.get(ch); buffer[index++] = ch; } while (!infile.eof() && ch != ')'); if (infile.eof()) { cerr << "Error: unexpected end of file when searching for" << " amplitude envelope!" << endl; exit(1); } buffer[index] = '\0'; output.setAmpEnv(buffer); } ////////////////////////////// // // findLine -- returns the number tag for the current line being // processed in the file stream. Returns -1 if no more lines. // int findLine(fstream& infile) { int output = 0; static char buffer[bufferSize]; while(infile.getline(buffer, bufferSize)) { if (strncmp(buffer, "line ", 5) == 0) { strtok(buffer, blanks); output = string2int(strtok(NULL, blanks)); if (output > 2000000000) { continue; } else { return output; } } } return -1; // end of file } ////////////////////////////// // // findVertex -- returns the number tag for the current vertex being // processed in the file stream. Returns -1 if no more vertices. // int findVertex(fstream& infile) { int output = 0; static char buffer[bufferSize]; while(infile.getline(buffer, bufferSize)) { if (strncmp(buffer, "line ", 5) == 0) { return -1; // end of vertices list } if (strncmp(buffer, "vertex ", 7) == 0) { strtok(buffer, blanks); output = string2int(strtok(NULL, blanks)); if (output > 2000000000) { continue; } else { return output; } } } return -1; // end of file } ////////////////////////////// // // getDimensions -- gets the window dimensions of the Diagram // document, and also gets the number of lines. Origin // is in the upper left corner. // void getDimensions(const char* filename, double& x, double& y) { char realFilename[1000] = {0}; strcat(realFilename, filename); strcat(realFilename, "/PrintInfo"); ifstream infile(realFilename); if (!infile.is_open()) { cerr << "Error: cannot open file: " << realFilename << endl; exit(1); } unsigned char ch, cho, choo, chooo; ch = cho = choo = chooo = 0; while (!infile.eof()) { chooo = choo; choo = cho; cho = ch; infile.get(ch); if (ch == 'c' && cho == 'i' && choo == 'i' && chooo == 'i') { x = extractNumber(infile) * hardX; y = extractNumber(infile); // this value is not being used if (infile.eof()) { cerr << "Error: unexpected end of file while reading " << "Diagram dimensions" << endl; exit(1); } return; } } } ////////////////////////////// // // extractNumber -- for use only with getDimensions. // int extractNumber(ifstream& infile) { static int status = -1; int numDigits; unsigned char ch; int i; int output = 0; if (status == -1) { infile.get(ch); if (ch < 0x80) { status = 0; return (int)ch; } else { status = 1; numDigits = (ch & 0x0f) + 1; for (i=0; i<numDigits; i++) { infile.get(ch); output += (int)ch * (int)pow(16, i * 2); } return output; } } if (status == 0) { infile.get(ch); return (int)ch; } else if (status == 1) { infile.get(ch); while (ch < 0x80) infile.get(ch); numDigits = (ch & 0x0f) + 1; for (i=0; i<numDigits; i++) { infile.get(ch); output += (int)ch * (int)pow(16, i * 2); } return output; } cerr << "Unexpected error reading Diagram dimensions" << endl; exit(1); } ////////////////////////////// // // getLineInfo -- return parameter information for one sinusoid. // you give it the file stream and it will extract the next // line from it. It is an error to look for a line when // there are no more lines in Diagram file. // OscParameter getLineInfo(fstream& infile, Vertices& vertices) { static char buffer[bufferSize] = {0}; // temporary storage for input file OscParameter output; // output of function char *buff; // temp pointer for words in string while(infile.getline(buffer, bufferSize)) { // skip to the line after "to" command" buff = strtok(buffer, blanks); if (strcmp(buff, "to") == 0) { // process the frequency envelope Collection<doubleXY> freqCoords; doubleXY temp; freqCoords.setSize(0); freqCoords.allowGrowth(); infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); while (isdigit(buff[0])) { temp = vertices.get(string2int(buff)); temp.x *= durationSamples; freqCoords.append(temp); infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); } qsort(freqCoords.getBase(), freqCoords.getSize(), sizeof(doubleXY), xyComp); output.setStart((long)freqCoords[0].x); output.setEnd((long)freqCoords[freqCoords.getSize()-1].x); { strstream freqStream; freqStream << yaxis << "("; for (int i=0; i<freqCoords.getSize(); i++) { freqStream << (long)(freqCoords[i].x - output.getStart()); freqStream << " "; freqStream << freqCoords[i].y; if (i != freqCoords.getSize() - 1) { freqStream << "; "; } } freqStream << ")" << ends; output.setFreqEnv(freqStream.str()); } // now check for amp envelope, then for local amplitude: if (strcmp(buff, "layer") != 0) { cerr << "Error: expected layer command but got \"" << buff << "\" instead." << endl; exit(1); } infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); // ignore the curved command if (strcmp(buff, "curved") == 0) { infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); } if (strcmp(buff, "rtfText") == 0) { extractAmpEnvelope(infile, output); do { infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); if (buff == NULL) { infile.getline(buffer, bufferSize); buff = strtok(buffer, blanks); } } while (!infile.eof() && strncmp(buff, "lineWidth", 9) != 0); } if (infile.eof()) { cerr << "Error: unexpected end of file." << endl; exit(1); } if (strcmp(buff, "lineWidth") != 0) { cerr << "Error: expected the lineWidth command but got \"" << buff << "\" instead." << endl; exit(1); } buff = strtok(NULL, blanks); if (!isdigit(buff[0])) { cerr << "Error: expecting a number, but got \"" << buff << "\" instead." << endl; exit(1); } output.setAmp(string2double(buff)*0.035); // .035 for cm scaling return output; } } cerr << "Error processing line info" << endl; exit(1); } ////////////////////////////// // // getVertexCoordinates -- returns the vertex coordinates // You must guarentee that valid data can be extracted // X value is in fractions of the total Duration. // Y value is in hertz. // doubleXY getVertexCoordinates(fstream& infile) { doubleXY output; static char buffer[bufferSize]; char *buff; while(infile.getline(buffer, bufferSize)) { buff = strtok(buffer, blanks); if (strcmp(buff, "location") == 0) { output.x = string2double(strtok(NULL, blanks)) / maxX; output.y = string2double(strtok(NULL, blanks)); // mod the y-axis to the primary page row: output.y = ((double)output.y/hardY - (int)(output.y/hardY)) * hardY; if (yaxis == 'l') { output.y = (maxFreq - minFreq) * (hardY - output.y) / hardY + minFreq; } else if (yaxis == 'g') { output.y = minFreq * pow(maxFreq/minFreq, (hardY - output.y)/hardY); } else { cerr << "Unknown interpolation type: " << yaxis << endl; exit(1); } return output; } } cerr << "Error: end of file without vertex coordinates" << endl; exit(1); } ////////////////////////////// // // inputParameters // void inputParameters(const char* tempFilename, OscParameters& p) { char filename[1000] = {0}; strcat(filename, tempFilename); strcat(filename, "/DiagramText"); fstream infile; infile.open(filename, ios::in); if (!infile.is_open()) { cerr << "Error: Cannot open file: " << filename << endl; exit(1); } getDimensions(tempFilename, maxX, maxY); Vertices vertices; readVertices(filename, vertices); int lineNum = 0; int index = 0; while (lineNum >= 0) { lineNum = findLine(infile); if (lineNum < 0) break; p.data[index] = getLineInfo(infile, vertices); p.start[index].x = p.data[index].getStart(); p.start[index].y = index; p.end[index].x = p.data[index].getEnd(); p.end[index].y = index; index++; } p.sort(); } ////////////////////////////// // // readVertices -- reads in an array of the vertices for // processing with lines. // void readVertices(char* filename, Vertices& vertices) { fstream infile; infile.open(filename, ios::in); if (!infile.is_open()) { cerr << "Error: Cannot open file: " << filename << endl; exit(1); } int vertexTag = 0; while (vertexTag >= 0) { vertexTag = findVertex(infile); if (vertexTag < 0) break; vertices.add(vertexTag, getVertexCoordinates(infile)); } } ////////////////////////////// // // string2double -- converts a string into a float // double string2double(const char* aString) { strstream tempStream; tempStream << aString; double output; tempStream >> output; return output; } ////////////////////////////// // // string2int -- converts a string into an int // int string2int(const char* aString) { strstream tempStream; tempStream << aString; int output; tempStream >> output; return output; }