Search the Community
Showing results for tags 'digital horn'.
-
I finished work on the last blog entry, unplugged the keyboard and plugged the digital horn. I was a little surprised to see how much data the breath pressure sensor(after touch) was being streamed to the Atari 8. Far more then it could keep up with. The Arduino needed to be reprogramed to reduce the number of bytes being send to the Atari8. The interface software was tested with the previous Atari 8 test program and it performed much better. The data seemed to make sense and it was difficult to overwrite the serial input buffer on the Arduino. These are the ways that the data stream was reduced. The DH-100 has 4 types of data it will generate. The Arduino reads in the data, modifies it and sends it out in the void loop() function. Casio DH-100 MIDI output 1 Note On Event 1001nnnn, 0kkkkkkk, 0vvvvvvv (144+,n,v) 2 Control Change 1011nnnn, 0ccccccc, 0vvvvvvv (176+,65,v) value 0=off, 127=on (portomento) 3 Program Change 1100nnnn,0ppppppp (192+,p) p = 0 to 5 (patch change) 4 Channel Pressure 1101nnnn,0vvvvvvv (208+,v) v = 0-127 scaled to 0-15 for Atari volume(breath controller) The DH-100 will only transmit data on MIDI channel 1. The Arduino will check for any incoming command byte. If it is a command byte, it will have the 7th bit set and bits 0-3 will contain the channel number(zero for channel 1). If the data for the command can be stated in under 4 bits it can be combined with the command in one byte and sent. Channel Pressure: This command generates a continuous stream of data. This 2 byte command is reduced to one byte by scaling the 0-127 from the DH100 to 0 - 15 for the Atari. The Atari value replaces the channel number. It is far less sensitive to changes and by sending the byte only when it has changed will reduce the data flow. Program Change: has a value of 0 to 5. It also can be added to the command byte and sent as one byte. It doesn't happen very often but its still 1/2 the original data byte count. Control Change: The DH-100 only has one controller and it is 65 for the Portomento switch. The Atari can assume that if a Control Change command is received and, the 65 doesn't have to be sent. A value of 0 for off or 127 for on. Change the 127 to 1 and added it to the command byte. Another byte saved. Note On: The third byte of the command is the volume and can be handled the same way as the Channel Pressure. The second byte (note number) may as well be sent as an unmodified second byte. Reducing this to 2 bytes is still a savings in time. All of this data is checked for redundancy and not sent unless there is a change. When a command is not to be sent to the Atari the command is reset to zero and some how makes it back to reading the data until a command byte is detected otherwise it is sent using the second switch-case set of routines. The Atari can split the data off the command byte without to much trouble. The integer math of the MMG Basic complier should make it even faster. Where X = combined input data: Command = int(X/16)*16 DataValue= X - Command /* Midi data transfer test 1/2018 * * This program reads midi data then outputs * the data to the Atari Joystick ports. * * The use of the control lines to the Atari Trigger * and from the cassette motor control line on the SIO * port are used for data flow control. * * An effort to minimize data transfer by combining data bytes * since DH100 uses Midi channel 1 the first four bits of the * data byte can be used for Atari Volume(0-15) and controller * data. Atari can then strip the lower four bits and make the settings. */ // Atari PORTA(54016) mapped to pins on Arduino int porta0 = 4; int porta1 = 5; int porta2 = 6; int porta3 = 7; int porta4 = 8; int porta5 = 9; int porta6 = 10; int porta7 = 11; int DSR = 3; //Arduino ready int DTR = 12;//Atari ready byte zero = 0; byte midiData = 0; byte midiCommand = 0; byte midiNoteNum = 0; byte lastMidiNoteNum = 0; byte volume = 0; byte lastVolume = 0; byte portemento = 0; //0=OFF 127=ON byte patch = 0; //0 - 5 byte controlChange = 0; byte dataByte = 0; //Function to Send a byte to the Atari void sendByte(byte byteToSend){ setPorta(byteToSend); digitalWrite(DSR,LOW); //data ready-Trigger 0 while(digitalRead(DTR) == LOW){} //Wait for Atari to get byte cmc goes low digitalWrite(DSR,HIGH); //data not ready-trigger 1 while(digitalRead(DTR) == HIGH){} //wait for Atari to signal ok to get next byte } void setPorta(byte byteToMap){ // Sets digital pins to transfer data to Atari joystick ports(PORTA) // When digital port high, joystick pin shorted to ground or logic 0 if (byteToMap & B00000001){digitalWrite(porta0,LOW);} else {digitalWrite(porta0,HIGH);} if (byteToMap & B00000010){digitalWrite(porta1,LOW);} else {digitalWrite(porta1,HIGH);} if (byteToMap & B00000100){digitalWrite(porta2,LOW);} else {digitalWrite(porta2,HIGH);} if (byteToMap & B00001000){digitalWrite(porta3,LOW);} else {digitalWrite(porta3,HIGH);} if (byteToMap & B00010000){digitalWrite(porta4,LOW);} else {digitalWrite(porta4,HIGH);} if (byteToMap & B00100000){digitalWrite(porta5,LOW);} else {digitalWrite(porta5,HIGH);} if (byteToMap & B01000000){digitalWrite(porta6,LOW);} else {digitalWrite(porta6,HIGH);} if (byteToMap & B10000000){digitalWrite(porta7,LOW);} else {digitalWrite(porta7,HIGH);} } void setup() { pinMode(porta0,OUTPUT); pinMode(porta1,OUTPUT); pinMode(porta2,OUTPUT); pinMode(porta3,OUTPUT); pinMode(porta4,OUTPUT); pinMode(porta5,OUTPUT); pinMode(porta6,OUTPUT); pinMode(porta7,OUTPUT); pinMode(DSR,OUTPUT); pinMode(DTR,INPUT); Serial.begin(31250); setPorta(zero);// digitalWrite(DSR,HIGH); } void loop() { //read data until its a Midi command //command bytes will have bit 7 true while (midiCommand < 128){ while(Serial.available()<1){}//wait for data midiCommand = Serial.read(); //read MIDI stream } // get data required by command. // volume - make sure it is an incremental change // midiCommand = 0 if not valid or redundant - do not send // patch - should be >6 switch(midiCommand){ case 208:// Breath controller while(Serial.available()<1){} volume = Serial.read(); if(volume != 0){volume = map(volume,1,127,1,15);} if(volume == lastVolume){midiCommand = 0;} break; case 144:// NoteOn command while(Serial.available()<1){} midiNoteNum = Serial.read(); while(Serial.available()<1){} volume = Serial.read(); if(volume != 0){volume = map(volume,1,127,1,15);} if((volume == 0) && (midiNoteNum != lastMidiNoteNum)){midiCommand = 0;} break; case 192: //Patch change while(Serial.available()<1){} patch = Serial.read(); if (patch >5){midiCommand = 0;} break; case 176: //control change (65=portemento) 0 off,127 on while(Serial.available()<1){} controlChange = Serial.read(); if (controlChange == 65){ while(Serial.available()<1){} portemento = Serial.read(); if (portemento >0){portemento = 1;} //1 = on break; } else {midiCommand = 0; break; } default: //if not a DH-100 command ignore midiCommand = 0; break; } // //combine data and send if command <> 0 if (midiCommand != 0){ switch(midiCommand){ case 208: //breath controller dataByte = midiCommand + volume; sendByte(dataByte); lastVolume = volume; break; case 192: dataByte = midiCommand + patch; sendByte(dataByte); break; case 176: dataByte = midiCommand + portemento; sendByte(dataByte); break; case 144: dataByte = midiCommand + volume; sendByte(dataByte); lastVolume = volume; sendByte(midiNoteNum); lastMidiNoteNum = midiNoteNum; break; } } midiCommand = 0; } A8 - Frequency tables De Re Atari has a chapter on sound that is quite interesting. Seems that SOUND 1 and SOUND 2 can be combined to improve pitch control. Then I found the article "Perfect Pitch" by Fred Coffey in COMPUTE!'S SECOND BOOK OF ATARI. It provided the information and formulas to convert frequency data to the equivalent 16 bit numbers needed to produce those numbers and it seemed to make the whole process easy. A utility program has been written to produce the DATA statements for a 108 X 3 array. The MIDI note number from the Arduino can be used to index into the array and then the 16 bit number (to be used for portomento calculations), high byte, and low bit can be accessed for use. This program should make it easy to modify the data statements if changes are to be made. Who knows, maybe the need will arise when they are required in MAC/65 format. The frequency data came from a keyboard chart. <http://newt.phys.unsw.edu.au/jw/notes.html> Line 20 needs to be changed when the DATA statements are to be listed to disk. Then they can be ENTERed into a program. Typing the Frequency DATA once was enough for me. 10 LINE=5000:REM LINE NUMBER 20 OPEN #1,8,0,"S:":REM "D:FTABLE.BTX" 30 ? #1;"4999 REM FREQUENCY FOR MIDINOTE 21-108(NOTE A0 TO C8)" 40 TRAP 300 50 ? #1;LINE;" ";"DATA "; 60 FOR X=1 TO 5 90 READ PITCH 100 P2=INT((1789790/(2*PITCH)-7)/256) 110 P1=INT(1789790/(2*PITCH)-7-256*P2+0.5) 115 P0=P2*256+P1 117 REM 2 BYTE, HI BYTE, LO BYTE 120 ? #1;P0;",";P2;",";P1; 125 IF X<5 THEN ? #1;","; 130 NEXT X 140 ? #1 150 LINE=LINE+10 160 GOTO 50 300 ? #1;-1:CLOSE #1:END 490 REM FREQUENCY FOR MIDINOTE 21-108(NOTE A0 TO C8) 500 DATA 27.5,29.135,30.868,32.703,34.648,36.708,38.891,41.203,43.654,46.249,48.999,51.913 510 DATA 55,58.270,61.735,65.406,69.296,73.416,77.782,82.407,87.307,92.499,97.999,103.83 520 DATA 110,116.54,123.47,130.81,138.59,146.83,155.56,164.81,174.61,185,196,207.65 530 DATA 220,233.08,246.94,261.63,277.18,293.67,311.13,329.63,349.23,369.99,392,415.30 540 DATA 440,466.16,493.88,523.25,554.37,587.33,622.25,659.26,698.46,739.99,783.99,830.61 550 DATA 880,932.33,987.77,1046.5,1108.7,1174.7,1244.5,1318.5,1396.9,1480,1568,1661.2 560 DATA 1760,1864.7,1975.5,2093.0,2217.5,2349.3,2489,2637,2793,2960,3136,3322.4 570 DATA 3520,3729.3,3951.1,4186