1 
2 #include "common.h"
3 #include "Timbre.h"
4 #include "Envelope.h"
5 
6 #include "minorGems/util/SimpleVector.h"
7 
8 
9 
10 #include <SDL/SDL.h>
11 #include <SDL/SDL_audio.h>
12 
13 #include <math.h>
14 #include <stdlib.h>
15 
16 int sampleRate = 22050;
17 //int sampleRate = 11025;
18 
19 
20 
21 
22 Image *musicImage = NULL;
23 int w, h;
24 
25 // total number of samples played so far
26 int streamSamples = 0;
27 
28 // offset into grid at start
29 // for testing
30 int gridStartOffset = 0;
31 
32 
33 
34 // overal loudness of music
35 double musicLoudness = 1.0;
36 
37 
38 
39 
40 
41 
42 // one grid step in seconds
43 double gridStepDuration = 0.25;
44 int gridStepDurationInSamples = (int)( gridStepDuration * sampleRate );
45 
46 double entireGridDuraton;
47 
48 
49 // c
50 double keyFrequency = 261.63;
51 
52 
53 int numTimbres = 4;
54 
55 Timbre *musicTimbres[ 4 ];
56 
57 int numEnvelopes = 4;
58 
59 Envelope *musicEnvelopes[ 4 ];
60 
61 
62 
63 class Note {
64     public:
65         // index into musicTimbres array
66         int mTimbreNumber;
67 
68         // index into musicEnvelopes array
69         int mEnvelopeNumber;
70 
71         int mScaleNoteNumber;
72 
73         // additional loudness adjustment
74         // places note in stereo space
75         double mLoudnessLeft;
76         double mLoudnessRight;
77 
78 
79         // start time, in seconds from start of note grid
80         double mStartTime;
81 
82         // duration in seconds
83         double mDuration;
84 
85         // used when note is currently playing to track progress in note
86         // negative if we should wait before starting to play the note
87         int mCurrentSampleNumber;
88 
89         // duration in samples
90         int mNumSamples;
91 
92 
93     };
94 
95 
96 // isomorphic to our music image, except only has an entry for each note
97 // start (all others, including grid spots that contain note continuations,
98 //  are NULL)
99 // indexed as noteGrid[y][x]
100 Note ***noteGrid;
101 
102 
103 SimpleVector<Note*> currentlyPlayingNotes;
104 
105 
106 
107 // need to synch these with audio thread
108 
setMusicLoudness(double inLoudness)109 void setMusicLoudness( double inLoudness ) {
110     SDL_LockAudio();
111 
112     musicLoudness = inLoudness;
113 
114     SDL_UnlockAudio();
115     }
116 
117 
118 
restartMusic()119 void restartMusic() {
120     SDL_LockAudio();
121 
122     // return to beginning (and forget samples we've played so far)
123     streamSamples = 0;
124 
125     // drop all currently-playing notes
126     currentlyPlayingNotes.deleteAll();
127 
128     SDL_UnlockAudio();
129     }
130 
131 
132 
133 
134 // called by SDL to get more samples
audioCallback(void * inUserData,Uint8 * inStream,int inLengthToFill)135 void audioCallback( void *inUserData, Uint8 *inStream, int inLengthToFill ) {
136 
137     // 2 bytes for each channel of stereo sample
138     int numSamples = inLengthToFill / 4;
139 
140 
141     Sint16 *samplesL = new Sint16[ numSamples ];
142     Sint16 *samplesR = new Sint16[ numSamples ];
143 
144     // first, zero-out the buffer to prepare it for our sum of note samples
145     // each sample is 2 bytes
146     memset( samplesL, 0, 2 * numSamples );
147     memset( samplesR, 0, 2 * numSamples );
148 
149 
150     int i;
151 
152 
153     // hop through all grid steps that *start* in this stream buffer
154     // add notes that start during this stream buffer
155 
156     // how far into stream buffer before we hit our first grid step?
157     int startOfFirstGridStep = streamSamples % gridStepDurationInSamples;
158 
159     if( startOfFirstGridStep != 0 ) {
160         startOfFirstGridStep =
161             gridStepDurationInSamples - startOfFirstGridStep;
162         }
163 
164 
165     // hop from start of grid step to start of next grid step
166     // ignore samples in between, since notes don't start there,
167     // and all we're doing right now is finding notes that start
168     for( i=startOfFirstGridStep;
169          i<numSamples;
170          i += gridStepDurationInSamples ) {
171 
172         // start of new grid position
173 
174         // check for new notes that are starting
175 
176         // map into our music image:
177         int x = ( streamSamples + i ) / gridStepDurationInSamples;
178 
179         // wrap in image
180         x = x % w;
181 
182         for( int y=0; y<h; y++ ) {
183 
184             Note *note = noteGrid[y][x];
185 
186             if( note != NULL ) {
187                 // new note
188                 currentlyPlayingNotes.push_back( note );
189                 // start it
190 
191                 // set a delay for its start based on our position
192                 // in this callback buffer
193                 note->mCurrentSampleNumber = -i;
194                 }
195             }
196         }
197 
198     streamSamples += numSamples;
199 
200 
201     // loop over all current notes and add their samples to buffer
202 
203     for( int n=0; n<currentlyPlayingNotes.size(); n++ ) {
204 
205         Note *note = *( currentlyPlayingNotes.getElement( n ) );
206 
207         int waveTableNumber = note->mScaleNoteNumber;
208         Timbre *timbre = musicTimbres[ note->mTimbreNumber ];
209         int tableLength = timbre->mWaveTableLengths[ waveTableNumber ];
210 
211         Sint16 *waveTable = timbre->mWaveTable[ waveTableNumber ];
212 
213         Envelope *env = musicEnvelopes[ note->mEnvelopeNumber ];
214         double *envLevels =
215             env->getEnvelope(
216                 // index envelope by number of grid steps in note
217                 note->mNumSamples / gridStepDurationInSamples );
218 
219 
220         double noteLoudnessL = note->mLoudnessLeft;
221         double noteLoudnessR = note->mLoudnessRight;
222 
223         // do this outside inner loop
224         noteLoudnessL *= musicLoudness;
225         noteLoudnessR *= musicLoudness;
226 
227 
228         int noteStartInBuffer = 0;
229         int noteEndInBuffer = numSamples;
230 
231         if( note->mCurrentSampleNumber < 0 ) {
232             // delay before note starts in this sample buffer
233             noteStartInBuffer = - note->mCurrentSampleNumber;
234 
235             // we've taken account of the delay
236             note->mCurrentSampleNumber = 0;
237             }
238 
239         char endNote = false;
240 
241         int numSamplesLeftInNote =
242             note->mNumSamples - note->mCurrentSampleNumber;
243 
244         if( noteStartInBuffer + numSamplesLeftInNote < noteEndInBuffer ) {
245             // note ends before end of buffer
246             noteEndInBuffer = noteStartInBuffer + numSamplesLeftInNote;
247             endNote = true;
248             }
249 
250 
251         int waveTablePos = note->mCurrentSampleNumber % tableLength;
252 
253         int currentSampleNumber = note->mCurrentSampleNumber;
254 
255         for( i=noteStartInBuffer; i != noteEndInBuffer; i++ ) {
256             double envelope = envLevels[ currentSampleNumber ];
257 
258             double monoSample = envelope *
259                 waveTable[ waveTablePos ];
260 
261 
262             samplesL[i] += (Sint16)( noteLoudnessL * monoSample );
263             samplesR[i] += (Sint16)( noteLoudnessR * monoSample );
264 
265             currentSampleNumber ++;
266 
267             waveTablePos ++;
268 
269             // avoid using mod operator (%) in inner loop
270             // found with profiler
271             if( waveTablePos == tableLength ) {
272                 // back to start of table
273                 waveTablePos = 0;
274                 }
275 
276             }
277 
278         note->mCurrentSampleNumber += ( noteEndInBuffer - noteStartInBuffer );
279 
280         if( endNote ) {
281             // note ended in this buffer
282             currentlyPlayingNotes.deleteElement( n );
283             n--;
284             }
285 
286         }
287 
288 
289     // now copy samples into Uint8 buffer
290     int streamPosition = 0;
291     for( i=0; i != numSamples; i++ ) {
292         Sint16 intSampleL = samplesL[i];
293         Sint16 intSampleR = samplesR[i];
294 
295         inStream[ streamPosition ] = (Uint8)( intSampleL & 0xFF );
296         inStream[ streamPosition + 1 ] = (Uint8)( ( intSampleL >> 8 ) & 0xFF );
297 
298         inStream[ streamPosition + 2 ] = (Uint8)( intSampleR & 0xFF );
299         inStream[ streamPosition + 3 ] = (Uint8)( ( intSampleR >> 8 ) & 0xFF );
300 
301         streamPosition += 4;
302         }
303 
304     delete [] samplesL;
305     delete [] samplesR;
306 
307     }
308 
309 
310 
311 // limit on n, based on Nyquist, when summing sine components
312 //int nLimit = (int)( sampleRate * M_PI );
313 // actually, this is way too many:  it takes forever to compute
314 // use a lower limit instead
315 // This produces fine results (almost perfect square wave)
316 int nLimit = 40;
317 
318 
319 
320 // square wave with period of 2pi
squareWave(double inT)321 double squareWave( double inT ) {
322     double sum = 0;
323 
324     for( int n=1; n<nLimit; n+=2 ) {
325         sum += 1.0/n * sin( n * inT );
326         }
327     return sum;
328     }
329 
330 
331 
332 // sawtoot wave with period of 2pi
sawWave(double inT)333 double sawWave( double inT ) {
334     double sum = 0;
335 
336     for( int n=1; n<nLimit; n++ ) {
337         sum += 1.0/n * sin( n * inT );
338         }
339     return sum;
340     }
341 
342 
343 // white noise, ignores inT
whiteNoise(double inT)344 double whiteNoise( double inT ) {
345     return 2.0 * ( rand() / (double)RAND_MAX ) - 1.0;
346     }
347 
348 
349 // white noise where each sample is averaged with last sample
350 // effectively a low-pass filter
351 double lastSample = 0;
352 
smoothedWhiteNoise(double inT)353 double smoothedWhiteNoise( double inT ) {
354     // give double-weight to last sample to make it even smoother
355     lastSample = ( 2 * lastSample + whiteNoise( inT ) ) / 3;
356 
357     return lastSample;
358     }
359 
360 
361 
362 
loadMusicImage(const char * inTGAFileName)363 void loadMusicImage( const char *inTGAFileName ) {
364 
365     musicImage = readTGA( "/usr/local/share/passage/music", inTGAFileName );
366 
367     w = musicImage->getWidth();
368     h = musicImage->getHeight();
369 
370     // notes are in red and green channel
371     double *redChannel = musicImage->getChannel( 0 );
372     double *greenChannel = musicImage->getChannel( 1 );
373 
374 
375     entireGridDuraton = gridStepDuration * w;
376 
377 
378     // jump ahead in stream, if needed
379     streamSamples += gridStartOffset * gridStepDurationInSamples;
380 
381 
382     // blank line of pixels between timbres
383     int heightPerTimbre = (h+1) / numTimbres - 1;
384 
385 
386     // find the maximum number of simultaneous notes in the song
387     // take loudness into account
388     double  maxNoteLoudnessInAColumn = 0;
389 
390     int x, y;
391     for( x=0; x<w; x++ ) {
392         double noteLoudnessInColumnL = 0;
393         double noteLoudnessInColumnR = 0;
394 
395         for( y=0; y<h; y++ ) {
396 
397             int imageIndex = y * w + x;
398 
399             // the note number in our scale
400             // scale starts over for each timbre, with blank line
401             // in between timbres
402             int noteNumber = (h - y - 1) % (heightPerTimbre + 1);
403 
404             if( // not blank line between timbres
405                 noteNumber < heightPerTimbre &&
406                 // tone present in image
407                 ( redChannel[ imageIndex ] > 0 ||
408                   greenChannel[ imageIndex ] > 0 ) ) {
409 
410                 noteLoudnessInColumnL += greenChannel[ imageIndex ];
411                 noteLoudnessInColumnR += redChannel[ imageIndex ];
412 
413                 }
414             }
415         // pick loudest channel for this column and compare it to
416         // loudest column/channel seen so far
417         if( maxNoteLoudnessInAColumn < noteLoudnessInColumnL ) {
418             maxNoteLoudnessInAColumn = noteLoudnessInColumnL;
419             }
420         if( maxNoteLoudnessInAColumn < noteLoudnessInColumnR ) {
421             maxNoteLoudnessInAColumn = noteLoudnessInColumnR;
422             }
423 
424         }
425 
426 
427     // divide loudness amoung timbres to avoid clipping
428     double loudnessPerTimbre = 1.0 / maxNoteLoudnessInAColumn;
429 
430     // further adjust loudness per channel here as we construct
431     // each timbre.
432     // This is easier than tweaking loundness of a given part by hand
433     // using a painting program
434 
435     musicTimbres[0] = new Timbre( sampleRate, 0.6 * loudnessPerTimbre,
436                                   keyFrequency,
437                                   heightPerTimbre, sawWave );
438     musicTimbres[1] = new Timbre( sampleRate, loudnessPerTimbre,
439                                   keyFrequency,
440                                   heightPerTimbre, sin );
441     musicTimbres[2] = new Timbre( sampleRate, 0.4 * loudnessPerTimbre,
442                                   keyFrequency / 4,
443                                   heightPerTimbre, squareWave );
444     musicTimbres[3] = new Timbre( sampleRate, 0.75 * loudnessPerTimbre,
445                                   keyFrequency / 4,
446                                   heightPerTimbre, smoothedWhiteNoise );
447 
448 
449     // next, compute the longest note in the song
450     int maxNoteLength = 0;
451 
452     for( y=0; y<h; y++ ) {
453         int currentNoteLength = 0;
454 
455         for( x=0; x<w; x++ ) {
456             int imageIndex = y * w + x;
457 
458             // the note number in our scale
459             // scale starts over for each timbre, with blank line
460             // in between timbres
461             int noteNumber = (h - y - 1) % (heightPerTimbre + 1);
462 
463             if( // not blank line between timbres
464                 noteNumber < heightPerTimbre &&
465                 // tone present in image
466                 ( redChannel[ imageIndex ] > 0 ||
467                   greenChannel[ imageIndex ] > 0 ) ) {
468 
469                 currentNoteLength ++;
470                 }
471             else {
472                 currentNoteLength = 0;
473                 }
474             if( currentNoteLength > maxNoteLength ) {
475                 maxNoteLength = currentNoteLength;
476                 }
477             }
478         }
479 
480     printf( "Max note length in song = %d\n", maxNoteLength );
481 
482 
483 
484     musicEnvelopes[0] = new Envelope( 0.05, 0.7, 0.25, 0.1,
485                                       maxNoteLength,
486                                       gridStepDurationInSamples );
487     musicEnvelopes[1] = new Envelope( 0.1, 0.9, 0.0, 0.0,
488                                       maxNoteLength,
489                                       gridStepDurationInSamples );
490     musicEnvelopes[2] = new Envelope( 0.25, 0.0, 1.0, 0.1,
491                                       maxNoteLength,
492                                       gridStepDurationInSamples );
493     musicEnvelopes[3] = new Envelope( 0.0, 0.2, 0.0, 0.0,
494                                       maxNoteLength,
495                                       gridStepDurationInSamples );
496 
497 
498 
499 
500     noteGrid = new Note**[ h ];
501 
502     for( int y=0; y<h; y++ ) {
503         noteGrid[y] = new Note*[ w ];
504 
505         // each row is one pitch for a given instrument
506         // thus, two consecutive pixels should be the same note
507         // handle this by tracking whether a note is playing or not
508         char notePlaying = false;
509         Note *noteStart = NULL;
510         for( int x=0; x<w; x++ ) {
511             int imageIndex = y * w + x;
512 
513             // default to NULL
514             noteGrid[y][x] = NULL;
515 
516             // the note number in our scale
517             // scale starts over for each timbre, with blank line
518             // in between timbres
519             int noteNumber = (h - y - 1) % (heightPerTimbre + 1);
520 
521 
522 
523             if( // not blank line between timbres
524                 noteNumber < heightPerTimbre &&
525                 // tone present in image
526                 ( redChannel[ imageIndex ] > 0 ||
527                   greenChannel[ imageIndex ] > 0 ) ) {
528 
529 
530                 if( notePlaying ) {
531                     // part of note that's already playing
532 
533                     // one more grid step
534                     noteStart->mDuration += gridStepDuration;
535                     noteStart->mNumSamples += gridStepDurationInSamples;
536 
537                     }
538                 else {
539                     // start a new note
540                     noteGrid[y][x] = new Note();
541 
542                     noteGrid[y][x]->mScaleNoteNumber = noteNumber;
543 
544                     noteGrid[y][x]->mTimbreNumber =
545                         y / ( heightPerTimbre + 1 );
546 
547                     // same as timbre number
548                     noteGrid[y][x]->mEnvelopeNumber =
549                         noteGrid[y][x]->mTimbreNumber;
550 
551                     // left loudness from green brightness
552                     noteGrid[y][x]->mLoudnessLeft = greenChannel[ imageIndex ];
553 
554                     // right loudness from red brightness
555                     noteGrid[y][x]->mLoudnessRight = redChannel[ imageIndex ];
556 
557                     noteGrid[y][x]->mStartTime = gridStepDuration * x;
558 
559                     // one grid step so far
560                     noteGrid[y][x]->mDuration = gridStepDuration;
561                     noteGrid[y][x]->mNumSamples = gridStepDurationInSamples;
562 
563                     // track if it needs to be continued
564                     notePlaying = true;
565                     noteStart = noteGrid[y][x];
566                     }
567                 }
568             else {
569                 // no tone
570 
571                 if( notePlaying ) {
572                     // stop it
573                     notePlaying = false;
574                     noteStart = NULL;
575                     }
576                 }
577             }
578         }
579 
580 
581     }
582 
583 
584 
startMusic(const char * inTGAFileName)585 void startMusic( const char *inTGAFileName ) {
586 
587     loadMusicImage( inTGAFileName );
588 
589     SDL_AudioSpec audioFormat;
590 
591     /* Set 16-bit stereo audio at 22Khz */
592     audioFormat.freq = sampleRate;
593     audioFormat.format = AUDIO_S16;
594     audioFormat.channels = 2;
595     audioFormat.samples = 512;        /* A good value for games */
596     audioFormat.callback = audioCallback;
597     audioFormat.userdata = NULL;
598 
599     /* Open the audio device and start playing sound! */
600     if( SDL_OpenAudio( &audioFormat, NULL ) < 0 ) {
601         printf( "Unable to open audio: %s\n", SDL_GetError() );
602         }
603 
604     // set pause to 0 to start audio
605     SDL_PauseAudio(0);
606 
607 
608     }
609 
610 
611 
stopMusic()612 void stopMusic() {
613     SDL_CloseAudio();
614 
615     if( musicImage != NULL ) {
616         delete musicImage;
617         musicImage = NULL;
618         }
619 
620     for( int y=0; y<h; y++ ) {
621 
622         for( int x=0; x<w; x++ ) {
623 
624             if( noteGrid[y][x] != NULL ) {
625                 delete noteGrid[y][x];
626                 }
627             }
628         delete [] noteGrid[y];
629         }
630 
631     delete [] noteGrid;
632 
633 
634     int i;
635 
636     for( i=0; i<numTimbres; i++ ) {
637         delete musicTimbres[i];
638         }
639     for( i=0; i<numEnvelopes; i++ ) {
640         delete musicEnvelopes[i];
641         }
642 
643     }
644 
645 
646