/** * @(#)DTMFapplet1.java 1.0 15/12/02 * by Andy Murray * * This applet mimics the DTMF (Dual Tone Multi Frequency) behaviour of a telephone * pad. The idea of DTMF is that there are different notes for each row and column. * The button you press gives two notes - one for its row and one for its column. * In this applet you 'play' the pairs of notes by holding the mouse down on a button * for as long as you want the notes to last. Releasing the mouse button stops the notes. * * Unlike real phones, this applet allows you to interact with the settings to change * the notes, timbres, volumes and pan settings. It addresses the computer's sound * hardware and, if it finds a valid MIDI port, it presumes this is the standard * General MIDI kind, with predictable implementation and 128 timbres. * * Why did I do it? Because this was the original reason I wanted to learn java! * I had a music education CD-ROM published about 6 months ago with various activities * for secondary pupils. One of these is a project about using music to send secret * messages. There's an area of my website that can be accessed from links in the CD-ROM. * That is where I propose to add this applet so that pupils can use it in their 'secret * message' compositions. Anyone who has sent a text message from a mobile phone can see * how it could be used. * * I'd set myself the target of being able to generate individual MIDI messages (not just * play preset files) by the end of this 1-year Java course. So I'm pretty pleased that * I've been able to do it just before the end of the first term! * */ import java.awt.*; import java.awt.event.*; import java.applet.*; import java.util.StringTokenizer; import javax.swing.*; import javax.swing.event.*; import javax.sound.midi.*; import TimbreChoice; import NoteChoice; import ControllerSlider; public class DTMFapplet1 extends Applet implements MouseListener, ItemListener, ChangeListener, ActionListener { CardLayout cardLayout; Box box; Label lblMessage; //*****************************for debug peeking Panel pnlPhonePad, pnlChanSettings; JTextArea jtaIntroMessage; JLabel jlblTitle; TimbreChoice tcRowTimbre, tcColTimbre; ControllerSlider csRowPan, csRowVol, csColPan, csColVol; Label lblLogo, lblRowTimbre, lblColTimbre; NoteChoice[] ncRow, ncCol; JButton jb, jbtnIntro; int CHAN_ROW=0; int CHAN_COL=1; Synthesizer synth; MidiChannel[] chans; MidiChannel chanRow, chanCol; public void init() { try { synth = MidiSystem.getSynthesizer(); synth.open(); chans = synth.getChannels(); chanRow = chans[CHAN_ROW]; chanCol = chans[CHAN_COL]; } catch (MidiUnavailableException e) { setMessage("Sorry, MIDI not found on this system!"); //I'd like to find a way of popping this up, like a javascript alert } lblMessage = new Label(" "); //************************** lblLogo = new Label("DTMFplayer",Label.CENTER); lblLogo.setFont(new Font("Serif",Font.BOLD,15)); lblLogo.setBackground(Color.green); makeIntro(); this.setBackground(Color.white); this.setSize(420,450); this.add(box); makeNoteChoices(); makePhonePadPanel(); makeChanSettingsPanel(); defaults(); // add(lblMessage); //******************************* } private void makeIntro() { box = new Box(BoxLayout.Y_AXIS); jlblTitle = new JLabel("What is DTMFplayer?",JLabel.LEFT); jlblTitle.setFont(new Font("Sans Serif",Font.BOLD,20)); jtaIntroMessage = new JTextArea("\nDTMFplayer is a bit like a telephone keypad, but it needs to find MIDI on your system else it won't work. "+ "Hold down the mouse button on any key in the pad and you will hear its dialing signal.\n\nUnlike a real phone, you can change the notes used for "+ "each row and column of the keypad. You can also choose 2 separate timbres, "+ "stereo pans and volumes for rows and columns.\n\nWith DTMFplayer, you can "+ "turn text-messaging into an interesting music performance experience!", 14,30); jtaIntroMessage.setLineWrap(true); jtaIntroMessage.setWrapStyleWord(true); jtaIntroMessage.setEditable(false); // jtaIntroMessage.setBackground(Color.yellow); jtaIntroMessage.setFont(new Font("Sans Serif",Font.PLAIN,15)); jbtnIntro = new JButton("Try it out now >"); jbtnIntro.addActionListener(this); box.add(jlblTitle); box.add(jtaIntroMessage); box.add(jbtnIntro); } private void makeNoteChoices() { ncRow=new NoteChoice[4]; ncCol=new NoteChoice[3]; for (int i=0;i<3;i++) { ncRow[i]=new NoteChoice(NoteChoice.LAYOUT_VERT); ncCol[i]=new NoteChoice(NoteChoice.LAYOUT_VERT); } ncRow[3]=new NoteChoice(NoteChoice.LAYOUT_VERT); } private void makePhonePadPanel() { pnlPhonePad = new Panel(new GridLayout(5,4,4,4)); pnlPhonePad.add(lblLogo); pnlPhonePad.add(ncCol[0]); pnlPhonePad.add(ncCol[1]); pnlPhonePad.add(ncCol[2]); StringTokenizer stButtonLegends = new StringTokenizer(" ,1,ABC,2,DEF,3,GHI,4,JKL,5,MNO,6,PQRS,7,TUV,8,WXYZ,9, ,*, ,0, ,#",","); for (int i=0;i<4;i++) { pnlPhonePad.add(ncRow[i]); for (int j=0;j<3;j++) { jb = new JButton("
"+stButtonLegends.nextToken()+"
"+stButtonLegends.nextToken()); jb.setBackground(Color.black); jb.setToolTipText("click-hold to play"); jb.setActionCommand(i+""+j); jb.addMouseListener(this); pnlPhonePad.add(jb); } } } private void makeChanSettingsPanel() { tcRowTimbre = new TimbreChoice(CHAN_ROW); tcRowTimbre.addItemListener(this); tcColTimbre = new TimbreChoice(CHAN_COL); tcColTimbre.addItemListener(this); lblRowTimbre =new Label("Row settings: ", Label.RIGHT); lblRowTimbre.setFont(new Font("Sans Serif",Font.BOLD,14)); lblRowTimbre.setBackground(Color.lightGray); lblColTimbre = new Label("Column settings: ", Label.RIGHT); lblColTimbre.setFont(new Font("Sans Serif",Font.BOLD,14)); lblColTimbre.setBackground(Color.lightGray); csRowPan = new ControllerSlider(CHAN_ROW,ControllerSlider.PAN,false); csRowPan.addChangeListener(this); csRowVol = new ControllerSlider(CHAN_ROW,ControllerSlider.VOL,false); csRowVol.addChangeListener(this); csColPan = new ControllerSlider(CHAN_COL,ControllerSlider.PAN,false); csColPan.addChangeListener(this); csColVol = new ControllerSlider(CHAN_COL,ControllerSlider.VOL,false); csColVol.addChangeListener(this); pnlChanSettings=new Panel(new GridLayout(4,2)); pnlChanSettings.add(lblRowTimbre); pnlChanSettings.add(tcRowTimbre); pnlChanSettings.add(csRowPan); pnlChanSettings.add(csRowVol); pnlChanSettings.add(lblColTimbre); pnlChanSettings.add(tcColTimbre); pnlChanSettings.add(csColPan); pnlChanSettings.add(csColVol); } private void setMessage(String message) //************************ { lblMessage.setText(message); lblMessage.validate(); this.validate(); } private void defaults() { //set up as near as poss to the actual dtmf phone settings //notes ncCol[0].setNoteNum(86); ncCol[1].setNoteNum(88); ncCol[2].setNoteNum(90); ncRow[0].setNoteNum(74); ncRow[1].setNoteNum(76); ncRow[2].setNoteNum(78); ncRow[3].setNoteNum(80); //timbres tcRowTimbre.select(80); progChange(chanRow,80); tcColTimbre.select(80); progChange(chanCol,80); //pan csRowPan.setValue(32); ctrlChange(chanRow,ControllerSlider.PAN,32); csColPan.setValue(96); ctrlChange(chanCol,ControllerSlider.PAN,96); //setVolumes to 100 (per chan) ctrlChange(chanRow,ControllerSlider.VOL,100); ctrlChange(chanCol,ControllerSlider.VOL,100); //reset all controllers (per chan) ctrlChange(chanRow,121,0); ctrlChange(chanCol,121,0); } //event listeners//////////////////////////////////////////////////////////// //MOUSE-------------------------------------- public void mousePressed(MouseEvent me) { JButton pressed = (JButton)me.getSource(); String ac = pressed.getActionCommand(); int row = ac.charAt(0) - '0'; int col = ac.charAt(1) - '0'; // setMessage(row+" "+col+" on"); noteOn(chanRow,ncRow[row].getNoteNum(),96); noteOn(chanCol,ncCol[col].getNoteNum(),96); } public void mouseReleased(MouseEvent me) { JButton pressed = (JButton)me.getSource(); pressed.setFocusPainted(false); String ac = pressed.getActionCommand(); int row = ac.charAt(0) - '0'; int col = ac.charAt(1) - '0'; // setMessage(row+" "+col+" off"); noteOn(chanRow,ncRow[row].getNoteNum(),0); noteOn(chanCol,ncCol[col].getNoteNum(),0); } public void mouseClicked(MouseEvent me) { //do nothing (but this method needs to be here to implement MouseListener) } public void mouseEntered(MouseEvent me) { //do nothing (but this method needs to be here to implement MouseListener) } public void mouseExited(MouseEvent me) { //do nothing (but this method needs to be here to implement MouseListener) } //CHOICE------------------------------------------- public void itemStateChanged(ItemEvent ie) { TimbreChoice changed = (TimbreChoice)ie.getSource(); // setMessage("timbre "+changed.getSelectedIndex()+" on chan "+changed.getChan()); progChange(chans[changed.getChan()], changed.getSelectedIndex()); } //SLIDERS------------------------------------------- public void stateChanged(ChangeEvent ce) { ControllerSlider changed = (ControllerSlider)ce.getSource(); ctrlChange(chans[changed.getChan()], changed.getControllerNum(), changed.getValue()); } //BUTTON--------------------------------------------- public void actionPerformed(ActionEvent ae) { this.setVisible(false); this.removeAll(); this.add(pnlPhonePad); this.add(pnlChanSettings); this.setBackground(Color.lightGray); this.validate(); this.setVisible(true); } //MIDI transmisson ///////////////////////////////////////////////////////////// public void noteOn(MidiChannel chan, int pitch, int vel) { chan.noteOn(pitch, vel); } public void noteOff(MidiChannel chan, int pitch) { chan.noteOn(pitch, 0); } public void progChange(MidiChannel chan, int progNo) { chan.programChange(progNo); } public void ctrlChange(MidiChannel chan, int type, int value) { chan.controlChange(type,value); } }