-
Notifications
You must be signed in to change notification settings - Fork 0
/
Playtune_samp.ino
858 lines (726 loc) · 40.1 KB
/
Playtune_samp.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
/************************************************************************************
Playtune_samp: A tune generator that uses sample-based
synthesis to play a polyphonic musical score.
About Playtune, generally
Playtune is a family of music players for Arduino-like microcontrollers. They
each intepret a bytestream of commands that represent a polyphonic musical
score, and play it using different techniques.
(1) The original Playtune that was first released in 2011 uses a separate hardware timer
to generate a square wave for each note played simultaneously. The timers run at twice
the frequency of the note being played, and the interrupt routine flips the output bit.
It can play only as many simultaneous notes as there are timers available. The sound
quality? Buzzy square waves.
https://github.com/LenShustek/arduino-playtune
(2) The second ("polling") version uses only one hardware timer that interrupts often,
by default at 20 Khz, or once every 50 microseconds. The interrupt routine determines
which, if any, of the currently playing notes need to be toggled. It also implements
primitive volume modulation by changing the duty cycle of the square wave.
The advantage over the first version is that the number of simultaneous notes is not
limited by the number of timers, only by the number of output pins. The sound quality
is still "buzzy square waves".
https://github.com/LenShustek/playtune_poll
(3) This third version also uses only one hardware timer interrupting frequently, but
uses the hardware digital-to-analog converter on high-performance microntrollers like
the Teensy to generate an analog wave that is the sum of stored samples of sounds. The
samples are scaled to the right frequency and volume, and any number of instrument
samples can be used and mapped to MIDI patches. The sound quality is much better,
although not in league with real synthesizers. This currently only support Teensy.
https://github.com/LenShustek/playtune_samp
For all these versions, once a score starts playing, the processing happens in
the interrupt routine. Any other "real" program can be running at the same time
as long as it doesn't use the timer or the output pins that Playtune is using.
**** Details about this version: Playtune_samp
This is currently implemented only for the PJRC Teensy LC and Teensy 3.x
microcontrollers, because they have an D-to-A converter and are fast enough.
You'll see some experimental code for Arudinos using PCM D-to-A, but it's not fast
enough yet and needs more work. A demotivating factor is that, even if it works,
it's hard to get enough wave tables and scores to fit in 32K, vs the 128K or 256K
in a Teensy. (Can you tell I'm a Teensy fan?)
(For an example of some great "extreme programming" for efficiency in music
generation on small 8-bit processors, see Erico Colombini's "play-v6" code, and his
nice blog tutorial articles: http://www.erix.it/play-v6/,
http://www.quintadicopertina.com/enricocolombini/language/en/.)
There is support in this version for volume modulation, instrument choice, and percussion.
(1) If the ASSUME_VOLUME compile-time switch is set to 1, or if the optional file
header file indicates that volume information is present, then we interpret MIDI
"velocity" information to scale the analog output of each tone generator separately.
The bytestream volume information can be generated by Miditones with the -v option.
(2) As we find instrument change instructions (Ct ii) in the bytestream, we map MIDI
patches to a variety of prerecorded instrument samples. The bytestream instrument
information can be generated by Miditones with the -i option.
(2) If we find note numbers greater than 127, we interpret them as having come from
MIDI channel 9 (10 if you start counting with 1) as percussion sounds, which we play
as single non-repeated samples. Those notes are relocated from 0..127 to 128.255
by Miditones with the -pt option.
It's possible to (barely) hear the music just by connecting the analog output pin
to a speaker through a 50-ohm resistor. But you'll really want to use an amplified
speaker. I use this one: https://www.amazon.com/gp/product/B00CWBABP4.
A little low-pass filtering with a resistor and capacitor helps too.
**** Programming with Playtune_poll
Unlike the original Playtune, this is not configured as a library because we make
compile-time changes for pin assignments. You should create a sketch directory
with the following files in it:
Playtune_samp.ino This file, which has most of the code
Playtune_samp.h The header file, which defines the output pin configuration
Playtune_samp_waves.ino The file that contains the stored sound samples
Playtune_samp_test.ino The main program, which contains the score(s) you wish to
play, and any other code you want to run.
You must change the #define at the begininng of Playtune_samp.h to indicate which
board you are compiling for, in addition to setting that in the Arduino/Teenyduino
IDE Tools/Board menu.
You can use up to MAX_CHANS tone generators, as defined in Playtune_samp.h.
There is some inefficiency if MAX_CHANS is much larger than the number of tone
generators actually being used if the file doesn't have a -d header to tell it.
We also use the TimerOne library files, which you can get at
http://playground.arduino.cc/Code/Timer1 and put into your Arduino library
directory, or just put in the directory with the other files.
There are four public functions and one public variable that you can use
in your runtime code in Playtune_samp_test.ino.
void tune_start_timer(int microseconds)
This is optional. Call it to set how often notes should be checked for transitions,
from 5 to 100 microseconds. If you don't call it, we'll pick something that seems
appropriate from the type of microcontroller and the frequency it's running at.
void tune_playscore(byte *score)
Call this pointing to a "score bytestream" to start playing a tune. It will
only play as many simultaneous notes as you have defined tone generators;
any more will be ignored. See below for the format of the score bytestream.
boolean tune_playing
This global variable will be "true" if a score is playing, and "false" if not.
You can use this to see when a score has finished.
void tune_stopscore()
This will stop a currently playing score without waiting for it to end by itself.
void tune_stop_timer()
This stops playing and also stops the timer interrupt.
Do this when you don't want to play any more tunes.
***** The score bytestream *****
The bytestream is a series of commands that can turn notes on and off, or
start a waiting period until the next note change. Here are the details, with
numbers shown in hexadecimal.
If the high-order bit of the byte is 1, then it is one of the following commands:
9t nn Start playing note nn on tone generator t. Generators are numbered
starting with 0. The notes numbers are the MIDI numbers for the chromatic
scale, with decimal 60 being Middle C, and decimal 69 being Middle A
at 440 Hz. The highest note is decimal 127 at about 12,544 Hz. except
that percussion notes (instruments, really) range from 128 to 255.
[vv] If ASSUME_VOLUME is set to 1, or the file header tells us to,
then we expect a third byte with the volume ("velocity") value from 1 to
127. You can generate this from Miditones with the -v option.
(Everything breaks for headerless files if the assumption is wrong!)
8t Stop playing the note on tone generator t.
Ct ii Change tone generator t to play instrument ii from now on. Miditones will
generate this with the -i option.
F0 End of score: stop playing.
E0 End of score: start playing again from the beginning.
If the high-order bit of the byte is 0, it is a command to wait. The other 7 bits
and the 8 bits of the following byte are interpreted as a 15-bit big-endian integer
that is the number of milliseconds to wait before processing the next command.
For example,
07 D0
would cause a wait of 0x07d0 = 2000 decimal millisconds or 2 seconds. Any tones
that were playing before the wait command will continue to play.
Playtune bytestream files generated by later version of the Miditones progam using
the -d option begin with a small header that describe what optional data is present
in the file. This makes the file more self-describing, and this version of Playtune
uses that if it is present.
'Pt' 2 ascii characters that signal the presence of the header
nn The length (in one byte) of the entire header, 6..255
ff1 A byte of flag bits, three of which are currently defined:
80 velocity information is present
40 instrument change information is present
20 translated percussion notes are present
ff2 Another byte of flags, currently undefined
tt The number (in one byte) of tone generators actually used in this music.
We use that the scale the volume when combining simulatneous notes.
Any subsequent header bytes covered by the count, if present, are currently undefined
and are ignored.
The score is stored in Flash memory ("PROGMEM") along with the program, because
there's a lot more of that than data memory.
***** Where does the score data come from? *****
Well, you can write the score by hand from the instructions above, but that's
pretty hard. An easier way is to translate MIDI files into these score commands,
and I've written a program called "Miditones" to do that. See the separate
documentation for that program, which is also open source at
https://github.com/lenshustek/miditones
The best Miditones options to use for this version of Playtune are: -v -i -pt -d
And, of course, if you want more than 6 tone generators, -tn
***** Nostalgia from me *****
Writing Playtune was a lot of fun, because it essentially duplicates what I did
as a graduate student at Stanford University in about 1973. That project used the
then-new Intel 8008 microprocessor, plus three hardware square-wave generators that
I built out of 7400-series TTL. The music compiler was written in Pascal and read
scores that were hand-written in a notation I made up, which looked something like
this: C Eb 4G 8G+ 2R + F D#
This was before MIDI had been invented, and anyway I wasn't a pianist so I would
not have been able to record my own playing. I could barely read music well enough
to transcribe scores, but I slowly did quite a few of them. MIDI is better!
Len Shustek, originally 4 Feb 2011,
...updated for the sampling version in August 2016.
------------------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2011, 2016, Len Shustek
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.
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.
**********************************************************************************/
/*
Change log
19 January 2011, L.Shustek, V1.0
- Initial release.
23 February 2011, L. Shustek, V1.1
- prevent hang if delay rounds to count of 0
4 December 2011, L. Shustek, V1.2
- add special TESLA_COIL mods
10 June 2013, L. Shustek, V1.3
- change for compatibility with Arduino IDE version 1.0.5
6 April 2015, L. Shustek, V1.4
- change for compatibility with Arduino IDE version 1.6.x
28 May 2016, T. Wasiluk
- added support for ATmega32U4
10 July 2016, Nick Shvelidze
- Fixed include file names for Arduino 1.6 on Linux.
11 July 2017, Len Shustek
- Start this derivative version that uses polling instead of a timer
for each tone generator. It needs a fast processor, but can play an arbitrary
number of notes simultaneously using only one timer.
19 July 2016, Len Shustek
- Add crude volume modulation and percussion sounds. Thanks go to
Connor Nishijima for providing his code and goading us into doing this.
12 August 2016, Len Shustek
- Make this derivative version that uses sample-based synthesis driving
a D-to-A converter. Thanks go again to Connor Nishijima for provoking me
into trying this technique.
- Process the optional file header
- Add instruments and percussion
*/
#include <Arduino.h>
#include <TimerOne.h>
#include "Playtune_samp.h"
#define BOOST_PERCUSSION 1 // amplify percussion instruments?
#define ASSUME_VOLUME 0 // assume volume information is present in bytestream files without headers?
// A general-purpose macro joiner that allow rescanning of the result
// (Yes, we need both levels! See http://stackoverflow.com/questions/1489932)
#define PASTER(x,y) x ## y
#define JOIN(x,y) PASTER(x,y)
static boolean volume_present = ASSUME_VOLUME; // is there volume information in the bytestream?
volatile boolean tune_playing = false;
static boolean timer_running = false;
static const byte *score_start = 0;
static const byte *score_cursor = 0;
unsigned /*short*/ int scorewait_interrupt_count;
static unsigned /*short*/ int millisecond_interrupt_count;
static unsigned /*short*/ int interrupts_per_millisecond;
//****** tone generator ("channel") status
// Terminology note: we call tone generators "channels" here. That is not the same as
// MIDI channels, and we should have called them tone generators, as we do in Miditones.
// Question: would an array of structures here generate better code? Probably not.
static int32_t chan_accumulator [MAX_CHANS]; // current value of division accmulator
static int32_t chan_decrement [MAX_CHANS]; // how much to subtract at each interrupt
static uint16_t chan_level [MAX_CHANS]; // the current D-to-A level for this channel, 0..4095
static byte chan_volume [MAX_CHANS]; // the volume of the note on this channel, 0..127
static byte chan_instrument [MAX_CHANS]; // the instrument currently playing, 0..255
static uint16_t const *chan_sample [MAX_CHANS]; // pointer to the sample array
static unsigned int chan_sample_index [MAX_CHANS]; // current index into sample array
static unsigned int chan_sample_index_limit [MAX_CHANS]; // maximum index for percussion
static byte chan_sample_increment [MAX_CHANS]; // how much to increment sample index by
static byte chan_playing [MAX_CHANS]; // is this channel currently playing?
static byte num_chans_used = MAX_CHANS; // how many channels this music really uses
static byte amplitude_shift_count = 0; // how many bits to shift off the amplitude when combining notes
static const byte amplitude_shift_counts[MAX_CHANS] PROGMEM = { //1..16 channels playing
/* Shift table to reduce channel volume based on how many channels we're playing.
To be absolutely safe we should use log2(n) amplitude shifts for n channels
But we're actually more conservative and shift less than that, assuming that highs won't often
be coincident and we will clip when it does happen. YMMV.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 num of channels
0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 true log2(n) */
0000, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3 // conservative
};
struct file_hdr_t { // the optional bytestream file header
char id1; // 'P'
char id2; // 't'
unsigned char hdr_length; // length of whole file header
unsigned char f1; // flag byte 1
unsigned char f2; // flag byte 2
unsigned char num_tgens; // how many tone generators are used by this score
} file_header;
#define HDR_F1_VOLUME_PRESENT 0x80
#define HDR_F1_INSTRUMENTS_PRESENT 0x40
#define HDR_F1_PERCUSSION_PRESENT 0x20
// note commands in the bytestream
#define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte, maybe volume */
#define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */
#define CMD_INSTRUMENT 0xc0 /* change instrument; low nibble is generator #, instrument is next byte */
#define CMD_RESTART 0xe0 /* restart the score from the beginning */
#define CMD_STOP 0xf0 /* stop playing */
/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit big-endian number of msec to wait */
/*------------------------------------------------------------------------------
We use various tables to control how notes are played:
-- accumulator decrement values for deciding when to move to the next sample
-- increment values to specify distance between samples used at each interrupt
These depend on the polling frequency and the accumulator restart value.
To generate a tone, we basically do incremental division for each channel in the
polling interrupt routine to determine when the next sample point should be used:
accum -= decrement
if (accum < 0) {
use the Kth next sample point
accum += ACCUM_RESTART
}
For regular instruments, we use all 256 samples (K=1) for low-frequency notes, and repeat until
the note is stopped. When the frequency is high enough so that more than one sample would be
needed for each polling interval, we start skipping samples by increasing K according to the
note_sample_increments array.
For percussion instruments, we use an arbitrary number of samples for the sound, but only play
it through only once and then stop.
*/
#define ACCUM_RESTART (int32_t)1073741824 // 2^30. Generates 1-byte arithmetic on 4-byte numbers!
#define MIN_NOTE 21 // we only do the piano range
#define MAX_NOTE 108
#define NUM_NOTES (MAX_NOTE - MIN_NOTE + 1)
#define MAX_POLLTIME_USEC 100 // 10 Khz
#define MIN_POLLTIME_USEC 5 // 200 Khz
static int32_t note_decrements[NUM_NOTES]; // decrements for incremental division for notes
static byte note_sample_increments[NUM_NOTES]; // wave table index increments for notes
// well-tempered note frequencies, based on the 12th root of 2.
const uint32_t freq4096 [NUM_NOTES] PROGMEM = { // note frequencies * 4096
/* 0..20 33488, 35479, 37589, 39824, 42192, 44701, 47359, 50175, 53159, 56320,
59669, 63217, 66976, 70959, 75178, 79649, 84385, 89402, 94719, 100351, 106318,*/
/* 21..108*/ 112640, 119338, 126434, 133952, 141918, 150356, 159297,
168769, 178805, 189437, 200702, 212636, 225280, 238676, 252868,
267905, 283835, 300713, 318594, 337539, 357610, 378874, 401403,
425272, 450560, 477352, 505737, 535809, 567670, 601425, 637188,
675077, 715219, 757749, 802807, 850544, 901120, 954703, 1011473,
1071618, 1135340, 1202851, 1274376, 1350154, 1430439, 1515497,
1605613, 1701088, 1802240, 1909407, 2022946, 2143237, 2270680,
2405702, 2548752, 2700309, 2860878, 3030994, 3211227, 3402176,
3604480, 3818814, 4045892, 4286473, 4541360, 4811404, 5097505,
5400618, 5721755, 6061989, 6422453, 6804352, 7208960, 7637627,
8091784, 8572947, 9082720, 9622807, 10195009, 10801236, 11443511,
12123977, 12844906, 13608704, 14417920, 15275254, 16183568,
17145893
/* 109..123 , 18165441, 19245614, 20390018, 21602472, 22887021,
24247954, 25689813, 27217409, 28835840, 30550508, 32367136,
34291786, 36330882, 38491228, 40780036 */
};
//*********** REGULAR AND PERCUSSION INSTRUMENTS WE DEFINE ****************
// To add a regular instrument, you must do ALL FOUR things below
// and keep instruments in order!
// (1) add an external reference here to the wave table you put in Playtune_samp_waves.c
extern const uint16_t sample_aguitar_0033[256] PROGMEM;
extern const uint16_t sample_altosax_0001[256] PROGMEM;
extern const uint16_t sample_birds_0011[256] PROGMEM;
extern const uint16_t sample_cello_0005[256] PROGMEM;
extern const uint16_t sample_clarinett_0001[256] PROGMEM;
extern const uint16_t sample_clavinet_0021[256] PROGMEM;
extern const uint16_t sample_dbass_0015[256] PROGMEM;
extern const uint16_t sample_ebass_0037[256] PROGMEM;
extern const uint16_t sample_eguitar_0002[256] PROGMEM;
extern const uint16_t sample_eorgan_0064[256] PROGMEM;
extern const uint16_t sample_epiano_0044[256] PROGMEM;
extern const uint16_t sample_flute_0001[256] PROGMEM;
extern const uint16_t sample_oboe_0002[256] PROGMEM;
extern const uint16_t sample_piano_0023[256] PROGMEM;
extern const uint16_t sample_violin_0003[256] PROGMEM;
// (2) put a pointer to your wave table at the end of this array
const uint16_t *instrument_samples[] = { // this order must match the enum below
sample_aguitar_0033, sample_altosax_0001, sample_birds_0011,
sample_cello_0005, sample_clarinett_0001, sample_clavinet_0021,
sample_dbass_0015, sample_ebass_0037, sample_eguitar_0002,
sample_eorgan_0064, sample_epiano_0044, sample_flute_0001,
sample_oboe_0002, sample_piano_0023, sample_violin_0003
};
// (3) add a symbolic index name for the regular instrument at the end of this list
enum { // instrument indexes
I_AGUITAR, I_SAX, I_BIRDS, I_CELLO, I_CLARINET, I_CLAVINET, I_DBASS, I_EBASS,
I_EGUITAR, I_ORGAN, I_EPIANO, I_FLUTE, I_OBOE, I_PIANO, I_VIOLIN
};
// (4) change whatever entries in the patch map corresponding to regular instruments
// that you want your new wave sample to play for
// (I didn't have a lot of patience to add many instruments, so some of these assignments are pretty random!)
const byte instrument_patch_map[128] PROGMEM = { // map from MIDI patch numbers to instrument indexes
/*1-8: DBASS*/ I_DBASS, I_DBASS, I_EBASS, I_DBASS, I_EBASS, I_EBASS, I_EBASS, I_EBASS,
/*9-16: chromatic percussion*/ I_CLAVINET, I_CLAVINET, I_CLAVINET, I_CLAVINET, I_CLAVINET, I_CLAVINET, I_CLAVINET, I_CLAVINET,
/*17-24: Organ*/ I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN,
/*25-32: guitar*/ I_AGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_AGUITAR,
/*33-40: bass*/ I_DBASS, I_EBASS, I_EBASS, I_DBASS, I_DBASS, I_DBASS, I_EBASS, I_EBASS,
/*41-48: strings*/ I_VIOLIN, I_VIOLIN, I_CELLO, I_CELLO, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN,
/*49-56: ensemble*/ I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN,
/*57-66: brass*/ I_DBASS, I_DBASS, I_DBASS, I_DBASS, I_DBASS, I_DBASS, I_DBASS, I_DBASS,
/*65-72: reed*/ I_SAX, I_SAX, I_SAX, I_OBOE, I_OBOE, I_SAX, I_SAX, I_OBOE,
/*73-80: pipe*/ I_FLUTE, I_FLUTE, I_FLUTE, I_FLUTE, I_FLUTE, I_FLUTE, I_FLUTE, I_FLUTE,
/*81-88: synth lead*/ I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR, I_EGUITAR,
/*89-96: synth pad*/ I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN, I_VIOLIN,
/*97-104: synth effects*/ I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS,
/*105-112: ethnic*/ I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN, I_ORGAN,
/*113-120: percussive*/ I_EBASS, I_EBASS, I_EBASS, I_EBASS, I_EBASS, I_EBASS, I_EBASS, I_EBASS,
/*121-128: sound effects*/ I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS, I_BIRDS
};
#if DO_PERCUSSION
// To add a percussion instrument, you must do ALL SIX things below
// and keep instruments in order!
// (1) add an external reference here to the wave table you put in Playtune_samp_waves.c
extern const uint16_t sample_base_drum_04[] PROGMEM;
extern const uint16_t sample_snare_drum_1[] PROGMEM;
extern const uint16_t sample_mid_high_tom[] PROGMEM;
extern const uint16_t sample_cymbal_2[] PROGMEM;
extern const uint16_t sample_hi_bongo[] PROGMEM;
extern const uint16_t sample_steel_bell_c6[] PROGMEM;
// (2) put the pointer to your wave table at the end of this array, before the NULL
static const uint16_t *drum_samples[] = {
sample_base_drum_04, sample_snare_drum_1, sample_mid_high_tom,
sample_cymbal_2, sample_hi_bongo, sample_steel_bell_c6,
NULL /* stopper so we can iterate over drum indexes */
};
// (3) put the size of it at the end of a table we refer to here that is
// actually located at the bottom of Playtune_samp_waves.c
extern const uint16_t drum_sample_size[];
// (4) add an element to the end of this array telling what the sampling frequency is
const uint16_t drum_sample_frequencies[] = {
4000, 8000, 8000, 8000, 4000, 4000
};
// (4) add another zero to this array
static int32_t drum_decrements[] = {
0, 0, 0, 0, 0, 0
};
// (5) add a symbolic index name for the percussion instrument at the end of this list
enum { // drum indexes
D_BASS, D_SNARE, D_TOM, D_CYMBAL, D_BONGO, D_BELL
};
// (6) finally, change whatever entries in the patch map correspond to percussion instruments
// that want your new wave sample to play for
// (I didn't have a lot of patience to add many instruments, so some of these assignments are pretty random!)
const byte drum_patch_map[128] PROGMEM = { // map from MIDI percussion instruments to drum indexes
/*01-16*/ D_BASS, D_SNARE, D_TOM, D_CYMBAL, D_BONGO, D_BELL, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS,
/*17-32*/ D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS,
/*33-48*/ D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_SNARE, D_SNARE, D_SNARE, D_TOM, D_CYMBAL, D_TOM, D_CYMBAL, D_TOM, D_CYMBAL, D_TOM, D_TOM,
/*49-64*/ D_CYMBAL, D_TOM, D_CYMBAL, D_CYMBAL, D_BELL, D_SNARE, D_CYMBAL, D_BELL, D_CYMBAL, D_CYMBAL, D_CYMBAL, D_BONGO, D_BONGO, D_BONGO, D_BONGO, D_BONGO,
/*65-80*/ D_TOM, D_TOM, D_BELL, D_BELL, D_CYMBAL, D_CYMBAL, D_BELL, D_BELL, D_BONGO, D_BONGO, D_BONGO, D_BONGO, D_BONGO, D_TOM, D_TOM, D_BELL,
/*81-96*/ D_BELL, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS,
/*97-112*/ D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS,
/*113-128*/ D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS, D_BASS
};
#endif // DO_PERCUSSION
//------------------------------------------------------------------------------
// Initialize and start the polling timer
//------------------------------------------------------------------------------
void tune_start_timer(int polltime/*usec*/) {
// Decide on an interrupt poll time
if (polltime) // set by the caller
polltime = max( min(polltime, MAX_POLLTIME_USEC), MIN_POLLTIME_USEC);
else { // polltime isn't specified; try to pick a good one
polltime = MAX_POLLTIME_USEC; // assume the worst
if (F_CPU >= 48000000L) polltime = 25; // unless the clock is really fast
#ifdef CORE_TEENSY
if (F_CPU < 50000000L)
polltime = 25; // slow Teensies
else polltime = 15; // fast teensies
#endif
}
interrupts_per_millisecond = 1000 / polltime; // this is a truncated approximation
millisecond_interrupt_count = interrupts_per_millisecond;
#if DBUG
Serial.print("polltime "); Serial.print(polltime);
Serial.print(", ints per msec "); Serial.println(interrupts_per_millisecond);
#endif
// Create the tables we will use. This uses 64-bit arithmetic, but we only do it at initialization.
for (int note = MIN_NOTE; note <= MAX_NOTE; ++note) { // for regular instruments
// increment = INT(256 * Fnote)/Fclk) + 1
note_sample_increments[note - MIN_NOTE] = (((int64_t)pgm_read_dword(freq4096 + note - MIN_NOTE) >> 4) * polltime
/ 1000000) + 1; // how to skip samples, if at all
// decrement = (RESTART * 256 * Fnote) / (Fclk * incr)
note_decrements[note - MIN_NOTE] = (((int64_t) ACCUM_RESTART * (int64_t)pgm_read_dword(freq4096 + note - MIN_NOTE)) >> 4) * polltime
/ 1000000 / note_sample_increments[note - MIN_NOTE];
#if 0 & DBUG
Serial.print("note "); Serial.print(note);
Serial.print(", incr "); Serial.print(note_sample_increments[note - MIN_NOTE]);
Serial.print(", decr "); Serial.println(note_decrements[note - MIN_NOTE]);
#endif
}
#if DO_PERCUSSION
for (int drum = 0; drum_samples[drum]; ++drum) { // for percussion instruments
// decrement = (RESTART * Fsample) / Fclk
drum_decrements[drum] = ((int64_t) ACCUM_RESTART * (int64_t) drum_sample_frequencies[drum] * polltime) / 1000000;
#if 0 & DBUG
Serial.print("drum "); Serial.print(drum);
Serial.print(", sample size "); Serial.print(drum_sample_size[drum]);
Serial.print(", decr "); Serial.println(drum_decrements[drum]);
#endif
}
#endif //DO_PERCUSSION
// define our output pin
#ifdef __AVR_ATmega32U4__
pinMode(6, OUTPUT); // PWN output on OCR4D
TCCR4A = 0b00000000; // nothing on outputs A or B
TCCR4B = 0b00000001; // use fast clock (no prescaling), so 16 Mhz, or 62.5 Khz D-to-A
TCCR4C = 0b00001001; // 10: non-inverting output on OC4D pin in fast PWM mode, OCR4D compare
TCCR4D = 0b00000000; // WGM41..40 == 00: Fast PWM mode
TCCR4E = 0b00000000; // not enhanced mode
#endif
#ifdef CORE_TEENSY
analogReference(INTERNAL);
analogWriteResolution(12);
#endif
#if SCOPE_TEST
pinMode(SCOPE_PIN, OUTPUT);
digitalWrite(SCOPE_PIN, 0);
#endif
Timer1.initialize(polltime); // start the timer
Timer1.attachInterrupt(timer_ISR);
timer_running = true;
}
//------------------------------------------------------------------------------
// Random byte generator
//
// This is an 8-bit version of the 2003 George Marsaglia XOR pseudo-random number
// generator. It has a full period of 255 before repeating.
// See http://www.arklyffe.com/main/2010/08/29/xorshift-pseudorandom-number-generator/
//------------------------------------------------------------------------------
static byte seed = 23;
byte random_byte(void) {
seed ^= (byte)(seed << 7); // The casts are silly, but are needed to keep the compiler
seed ^= (byte)(seed >> 5); // from generating "mul 128" for "<< 7"! Shifts are faster.
seed ^= (byte)(seed << 3);
return seed;
}
//------------------------------------------------------------------------------
// Start playing a note on a particular channel
//------------------------------------------------------------------------------
void tune_playnote (byte chan, byte note, byte vol) {
if (chan < MAX_CHANS) {
if (note >= 128) { // percussion instrument
#if DO_PERCUSSION
byte instrument_enum = pgm_read_byte(drum_patch_map + note - 128);
chan_sample[chan] = drum_samples [instrument_enum];
chan_sample_index_limit[chan] = drum_sample_size[instrument_enum];
chan_decrement[chan] = drum_decrements[instrument_enum];
chan_sample_increment[chan] = 1;
chan_sample_index[chan] = 0;
// percussion notes generally seem undermodulated, so we double the volume we get and clip
if (BOOST_PERCUSSION) vol = vol > 63 ? 127 : vol << 1;
#if DBUG
Serial.print("chan="); Serial.print(chan);
Serial.print(" drum="); Serial.print(instrument_enum);
Serial.print(" vol="); Serial.print(vol);
Serial.print(" index_limit="); Serial.print(chan_sample_index_limit[chan]);
Serial.print(" decr="); Serial.println(chan_decrement[chan]);
#endif
#else //!DO_PERCUSSION
return; // ignore percussion note
#endif
}
else { // regular instrument
if (note < MIN_NOTE) note = MIN_NOTE;
if (note > MAX_NOTE) note = MAX_NOTE;
chan_sample[chan] = instrument_samples [pgm_read_byte(instrument_patch_map + chan_instrument[chan])];
chan_sample_index_limit[chan] = 256;
chan_decrement[chan] = note_decrements[note - MIN_NOTE];
chan_sample_increment[chan] = note_sample_increments[note - MIN_NOTE];
// start at a random place in the wavecycle to minimize phase lock cancellation
chan_sample_index[chan] = random_byte();
#if DBUG
Serial.print("chan="); Serial.print(chan);
Serial.print(" note="); Serial.print(note);
Serial.print(" vol="); Serial.print(vol);
Serial.print(" instr="); Serial.print(chan_instrument[chan]);
Serial.print(" incr="); Serial.print(chan_sample_increment[chan]);
Serial.print(" decr="); Serial.println(chan_decrement[chan]);
#endif
}
chan_volume[chan] = vol & 0x7f;
chan_accumulator[chan] = ACCUM_RESTART;
chan_level[chan] = 2048; // starting level is in the middle
chan_playing[chan] = true; // go!
}
}
//------------------------------------------------------------------------------
// Stop playing a note on a particular channel
//------------------------------------------------------------------------------
void tune_stopnote (byte chan) {
if (chan < MAX_CHANS) {
if (chan_playing[chan]) {
chan_playing[chan] = false;
#if DBUG
Serial.print(" stop chan "); Serial.println(chan);
#endif
}
}
}
//------------------------------------------------------------------------------
// Play a score
//------------------------------------------------------------------------------
void tune_stepscore (void);
void tune_playscore (const byte * score) { // start up the score
if (tune_playing) tune_stopscore();
if (!timer_running) tune_start_timer(0);
score_start = score;
volume_present = ASSUME_VOLUME;
num_chans_used = MAX_CHANS;
for (byte chan = 0; chan < MAX_CHANS; ++chan) chan_instrument[chan] = 0;
// look for the optional file header
memcpy_P(&file_header, score, sizeof(file_hdr_t)); // copy possible header from PROGMEM to RAM
if (file_header.id1 == 'P' && file_header.id2 == 't') { // validate it
volume_present = file_header.f1 & HDR_F1_VOLUME_PRESENT;
num_chans_used = max(1, min(MAX_CHANS, file_header.num_tgens));
#if DBUG
Serial.print("header: volume_present="); Serial.print(volume_present);
Serial.print(", #chans="); Serial.println(num_chans_used);
#endif
score_start += file_header.hdr_length; // skip the whole header
}
// We will attentuate amplitudes prior to combining notes based on the
// worst-case number of notes that might be playing simultaneously.
// Since we only shift instead of divide (too slow!), this is a rough
// logarithmic approximation instead of an average.
amplitude_shift_count = pgm_read_byte(amplitude_shift_counts + num_chans_used - 1);
#if DBUG
Serial.print("amplitude shift count is "); Serial.println(amplitude_shift_count);
#endif
score_cursor = score_start;
tune_stepscore(); /* execute initial the commands and return */
tune_playing = true;
}
void tune_stepscore (void) { //********* continue in the score
byte cmd, opcode, chan, note, vol;
/* Do score commands until a "wait" is found, or the score is stopped.
This is called initially from tune_playscore, but then is called
from the slow interrupt routine when waits expire.
*/
while (1) {
cmd = pgm_read_byte(score_cursor++);
if (cmd < 0x80) { /* wait count in msec. */
/* wait count is in msec. */
scorewait_interrupt_count = ((unsigned)cmd << 8) | (pgm_read_byte(score_cursor++));
#if DBUG
//scorewait_interrupt_count *= 10; //TEMP slow down for debugging
Serial.print("waitcount="); Serial.println(scorewait_interrupt_count);
#endif
break;
}
opcode = cmd & 0xf0;
chan = cmd & 0x0f;
if (opcode == CMD_STOPNOTE) { /* stop note */
tune_stopnote (chan);
}
else if (opcode == CMD_PLAYNOTE) { /* play note */
note = pgm_read_byte(score_cursor++); // argument evaluation order is undefined in C!
vol = volume_present ? pgm_read_byte(score_cursor++) : 127;
tune_playnote (chan, note, vol);
}
else if (opcode == CMD_INSTRUMENT) { /* change a channel's instrument */
chan_instrument[chan] = pgm_read_byte(score_cursor++);
}
else if (opcode == CMD_RESTART) { /* restart the score */
score_cursor = score_start;
}
else if (opcode == CMD_STOP) { /* stop playing the score */
tune_stopscore();
break;
}
}
}
//------------------------------------------------------------------------------
// Stop playing a score
//------------------------------------------------------------------------------
void tune_stopscore (void) {
int i;
for (i = 0; i < MAX_CHANS; ++i)
tune_stopnote(i);
tune_playing = false;
}
//------------------------------------------------------------------------------
// Stop all channels
//------------------------------------------------------------------------------
void tune_stop_timer(void) {
tune_stopscore();
Timer1.stop();
Timer1.detachInterrupt();
timer_running = false;
}
//------------------------------------------------------------------------------
// Timer interrupt Service Routine
//
// We look at each playing note on the active channels,
// and determine whether it is time to create the next edge of the square wave
//
// This routine needs to be really fast! Avoid function calls, division, etc.
//------------------------------------------------------------------------------
static uint16_t old_level = 2048;
void timer_ISR(void) { //**** THE TIMER INTERRUPT COMES HERE ****
#if SCOPE_TEST // turn on the scope probe output
#ifdef CORE_TEENSY
digitalWriteFast(SCOPE_PIN, HIGH);
#else
JOIN(PIN, SCOPE_REG) = (1 << SCOPE_BIT);
#endif
#endif
// first, check for millisecond timing events
if (--millisecond_interrupt_count == 0) {
millisecond_interrupt_count = interrupts_per_millisecond;
// decrement the current score wait counter
if (tune_playing && scorewait_interrupt_count && --scorewait_interrupt_count == 0) {
tune_stepscore (); // end of a score wait, so execute more score commands
}
}
// Now, check for playing notes that need to be adjusted.
uint16_t level;
level = 0;
for (byte chan = 0; chan < num_chans_used; ++chan) {
if (chan_playing[chan]) {
chan_accumulator[chan] -= chan_decrement[chan];
if (chan_accumulator[chan] < 0) { // time for next sample
chan_sample_index[chan] += chan_sample_increment[chan]; // point to next sample
if (chan_sample_index_limit[chan] > 256) { // percussion sample
if (chan_sample_index[chan] >= chan_sample_index_limit[chan])
chan_playing[chan] = false; // end of percussion sample; stop playing
}
else {
chan_sample_index[chan] &= 0xff; // modulo 256 for note samples
}
chan_level[chan] = pgm_read_word(chan_sample[chan] + chan_sample_index[chan]);
chan_accumulator[chan] += ACCUM_RESTART;
}
#ifdef CORE_TEENSY
// For Teensy we allow ourselves one multiplication here, since it only takes 1 or 2 cycles. But no divisions!
level += ((uint32_t)chan_level[chan] * (1 + chan_volume[chan])) >> (amplitude_shift_count + 7);
#else
... need to bring back the volume shift table; multiplication is too slow on AVR!
#endif
}
}
if (level > 4095) level = 4095; // clip at maximum
if (level != 0 && level != old_level) { // if there is anything playing and it changed, output new value
#ifdef TEENSY_LC // we crib optimized code from analog.c for speed
// analogWrite(DAC_PIN, level);
SIM_SCGC6 |= SIM_SCGC6_DAC0;
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS | DAC_C0_DACSWTRG; // 3.3V VDDA
*(int16_t *)&(DAC0_DAT0L) = level;
#endif
#ifdef TEENSY_3x // we crib optimized code from analog.c for speed
SIM_SCGC2 |= SIM_SCGC2_DAC0;
DAC0_C0 = DAC_C0_DACEN; // 1.2V ref is DACREF_1
*(int16_t *)&(DAC0_DAT0L) = level;
#endif
#ifdef __AVR_ATmega32U4__ // change PWM duty cycle, 0..255
OCR4D = level >> 4;
#endif
// #else: .... TODO for other microcontrollers...
old_level = level;
}
#if SCOPE_TEST // turn off the scope probe output
#ifdef CORE_TEENSY
digitalWriteFast(SCOPE_PIN, LOW);
#else
JOIN(PIN, SCOPE_REG) = (1 << SCOPE_BIT);
#endif
#endif
}