Search the Community
Showing results for tags 'DH100'.
-
Finally got the DH-100 digital horn telling the Atari8 what tones to make and when. There was a couple of times I didn't think it was going to happen. Luckily the compiled basic program runs fast enough to keep up with the filtered data from the Arduino. Some sound tests have been recorded and placed in the following ZIP file. sound checks.zip Nothing has been done to enhance the sound from the Pokey chip other then combining 2 channels to allow the use of 16 bits to define the tones. The breath controller was turned on during the recording to maximize any delay that may exist. While messing with the mixer and sound effects I recorded a bonus file for your listening pleasure. Cranked up the sound effects to full on, so I hope it don't make your ears bleed. I may record some music when I get the band together. (Or should I say, I have to get the ST operational and remember how the Band-In-A-Box software works) The technical stuff: This project has been ongoing over the last 6 months and will continue to be modified/ tweaked probably as often as it is used. Older blog entries begin with "DH100". It should make them easier to find. The Arduino interface hardware hasn't changed since it was last described. It is close to being a good general purpose design for manipulating the joystick port. I'm debating if I should design a PC board for an Arduino shield. I have to build 5 or 6 more interfaces for the Atari MIDI rack of my dreams. The software used for the Arduino and Atari8 are in the following zip file. You should be able to follow most of the logic. I tried to comment. DHMIDI.zip DH100-2-ATARI8 folder. Holds the Arduino program. The biggest difference from the last blog entry is that a counter for the Breath control data was added. The counter keeps track of the number of 208 MIDI commands (pressure control set points) have been received. Then every 15th data point is sent to the Atari and the counter reset. This greatly reduce the number of data points being sent to the Atari and still give some degree of volume control. DHMIDI.ATR contains- DHMIDI.EXE is the compiled DHMIDI.BAS using the MMG BASIC Compiler. The double precession integer math used instead of the floating point package greatly increased the speed of the program. Most of the received MIDI commands are printed to the screen since the display doesn't need to be turned off to save processor time. DHMIDI.BAS is the program that receives the data from the Arduino and turns it into sounds. This may help if you wish to examine the code. 1-9 Title and program information 10 GOSUB 14000 to set up variables, then GOTO 500 to start 100-150 subroutine to get a byte of data from the joystick port- returns x 500-530 gets input - if command then split upper and lower bits 600-630 Pressure sensor command - set new volume 700-720 Set Portomento flag 800-830 Set Atari Distortion level - should be used to select different voice envelopes 900-990 Plays note when portomento is turned off or redirects to portomento routine 2000-2060 portomento slide to next note. Same number steps between any 2 notes 14000-14200 sets up variables, tables, register names, motor control bit, unitize sound registers 15000-15170 data for audio control bytes Low byte, High byte and double byte. Used to save time calculating these number when called. Saved so MIDI note number = index 15500 data for distortion number. 10 -pure tone. upper 4 bits of AUDIO Control 2 location. It hurts my brain when I try to think of all the ways this program can be improved/modified. The system works as a monophonic sound source when any MIDI instrument is hooked up and transmitting data on channel 1.
-
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.
-
I have 9 optocouplers setup to receive data from the Arduino for the SAM Rock You project. Eight for the MIDI data byte from the Arduino and 1 to signal the Trigger when new data is ready to be read by the Atari8. A tenth optocoupler needs to be added so that the A8 can let the Arduino know that it is ready for the next byte. That 10th optocoupler can indicate the status set using the Cassette Motor Control. The Cassette Motor Control line on the SIO port (Pin8) is used to turn the cassette motor on and off. I have used an accessory for the Diamond GOS that sets this bit to control the cassette for audio play back. Even though I wrote the accessory, I can't remember which bit to set but I remembered the program would set that bit to test the circuit. The circuit was put together on a breadboard for testing before soldering onto the prototype board. Preliminary tests proved that the optocoupler would switch states to achieve 2000 bytes/sec. Atari BASIC isn't going to outpace this circuit. The input side of the optocoupler was wired to SIO PIN 8(motor control) and PIN4(ground). The Arduino was programed to read a pin status set by the output of the optocoupler and then turning on/off an LED. The Diamond GOS "ON/OFF CAS" accessory successfully toggle SIO-Pin8 when activated. This simple Arduino program is used to read the optocoupler output attached to Arduino digital pin 4 and turn on the LED attached to pin 6. //check connection between A8 sio cassette //motor pin and Arduino int casMotorPin= 4; int ledPin= 6; void setup() { // put your setup code here, to run once: pinMode(casMotorPin, INPUT); pinMode(ledPin, OUTPUT); } void loop() { if (digitalRead(casMotorPin) == HIGH){ digitalWrite(ledPin, HIGH);} else { digitalWrite(ledPin, LOW);} } Later the prototype was soldered together and added to the Arduino hardware board. The next step is to write the software for the Arduino to pass all relevant MIDI data to the Atari and a short Atari program to monitor the incoming data and test the data flow control. I suppose I should also look over the accessory source code to remind myself how to set the cassette motor control bit.