Search the Community
Showing results for tags 'DH-100'.
-
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
-
I was going to start writing my list of resolutions for 2018. Then I thought about writing about what I didn't accomplish in 2017. Then I decided to just work on programing the interface and get the Atari to read and print the MIDI data stream through the joystick port. I had high hopes of creating a midi monitor that would accept the data and print out the commands as they were received but all that extra code was getting in the way of finding errors. I was happy when I got the Atari8 and Arduino to just reliably print the correct bytes. A SIO cable was to be modified because the SIO2PC dead-ends the SIO chain. I just attached jumper wires with female ends to the 4th and 8th pins on a standard cable. Saved me a lot of plugging and unplugging. I wasn't looking forward to programming the Arduino and Atari 8 because trouble shooting was going to be a painful endeavor. Was it the interface hardware, the Arduino program or the Atari8 program that is the problem. Yes, I had problems with all three and even created more. The Arduino Program. The Arduino performs the following steps. 1. Reads the midi data stream and puts it into a buffer. Continuously. 2. Gets a bit from the buffer. FIFO 3. Sets control pins for the 8 joystick pins 4. Sets trigger to signal that data is ready to be read 5. Waits for Atari to signal it got the data 6. Sets the trigger to signal data not ready 7. Waits for Atari to signal reset the process to step 2. Its simple, when you remember that the logic from the Arduino is inverted. When a pin on the Arduino is set high the transistor in the optocoupler turns on, shorting the pin to ground. Thus making the logic level, read by the Atari, LOW. /* Midi data transfer test 12/2017 * * This program reads midi data then ouputs * 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. * */ // 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; //data set ready int DTR = 12;//Atari ready byte zero = 0; byte tempX = 0; int midiData = 0; //Function to Send a byte to the atari and 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() { if (Serial.available()>0){ midiData = Serial.read(); //read MIDI stream tempX = byte(midiData); sendByte(tempX); //Send to Atari } } The Atari Program When the Atari wants data it checks the trigger button to see if there is any to read. If not, this program simply waits for one to show up. 10 GOSUB 29000:REM SET UP VARIABLES 15 POKE PACTL,CMCOFF:REM CAS.OFF 20 IF STRIG(0)=0 THEN GOTO 20:REM WAIT ARDUINO TO SIGNAL DATA READY 30 MIDIDATA=PEEK(PORTA):REM READ 35 ? MIDIDATA;" ";:SOUND 0,200,15,15:SOUND 0,0,0,0:REM SHOW DATA ON SCREEN 40 POKE PACTL,CMCON:REM GOT DATA 60 IF STRIG(0)=1 THEN GOTO 60:REM ARDUINO BUSY GETTING DATA 70 POKE PACTL,CMCOFF:REM TELL ARDUINO TO HOLD DATA UNTILL READY 80 GOTO 20:REM GO WAIT FOR NEXT BYTE TO BE READY 29000 PORTA=54016:REM STICK 0 AND 1 29020 PACTL=54018:REM PORTA CONTROL 29030 CMCON=52:REM CASSETTE MOTOR ON 29040 CMCOFF=60:REM CAS. MOTOR OFF 29050 REM POKE 559,0:REM TRUN OFF SCREEN 29100 RETURN A Casio Keyboard was attached to the interface and data was sent to the Atari and dumped on the screen. The following screen shot shows three 255s that were produced when the Arduino was turned on and the reset button was pushed twice. Then anytime a key was pressed or released a NOTE ON command on channel 0 (144) was sent. You see the 144 then the MIDI key number and then the volume setting. If a volume setting was 0 the key was released. The keyboard is not pressure sensitive so its volume defaults to 100. I didn't see any data being missed by the Atari but it was so slow. I mean, it make 300 baud seem fast. I mean, it was so slow that I tried to turn off the display. I mean, it was soooooo slooooooow that I thought I would have to get out the M65 cartridge. Instead, the program was compiled using the MMG Compiler. The speed improvement was remarkable. I couldn't get the buffer to over flow even when a bunch of keys were pressed and released. Since the digital horn is monophonic, its data output should be manageable even when it is transmitting the breath pressure(after touch) data. I also took the time to load the compiled program onto a Max Flash cartridge. Easily running these programs on computers with out a monitor and disk drive will simplify the hardware setup significantly. There was a problem when the Arduino was powered up, all the joystick pins were reading LOW. This confused the Max Flash menu and the program didn't load when the return was pressed. Pressing the Reset button on the Arduino resets the pins until they are turned off again by void Setup() function and hitting the return key on the Atari before that happens will run the program. Now on to writing the program that actually makes some noise and maybe a little music.
-
Between the time that the Casio DH-100 Digital Horn was introduced and todays prices on eBay, the price dropped to the point where it seemed to be a bargain. I was fortunate enough to have pulled the batteries (15 years ago), so there was a good chance that it would still work. Now I want to build an Atari8 sound module to accept MIDI information from the DH-100's MIDI port(or any MIDI controller). The MIDI Implementation chart for the DH-100 is not extensive but the streaming of the channel pressure data could easily overwhelm the system. I'm going to attempt to do this in Atari BASIC to facilitate experimentation. The plan is to increase the data transfer rate from the MIDI-Arduino interface to the computer by using the cassette motor pin on the SIO port to set up feed back to the Arduino. The Trigger can indicate DataSetReady and cassette motor control for DataTerminalReady thus eliminate the use of delay loops for transfer timing. My previous Atari BASIC MIDI programs have been kept very simple; mostly NOTE ON and NOTE OFF. The MIDI Implementation Chart shows 4 midi commands that can be sent from the DH-100 to control the sound. A closer look at the horn and chart should help define the program requirements for the A8. CASIO DH-100 Call it a digital horn, wind controller, or breath controller, the DH-100 works like a wind instrument. Press some keys to change the pitch and blow into the mouth piece. The harder you blow the louder the sound. On its own as a performance instrument, the internal speaker and sound synthesizer made it sound like an expensive toy. The sounds could get annoying after about 10 min. of play. (A personal observation.) The MIDI OUT port was its redeeming feature. Use it as a MIDI controller on a $500 MIDI synth and it sounded like a $500 instrument. But that's not what I want to do. I want the output for the DH-100 to control a Atari8 programed to be the sound synthesizer. Then it will sound a little POKEY. (The DH-100 has a tendency to develop a squeal(audio feedback loop). This is generally caused by 2 capacitors that can be replaced. It happened to my horn. Even though the internal sounds were useless, the MIDI output still worked. I turned down the volume and played the synth. Eventually I found the information on which capacitors to change and how. The original sound is restored. Sorry, that was a couple of years ago and the link to the information has been lost.) The horn was hooked up to the MIDI monitor to view the data stream. This information will be of value when the Arduino and Atari are programmed. These are my observations while testing the switches and buttons. Casio DH-100 MIDI Implimentation Chart.pdf Breath - off: This allows the playing of the horn without having to blow into it. Press the note keys and the sound will be produced. Press sends a NOTE ON at velocity 64, Release sends a NOTE ON at zero velocity. Breath - on: Press the note keys and then blow into the mouthpiece. A NOTE ON will be sent with a velocity proportional to the force of your exhale. Stop blowing and a NOTE ON at 0 velocity will be sent. While the note is on, the pressure sensor in the horn monitors the air pressure and sends out velocity(or after touch) whenever there is a change. Command 192+channel#, pressure from 1-127. Its difficult to maintain a constant air pressure. Transpose Button - Press the button and the note number being sent by the horn is increased by one note number. This continues up until an octave is reached, then drops 2 octaves. Keep pressing and you end up where you started. The note number is changes by the DH-100, no MIDI data is sent. Tone Selector - cycles the tone from 0 to 5. When this is pressed a PROGRAM CHANGE command is sent. Command 192+channel#,nnnn(0-5). Portamento - When pressed, notes will slide from one to the next. It uses a CONTROL CHANGE command and is controller number 65. Release the key to turn it off. The command is 176+channel#, controller #, value. Value is 0 for off and 127 for on. The command is sent only when the status has changed. That completes the tour of the MIDI Implementation Chart. I noted that there may be a typo at the Note Number - Transmitted numbers cell. The range has to be greater then 36-39. The note numbers may have to be manipulated to stay within the range that the Atari can produce. I'll deal with that at a later date.