SoundUsage scanSoundUsage( char *inString ) { SimpleVector<int> idVector; SimpleVector<double> volVector; int numParts = 0; char **parts = split( inString, "#", &numParts ); for( int i=0; i<numParts; i++ ) { int id = -1; double vol = 1.0; sscanf( parts[i], "%d:%lf", &id, &vol ); if( id != -1 && vol >=0 && vol <= 1.0 ) { idVector.push_back( id ); volVector.push_back( vol ); } delete [] parts[i]; } delete [] parts; if( idVector.size() > 0 ) { if( idVector.size() == 1 && idVector.getElementDirect( 0 ) == -1 ) { return blankSoundUsage; } SoundUsage u = { idVector.size(), idVector.getElementArray(), volVector.getElementArray() }; return u; } else { return blankSoundUsage; } }
MusicNoteWaveTable::MusicNoteWaveTable( unsigned long inSamplesPerSecond ){ // read frequencies and lengths from files SimpleVector<double> *frequencyVector = new SimpleVector<double>(); SimpleVector<double> *lengthVector = new SimpleVector<double>(); FILE *musicNotePitchesFILE = NULL; FILE *musicNoteLengthsFILE = NULL; if( musicNotePitchesFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNotePitchesFILE, "%lf", &readValue ); if( numRead == 1 ) { frequencyVector->push_back( readValue ); } } fclose( musicNotePitchesFILE ); } else { // default pitches // Note N,, means N two octaves down // N'' means two octaves up /* // This one sounds pretty good // but not enough notes, too bland // A,, frequencyVector->push_back( 110 ); // D, frequencyVector->push_back( 146.832 ); // A, frequencyVector->push_back( 220 ); // D frequencyVector->push_back( 293.665 ); // G frequencyVector->push_back( 391.995 ); // C' frequencyVector->push_back( 523.251 ); // E' frequencyVector->push_back( 659.255 ); // G' frequencyVector->push_back( 783.991 ); */ // Instead, use entire two-octaves from c-major scale // Problem: there can be some discords. // However: much more interesting sounding than the two-chord version // above // base note: C, double baseNote = 130.8127827; int majorScaleSteps[7] = {2,2,1,2,2,2,1}; int scaleIndex = 0; int notePower = 0; // two octaves while( notePower < 25 ) { frequencyVector->push_back( baseNote * pow( 2, notePower / 12.0 ) ); notePower += majorScaleSteps[ scaleIndex ]; scaleIndex ++; if( scaleIndex >= 7 ) { // wrap around scaleIndex = 0; } } /* // These are the notes from Transcend level 001 frequencyVector->push_back( 220 ); frequencyVector->push_back( 277.183 ); frequencyVector->push_back( 329.628 ); frequencyVector->push_back( 440 ); frequencyVector->push_back( 554.365 ); frequencyVector->push_back( 659.255 ); */ } if( musicNoteLengthsFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNoteLengthsFILE, "%lf", &readValue ); if( numRead == 1 ) { lengthVector->push_back( readValue ); } } fclose( musicNoteLengthsFILE ); } else { // default lengths lengthVector->push_back( globalLongestNoteLength ); lengthVector->push_back( globalShortestNoteLength ); } mFrequencyCount = frequencyVector->size(); mLengthCount = lengthVector->size(); double *frequencies = frequencyVector->getElementArray(); mLengthsInSeconds = lengthVector->getElementArray(); delete frequencyVector; delete lengthVector; mSampleTable = new float**[ mFrequencyCount ]; mSampleCounts = new unsigned long[ mLengthCount ]; for( int F=0; F<mFrequencyCount; F++ ) { mSampleTable[F] = new float*[ mLengthCount ]; for( int L=0; L<mLengthCount; L++ ) { // construct a sample table for this freqency/length pair unsigned long lengthInSamples = (unsigned long)( mLengthsInSeconds[L] * inSamplesPerSecond ); mSampleTable[F][L] = new float[ lengthInSamples ]; // setting this inside a double-loop will set the same // value repeatedly with the same value, but this makes the code // cleaner (other options: a separate loop to set this value, or // an if statement to ensure that it is set only once) mSampleCounts[L] = lengthInSamples; // populate the sample table with a linearly decaying sine wave double frequencyInCyclesPerSecond = frequencies[F]; double frequencyInCyclesPerSample = frequencyInCyclesPerSecond / inSamplesPerSecond; // sine function cycles every 2*pi // adjust so that it cycles according to our desired frequency double adjustedFrequency = frequencyInCyclesPerSample * ( 2 * M_PI ); // try to fade in for 100 samples to avoid a click // at the start of the note unsigned long numFadeInSamples = 100; if( numFadeInSamples > lengthInSamples ) { numFadeInSamples = lengthInSamples / 2; } // optimizations (found with profiler) // pull these out of inner loop float lengthInSamplesMinusOne = (float)lengthInSamples - 1.0f; float inv_lengthInSamplesMinusOne = 1.0f / lengthInSamplesMinusOne; float *theseSamples = mSampleTable[F][L]; for( unsigned long i=0; i<lengthInSamples; i++ ) { // decay loudness linearly float loudness = ( lengthInSamplesMinusOne - i ) * inv_lengthInSamplesMinusOne; // fade in for the first 100 samples to avoid // a click if( i < numFadeInSamples ) { float fadeInFactor = (float)( i ) / (float)( numFadeInSamples - 1 ); // optimization: // only do this extra multiplication for notes that // are fading in loudness *= fadeInFactor; } theseSamples[i] = loudness * (float)sin( i * adjustedFrequency ); } } } delete [] frequencies; }
char pathFind( int inMapH, int inMapW, char *inBlockedMap, GridPos inStart, GridPos inGoal, int *outFullPathLength, GridPos **outFullPath ) { // watch for degen case where start and goal are equal if( equal( inStart, inGoal ) ) { if( outFullPathLength != NULL ) { *outFullPathLength = 0; } if( outFullPath != NULL ) { *outFullPath = NULL; } return true; } // insertion-sorted queue of records waiting to be searched pathSearchQueue recordsToSearch; // keep records here, even after we're done with them, // to ensure they get deleted SimpleVector<pathSearchRecord*> searchQueueRecords; SimpleVector<pathSearchRecord> doneQueue; int numFloorSquares = inMapH * inMapW; // quick lookup of touched but not done squares // indexed by floor square index number char *openMap = new char[ numFloorSquares ]; memset( openMap, false, numFloorSquares ); char *doneMap = new char[ numFloorSquares ]; memset( doneMap, false, numFloorSquares ); pathSearchRecord startRecord = { inStart, inStart.y * inMapW + inStart.x, 0, getGridDistance( inStart, inGoal ), getGridDistance( inStart, inGoal ), -1, NULL }; // can't keep pointers in a SimpleVector // (change as vector expands itself) // push heap pointers into vector instead pathSearchRecord *heapRecord = new pathSearchRecord( startRecord ); searchQueueRecords.push_back( heapRecord ); recordsToSearch.head = heapRecord; openMap[ startRecord.squareIndex ] = true; char done = false; //while( searchQueueRecords.size() > 0 && !done ) { while( recordsToSearch.head != NULL && !done ) { // head of queue is best pathSearchRecord bestRecord = *( recordsToSearch.head ); recordsToSearch.head = recordsToSearch.head->nextSearchRecord; if( false ) printf( "Best record found: " "(%d,%d), cost %d, total %f, " "pred %d, this index %d\n", bestRecord.pos.x, bestRecord.pos.y, bestRecord.cost, bestRecord.total, bestRecord.predIndex, doneQueue.size() ); doneMap[ bestRecord.squareIndex ] = true; openMap[ bestRecord.squareIndex ] = false; doneQueue.push_back( bestRecord ); int predIndex = doneQueue.size() - 1; if( equal( bestRecord.pos, inGoal ) ) { // goal record has lowest total score in queue done = true; } else { // add neighbors GridPos neighbors[4]; GridPos bestPos = bestRecord.pos; neighbors[0].x = bestPos.x; neighbors[0].y = bestPos.y - 1; neighbors[1].x = bestPos.x; neighbors[1].y = bestPos.y + 1; neighbors[2].x = bestPos.x - 1; neighbors[2].y = bestPos.y; neighbors[3].x = bestPos.x + 1; neighbors[3].y = bestPos.y; // one step to neighbors from best record int cost = bestRecord.cost + 1; for( int n=0; n<4; n++ ) { int neighborSquareIndex = neighbors[n].y * inMapW + neighbors[n].x; if( ! inBlockedMap[ neighborSquareIndex ] ) { // floor char alreadyOpen = openMap[ neighborSquareIndex ]; char alreadyDone = doneMap[ neighborSquareIndex ]; if( !alreadyOpen && !alreadyDone ) { // for testing, color touched nodes // mGridColors[ neighborSquareIndex ].r = 1; // add this neighbor double dist = getGridDistance( neighbors[n], inGoal ); // track how we got here (pred) pathSearchRecord nRecord = { neighbors[n], neighborSquareIndex, cost, dist, dist + cost, predIndex, NULL }; pathSearchRecord *heapRecord = new pathSearchRecord( nRecord ); searchQueueRecords.push_back( heapRecord ); insertSearchRecord( &recordsToSearch, heapRecord ); openMap[ neighborSquareIndex ] = true; } else if( alreadyOpen ) { pathSearchRecord *heapRecord = pullSearchRecord( &recordsToSearch, neighborSquareIndex ); // did we reach this node through a shorter path // than before? if( cost < heapRecord->cost ) { // update it! heapRecord->cost = cost; heapRecord->total = heapRecord->estimate + cost; // found a new predecessor for this node heapRecord->predIndex = predIndex; } // reinsert insertSearchRecord( &recordsToSearch, heapRecord ); } } } } } char failed = false; if( ! done ) { failed = true; } delete [] openMap; delete [] doneMap; for( int i=0; i<searchQueueRecords.size(); i++ ) { delete *( searchQueueRecords.getElement( i ) ); } if( failed ) { return false; } // follow index to reconstruct path // last in done queue is best-reached goal node int currentIndex = doneQueue.size() - 1; pathSearchRecord *currentRecord = doneQueue.getElement( currentIndex ); pathSearchRecord *predRecord = doneQueue.getElement( currentRecord->predIndex ); done = false; SimpleVector<GridPos> finalPath; finalPath.push_back( currentRecord->pos ); while( ! equal( predRecord->pos, inStart ) ) { currentRecord = predRecord; finalPath.push_back( currentRecord->pos ); predRecord = doneQueue.getElement( currentRecord->predIndex ); } // finally, add start finalPath.push_back( predRecord->pos ); SimpleVector<GridPos> finalPathReversed; int numSteps = finalPath.size(); for( int i=numSteps-1; i>=0; i-- ) { finalPathReversed.push_back( *( finalPath.getElement( i ) ) ); } if( outFullPathLength != NULL ) { *outFullPathLength = finalPath.size(); } if( outFullPath != NULL ) { *outFullPath = finalPathReversed.getElementArray(); } return true; }
ParameterizedStereoSound::ParameterizedStereoSound( FILE *inFILE, char *outError ) { char readError = false; // read the sound length int numRead = fscanf( inFILE, "%lf", &mSoundLengthInSeconds ); if( numRead != 1 ) { readError = true; printf( "Error: failed to read sound length from sound space.\n" ); } SimpleVector<ParameterSpaceControlPoint *> *controlPoints = new SimpleVector<ParameterSpaceControlPoint*>(); SimpleVector<double> *controlPointParameterAnchors = new SimpleVector<double>(); // keep reading parameter anchors and control points until we // can read no more while( !readError ) { // read the parameter space anchor double anchor = 0; numRead = fscanf( inFILE, "%lf", &anchor ); if( numRead != 1 ) { readError = true; } else { // read the control point StereoSoundParameterSpaceControlPoint *point = new StereoSoundParameterSpaceControlPoint( inFILE, &readError ); if( !readError ) { controlPointParameterAnchors->push_back( anchor ); controlPoints->push_back( point ); } else { delete point; } } } mNumControlPoints = controlPoints->size(); mControlPoints = controlPoints->getElementArray(); mControlPointParameterAnchors = controlPointParameterAnchors->getElementArray(); delete controlPoints; delete controlPointParameterAnchors; if( mNumControlPoints >= 2 ) { *outError = false; } else { // we didn't read enough control points *outError = true; } }
void saveCategoryToDisk( int inParentID ) { CategoryRecord *r = getCategory( inParentID ); if( r == NULL ) { return; } File categoriesDir( NULL, "categories" ); if( !categoriesDir.exists() ) { categoriesDir.makeDirectory(); } if( ! categoriesDir.exists() || ! categoriesDir.isDirectory() ) { printf( "Failed to make categories directory\n" ); return; } File *cacheFile = categoriesDir.getChildFile( "cache.fcz" ); cacheFile->remove(); delete cacheFile; char *fileName = autoSprintf( "%d.txt", inParentID ); File *categoryFile = categoriesDir.getChildFile( fileName ); if( r->objectIDSet.size() == 0 ) { // empty category, simply remove it categoryFile->remove(); } else { // resave SimpleVector<char*> lines; lines.push_back( autoSprintf( "parentID=%d", inParentID ) ); if( r->isPattern ) { lines.push_back( stringDuplicate( "pattern" ) ); } else if( r->isProbabilitySet ) { lines.push_back( stringDuplicate( "probSet" ) ); } // start with 0 objects in a new category lines.push_back( autoSprintf( "numObjects=%d", r->objectIDSet.size() ) ); for( int i=0; i<r->objectIDSet.size(); i++ ) { if( r->isProbabilitySet ) { lines.push_back( autoSprintf( "%d %f", r->objectIDSet.getElementDirect(i), r->objectWeights.getElementDirect(i) ) ); } else { lines.push_back( autoSprintf( "%d", r->objectIDSet.getElementDirect(i) ) ); } } char **linesArray = lines.getElementArray(); char *contents = join( linesArray, lines.size(), "\n" ); categoryFile->writeToFile( contents ); delete [] contents; delete [] linesArray; lines.deallocateStringElements(); } delete [] fileName; delete categoryFile; return; }