Επιβλέπων Καθηγητής : κ. Ακουµιανάκης ∆ηµοσθένης Γρηγορακάκη Αιµιλία Α.Μ. 1292
by user
Comments
Transcript
Επιβλέπων Καθηγητής : κ. Ακουµιανάκης ∆ηµοσθένης Γρηγορακάκη Αιµιλία Α.Μ. 1292
ΤΕΧΝΟΛΟΓΙΚΟ ΕΚΠΑΙ∆ΕΥΤΙΚΟ Ι∆ΡΥΜΑ ΚΡΗΤΗΣ ΣΧΟΛΗ ΤΕΧΝΟΛΟΓΙΚΩΝ ΕΦΑΡΜΟΓΩΝ ΤΜΗΜΑ ΕΦΑΡΜΟΣΜΕΝΗΣ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΠΟΛΥΜΕΣΩΝ Θέµα : « Εικονική Αναπαράσταση Μουσικών Κοινοτήτων Στο ∆ιαδίκτυο » Επιβλέπων Καθηγητής : κ. Ακουµιανάκης ∆ηµοσθένης Επιµέλεια : Ευθυµίου Μιχαλίτσα Α.Μ. 1220 Γρηγορακάκη Αιµιλία Α.Μ. 1292 Κρήτη, Ιανουάριος 2009 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Περιεχόμενα 1. Εκφώνηση ...........................................................................................................................7 2. Παραπλήσιες εργασίες........................................................................................................8 2.1 ABC4J...........................................................................................................................9 2.2 Impro-Visor.................................................................................................................13 2.3 JFugue .........................................................................................................................14 2.4 JMusic .........................................................................................................................16 2.5 JFrets ...........................................................................................................................17 2.6 Οδηγίες λήψης κώδικα εφαρµογών ............................................................................19 2.6.1 Λήψη κώδικα JFugue Music Notepad από NetBeans ........................................19 2.6.2 Λήψη κώδικα JFrets από NetBeans .....................................................................20 3. Μουσικοί κανόνες.............................................................................................................22 4. Προδιαγραφές πτυχιακής εργασίας ..................................................................................41 5. Ανάλυση του κώδικα score-editor ....................................................................................43 5.1 Αρχείο: Main.java.......................................................................................................46 5.2 Αρχεία: DPianoStave,Dstave,DGrandStave,DTrebleStave ........................................47 5.3 Αρχεία: DNoteEditor και DStaveActionHandler .......................................................51 5.4 Αρχεία: Dot1.java, Main.form, ScoreEditor.form ......................................................53 5.5 Αρχείο: ScoreEditor.java ............................................................................................55 6. Υλοποίηση ........................................................................................................................56 6.1 Μετρονόµος ................................................................................................................56 6.2 Βελτίωση αρχείου: ScoreEditor.java ..........................................................................58 6.3 Βελτίωση αρχείου: Main.java.....................................................................................63 6.4 Αρχείο: Constants.java................................................................................................68 6.5 Αρχείο: GuitarNeck.java.............................................................................................70 6.6 Αρχείο: DStave.java....................................................................................................79 6.7 ∆ιάδραση ανάµεσα στην παρτιτούρα και την εικονική κιθάρα..................................83 6.8 Ανοιχτές χορδές ..........................................................................................................88 6.9 ∆ηµιουργία Ant Script ................................................................................................96 6.10 Μετατροπή εφαρµογής σε Applet ..........................................................................102 6.11 ∆ηµιουργία Ιστοσελίδας .........................................................................................111 7. Σύνοψη και Συµπεράσµατα ............................................................................................120 8. Βιβλιογραφία ..................................................................................................................121 Παράρτηµα 1.......................................................................................................................124 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 2 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήματα Σχήµα 2-1: Παρτιτούρα µε βάση το ABC4J.......................................................................... 9 Σχήµα 2-2: Απεικόνιση παρτιτούρας ABC4J µε νότες και µουσικό κλειδί. ....................... 10 Σχήµα 2-3: Απεικόνιση παρτιτούρας ABC4J µετά την προσθήκη µπαρών. ....................... 10 Σχήµα 2-4: Απεικόνιση παρτιτούρας ABC4J µετά την προσθήκη του µέτρου................... 11 Σχήµα 2-5: Απεικόνιση παρτιτούρας ABC4J µετά την εισαγωγή tie και slurs................... 11 Σχήµα 2-6: Παρτιτούρα ABC4J χωρίς στοίχιση. ................................................................ 12 Σχήµα 2-7: Παρτιτούρα ABC4J µε πλήρη στοίχιση............................................................ 12 Σχήµα 2-8: ∆ιεπαφή της εφαρµογής Impro-Visor............................................................... 13 Σχήµα 2-9: ∆ιεπαφή της εφαρµογής JFrets. ........................................................................ 17 Σχήµα 2-10: Ο µετρονόµος του JFrets................................................................................. 18 Σχήµα 2-11: Παράθυρο Netbeans για το checkout του JFugue Music Notepad. ................ 20 Σχήµα 2-12: Παράθυρο Netbeans για το checkout του JFrets............................................. 21 Σχήµα 3-1: Μουσικό πεντάγραµµο...................................................................................... 22 Σχήµα 3-2: ∆ιαστήµατα µουσικού πενταγράµµου. ............................................................. 22 Σχήµα 3-3: Κλειδιά του ΣΟΛ. ............................................................................................. 23 Σχήµα 3-4: Κλειδιά του ΦΑ................................................................................................. 23 Σχήµα 3-5: Κλειδιά του ΝΤΟ. ............................................................................................. 23 Σχήµα 3-6: Κλειδί του ΣΟΛ της 2ης γραµµής...................................................................... 24 Σχήµα 3-7: Οι φθόγγοι του πενταγράµµου. ......................................................................... 24 Σχήµα 3-8: Οκτάβα ή ογδόη. ............................................................................................... 24 Σχήµα 3-9: Κλίµακα (σκάλα του ΝΤΟ)............................................................................... 25 Σχήµα 3-10: Τόνοι και ηµιτόνια. ......................................................................................... 25 Σχήµα 3-11: Σχήµατα φθογγοσήµων. .................................................................................. 26 Σχήµα 3-12: Ολόκληρο........................................................................................................ 27 Σχήµα 3-13: ∆ιαίρεση ολόκληρου σε 2 µισά....................................................................... 27 Σχήµα 3-14: ∆ιαίρεση ολόκληρου σε 4 τέταρτα. ................................................................ 28 Σχήµα 3-15: ∆ιαίρεση ολόκληρου σε 8 όγδοα. ................................................................... 28 Σχήµα 3-16: Ισοδυναµίες ολόκληρου. ................................................................................. 29 Σχήµα 3-17: Ισοδυναµίες µισού........................................................................................... 30 Σχήµα 3-18: Ισοδυναµίες τετάρτου. .................................................................................... 30 Σχήµα 3-19: Ισοδυναµίες ογδόου. ....................................................................................... 31 Σχήµα 3-20: Ισοδυναµίες δεκάτου έκτου. ........................................................................... 31 Σχήµα 3-21: Ισοδυναµία τριακοστού δευτέρου................................................................... 32 Σχήµα 3-22: Ισοδυναµία αξίας κάθε φθογγοσήµου............................................................. 32 Σχήµα 3-23: Γραφή φθογγοσήµων στο πεντάγραµµο. ........................................................ 33 Σχήµα 3-24: Μουσικό µέτρο στο πεντάγραµµο. ................................................................. 33 Σχήµα 3-25: Μουσική σύνθεση σε µέτρο 2:4...................................................................... 34 Σχήµα 3-26: Ισοδυναµία ολόκληρου παρεστιγµένου. ......................................................... 35 Σχήµα 3-27: Ισοδυναµίες παρεστιγµένων φθογγοσήµων.................................................... 35 Σχήµα 3-28: Πίνακας σχηµάτων φθογγοσήµων και παύσεων............................................. 36 Σχήµα 3-29: Παρεστιγµένες και δις παρεστιγµένες παύσεις............................................... 36 Σχήµα 3-30: Απεικόνιση φθογγοσήµου ΝΤΟ δίεση στο πεντάγραµµο. ............................. 37 Σχήµα 3-31: Αντιστοιχία φθογγοσήµων στην ταστιέρα της κιθάρας.................................. 38 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 3 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 4-1: ∆ιεπαφή αρχικού score editor. .......................................................................... 41 Σχήµα 5-1: ∆οµή αρχείων του score editor. ........................................................................ 43 Σχήµα 5-2: Προσθήκη βιβλιοθήκης JMusic στον score editor............................................ 44 Σχήµα 5-3: Exception του score editor. ............................................................................... 45 Σχήµα 5-4: Uml διάγραµµα κλάσεων των staves του score editor...................................... 47 Σχήµα 5-5: Attributes του DStave.java................................................................................ 49 Σχήµα 5-6: Απεικόνιση του JComboBox του score editor.................................................. 50 Σχήµα 5-7: Παράθυρο επεξεργασίας των ............................................................................ 51 Σχήµα 5-8: Popup menu του score editor. ........................................................................... 52 Σχήµα 5-9: Γραφικό περιβάλλον κλάσης View................................................................... 54 Σχήµα 5-10: Παράθυρο επιλογής µουσικού οργάνου. ........................................................ 55 Σχήµα 5-11: Απεικόνιση κουµπιών του score editor........................................................... 55 Σχήµα 5-12: Κώδικας εισαγωγής τυχαίων νοτών στην παρτιτούρα.................................... 55 Σχήµα 6-1: Package structure µετρονόµου. ......................................................................... 56 Σχήµα 6-2: Παράθυρο µετρονόµου. .................................................................................... 57 Σχήµα 6-3: Μενού µε κουµπιά του score editor. ................................................................. 58 Σχήµα 6-4: Exception διαγραφής τελευταίας νότας σε άδεια παρτιτούρα. ......................... 58 Σχήµα 6-5: Look & Feel εφαρµογής.................................................................................... 63 Σχήµα 6-6: Μενού File. ....................................................................................................... 64 Σχήµα 6-7: Μενού Tools...................................................................................................... 64 Σχήµα 6-8: Μενού Help....................................................................................................... 64 Σχήµα 6-9: Μήνυµα στο ‘About’......................................................................................... 64 Σχήµα 6-10: Παράθυρο επιλογής αρχείου για αποθήκευση. ............................................... 67 Σχήµα 6-11: Οι εικόνες του Constants.java......................................................................... 68 Σχήµα 6-12: Εικονική κιθάρα JFrets. .................................................................................. 70 Σχήµα 6-13: Απεικόνιση τωνµεγεθών των κουµπιών που υλοποιήθηκαν. ......................... 70 Σχήµα 6-14: Πρώτο prototype της εικονικής κιθάρας........................................................ 70 Σχήµα 6-15: ∆εύτερο prototype της εικονικής κιθάρας. ..................................................... 71 Σχήµα 6-16: Τρίτο prototype της εικονικής κιθάρας........................................................... 71 Σχήµα 6-17: Απεικόνιση οβάλ σχήµατος στο prototype της εικονικής κιθάρας................. 72 Σχήµα 6-18: Wood texture ταστίερας της κιθάρας.............................................................. 72 Σχήµα 6-19: Εικονική αναπαράσταση µπράτσου κιθάρας. ................................................. 72 Σχήµα 6-20: Πάνελ κουµπιών εικονικής κιθάρας. .............................................................. 73 Σχήµα 6-21: Πάνελ εικονικής κιθάρας. ............................................................................... 73 Σχήµα 6-22: Νότες στην ταστιέρα της κιθάρας................................................................... 73 Σχήµα 6-23: ∆ιεπαφή εικονικής κιθάρας............................................................................. 74 Σχήµα 6-24: Μουσικά σύµβολα που έπρεπε να επαναδηµιουργηθούν. .............................. 79 Σχήµα 6-25: Απεικόνιση επιλεγµένης νότας (αλλαγή χρώµατος)....................................... 82 Σχήµα 6-26: Αντιστοιχία νότας E της 1η γραµµής στην ταστιέρα της κιθάρας. ................. 86 Σχήµα 6-27: Αντιστοιχία νότας E της 1ης γραµµής στην εφαρµογή.................................... 86 Σχήµα 6-28: Το RGB της πρώτης χορδής. .......................................................................... 88 Σχήµα 6-29: Το RGB της δεύτερης χορδής......................................................................... 88 Σχήµα 6-30: Το RGB της τρίτης χορδής. ............................................................................ 89 Σχήµα 6-31: Το RGB της τέταρτης χορδής. ........................................................................ 89 Σχήµα 6-32: Το RGB της πέµπτης χορδής. ......................................................................... 89 Σχήµα 6-33: Το RGB της έκτης χορδής. ............................................................................. 90 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 4 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-34: Απεικόνιση 1ης χορδής ανοιχτής. .................................................................... 90 Σχήµα 6-35: Απεικόνιση 2ης χορδής ανοιχτής. .................................................................... 90 Σχήµα 6-36: Απεικόνιση 3ης χορδής ανοιχτής. .................................................................... 91 Σχήµα 6-37: Απεικόνιση 4ης χορδής ανοιχτής. .................................................................... 91 Σχήµα 6-38: Απεικόνιση 5ης χορδής ανοιχτής. .................................................................... 91 Σχήµα 6-39: Απεικόνιση 6ης χορδής ανοιχτής. .................................................................... 91 Σχήµα 6-40: Αντιστοιχία νότας Ε 4ου διαστήµατος στην εφαρµογή.................................. 93 Σχήµα 6-41: Αντιστοιχία νότας D στην εφαρµογή.............................................................. 94 Σχήµα 6-42: Αντιστοιχία νότας C - sharp στην εφαρµογή. ................................................. 95 Σχήµα 6-43: Λίστα µε tasks που υποστηρίζει το script. .................................................... 100 Σχήµα 6-44: Απεικόνιση εκτέλεσης εντολής ‘ant clean’................................................... 100 Σχήµα 6-45: Απεικόνιση εκτέλεσης εντολής ‘ant jar’. ...................................................... 101 Σχήµα 6-46: Μήνυµα λάθους της Java Console. ............................................................... 103 Σχήµα 6-47: Exception: diamouses.ui.scoreEditor.Main cannot be cast to java.applet.Applet. .......................................................................................................... 104 Σχήµα 6-48: Στοιχεία που ακολουθούν το κλειδί ασφαλείας. ........................................... 105 Σχήµα 6-49: Αρχείο .keystore............................................................................................ 106 Σχήµα 6-50: Αρχείο scoreeditor.cer................................................................................... 106 Σχήµα 6-51: Μήνυµα στο χρήστη που αφορά τα δικαιώµατα του Applet. ....................... 108 Σχήµα 6-52: Παράθυρο ‘More Information’. .................................................................... 109 Σχήµα 6-53: Στοιχεία που συνοδεύουν το πιστοποιητικό ασφαλείας. .............................. 109 Σχήµα 6-54: Απεικόνιση Save Dialog για αποθήκευση παρτιτούρας. .............................. 110 Σχήµα 6-55: Περιβάλλον διαχείρισης Joomla. .................................................................. 111 Σχήµα 6-56: Template ιστοσελίδας. .................................................................................. 112 Σχήµα 6-57: Κεντρικό µενού της σελίδας. ........................................................................ 113 Σχήµα 6-58: Κεντρική σελίδα............................................................................................ 114 Σχήµα 6-59: Σελίδα ‘Είσοδος στην τάξη’.......................................................................... 115 Σχήµα 6-60: Πίνακας ελέγχου διαχειριστή φόρουµ. ......................................................... 116 Σχήµα 6-61: Σελίδα ‘Φόρουµ’. .......................................................................................... 117 Σχήµα 6-62: Φόρµα εγγραφής ........................................................................................... 117 Σχήµα 6-63: Σελίδα ‘Βοήθεια’. ......................................................................................... 119 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 5 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Πίνακες Πίνακας 2-1: Παραπλήσια πακέτα λογισµικού στη µουσική τεχνολογία. ............................ 8 Πίνακας 2-2: Λίστα µουσικών οργάνων JFugue. ................................................................ 15 Πίνακας 3-1: ∆ιεθνής ονοµατολογία ακκόρντων. ............................................................... 39 Πίνακας 3-2: Είδη ακκόρντων. ............................................................................................ 39 Πίνακας 3-3: Σηµειογραφίες ακκόρντων............................................................................. 40 Πίνακας 6-1: Λίστα των tasks που υλοποιούνται στο αρχείο build.xml. ............................ 97 Πίνακας 6-2: ∆ικαιώµατα πρόσβασης στις ∆ηµόσιες Συζητήσεις. ................................... 118 Πίνακας 6-3: Επίπεδο πρόσβασης χρηστών στις σελίδες.................................................. 119 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 6 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 1. Εκφώνηση Η πτυχιακή αυτή εργασία έχει σκοπό τη δηµιουργία µιας εφαρµογής σε Java όπου ο χρήστης θα µπορεί να συνθέτει µουσική και να την αναπαράγει. Η εφαρµογή χωρίζεται σε δυο µέρη. Το πρώτο κοµµάτι αφορά την σύνθεση µουσικής σε παρτιτούρα, ώστε ο χρήστης να εξοικιωθεί µε τους βασικούς κανόνες της µουσικής. Η εφαρµογή θα δίνει τη δυνατότητα στο χρήστη να εισάγει νότες,παύσεις, καθώς και να καθορίζει το µουσικό όργανο µε το οποίο επιθυµεί να συνθέσει. Επίσης θα µπορεί να αναπαράγει τη µουσική που σύνθεσε. Το δεύτερο κοµµάτι αφόρα την οπτική αναπαράσταση του µουσικού οργάνου και την οπτική απεικόνιση των νοτών σε αυτό. Ο χρήστης θα έχει την δυνατότητα να επιλέγει µια νότα από την παρτιτούρα και να βλέπει τη θέση που έχει η συγκεκριµένη νότα στο µουσικό όργανο. Αυτό έχει ως σκοπό την εύκολη κατανόηση και εκµάθηση της θεωρίας της µουσικής και την αντιστοιχία µε το µουσικό όργανο. Η πτυχιακή αυτή εργασία έχει υλοποιηθεί για µουσικά όργανα που ανήκουν στην κατηγορία "κιθάρα". Η διαδικασία ολοκλήρωσης της εργασίας αποτελείται από τα ακόλουθα βήµατα: α) Αναζήτηση εργαλείων ανοιχτού κώδικα και µελέτη των δυνατοτήτων τους, β) Μελέτη της υπάρχουσας εφαρµογής (score editor), γ) Σχεδιασµός της νέας εφαρµογής δ) Ανάπτυξη και υλοποίηση της εφαρµογής της πτυχιακής εργασίας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 7 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2. Παραπλήσιες εργασίες Μετά από σχετική έρευνα και µελέτη αρκετών project στον παγκόσµιο ιστό, τα περισσότερα εκ των οποίων ήταν τύπου ανοιχτού λογισµικού, θα παρουσιαστούν αυτά τα οποία είναι πιο κοντά µε την πτυχιακή εργασία και µπορούν να µας βοηθήσουν για να παραχθούν ιδέες οι οποίες θα αντιγραφούν και θα υλοποιηθούν στα πλαίσια της εργασίας αυτής. Σε γενικότερες γραµµές τα projects αυτά µπορούν να τοποθετηθούν σε δύο (2) κύριες κατηγορίες. Η πρώτη κατηγορία αφορά βιβλιοθήκες σε Java που παρέχουν µηχανισµούς για αναπαραγωγή ή σύνθεση µουσικής. Η κατηγορία αυτή θα ονοµάζεται για τη συνέχεια της πτυχιακής ‘µουσικές βιβλιοθήκες’. H δεύτερη κατηγορία αφορά ολοκληρωµένες εφαρµογές που έχουν γραφτεί από µουσικούς ή για µουσικούς για να βοηθήσουν τους χρήστες τους στην εξάσκηση των µουσικών τους ικανοτήτων. Από την πρώτη κατηγορία θα επιλεχθεί η κατάλληλη µουσική βιβλιοθήκη ώστε να χρησιµοποιηθεί κατά την υλοποίηση της πτυχιακής αυτής εργασίας. Από τη δεύτερη κατηγορία θα παρουσιαστούν οι πιο ευφυής ιδέες και οι πιο λειτουργικές υλοποιήσεις διεπαφών των εφαρµογών µε τον χρήστη. Τα πακέτα λογισµικού που πληρούν τις παραπάνω προϋποθέσεις, και επιλέχθηκαν µέσα από λίστες βιβλιοθηκών και πακέτων λογισµικού [13] παρουσιάζονται στον Πίνακα 2-1. Όνοµα ABC4J [2] Impro-Visor [6] JFugue [7] Java MIDI Kit [8] jMusic [9] jFrets [10] ∆υνατότητες Κάνει parse παίζει και προβάλει παρτιτούρες κάνοντας χρήση της ABC notation [3]. Εφαρµογή για συνθέτες µουσικής jazz. Βιβλιοθήκη για αναπαράσταση µουσικής µε χαρακτήρες, και αναπαραγωγής ήχων. Βιβλιοθήκη για εφαρµογές MIDI Βιβλιοθήκη Εφαρµογή για εκµάθηση κιθάρας Licence GNU GPL v2 GPL Open Source GNU GPL GNU GPL GNU GPL v2 Πίνακας 2-1: Παραπλήσια πακέτα λογισµικού στη µουσική τεχνολογία. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 8 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.1 ABC4J Από το ABC4J µπορούµε να λάβουµε ενδιαφέροντα διδάγµατα µιας και το ABC notation έχει απλοποιήσει τα πράγµατα πάρα πολύ. Στο Σχήµα 2-1 παρουσιάζεται παρτιτούρα µε βάση το ABC4J. Σχήµα 2-1: Παρτιτούρα µε βάση το ABC4J. Ας δούµε τα βήµατα που απαιτούνται για την παραγωγή της παραπάνω παρτιτούρας. [4] Το πρώτο βήµα είναι η δηµιουργία ενός αρχείου που θα περιέχει το κλειδί και τις νότες. X:0 T:A simple scale exercise K:D CDEFGABcdefggfedcBAGFEDC Με τη χρήση του παρακάτω κώδικα µπορούµε να εµφανίσουµε την πρώτη µας παρτιτούρα. import javax.swing.JFrame; import abc.notation.Tune; import abc.parser.TuneParser; import abc.ui.swing.JScoreComponent; public static void main (String[] arg) { String tuneAsString = "X:0\nT:A simple scale exercise\nK:D\nCDEFGABcdefggfedcBAGFEDC\n"; Tune tune = new TuneParser().parse(tuneAsString); JScoreComponent scoreUI =new JScoreComponent(); scoreUI.setTune(tune); JFrame j = new JFrame(); j.add(scoreUI); j.pack(); j.setVisible(true); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 9 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Στο Σχήµα 2-2 φαίνεται η παρτιτούρα που θα προκύψει αν τρέξουµε τον παραπάνω κώδικα. Σχήµα 2-2: Απεικόνιση παρτιτούρας ABC4J µε νότες και µουσικό κλειδί. Είναι εύκολο να παρατηρηθεί ότι το abc notation δεν ξεχωρίζει τις νότες από µόνο του. Για να βελτιώσουµε την παραπάνω παρτιτούρα πρέπει για αρχή να τοποθετηθούν µπάρες | X:0 T:A simple scale exercise K:D CDEF|GABc|defg|gfed|cBAG|FEDC Στο Σχήµα 2-3 φαίνεται η παρτιτούρα µετά την χρήση των µπαρών. Σχήµα 2-3: Απεικόνιση παρτιτούρας ABC4J µετά την προσθήκη µπαρών. Το επόµενο βήµα είναι να χρησιµοποιήσουµε το time signature M:4:4 και να οµαδοποιήσουµε τις νότες ανά δύο. Το τελευταίο το επιτυγχάνουµε αφήνοντας κενά ανάµεσα στις νότες. Στο Σχήµα 2-4 απεικονίζεται η παρτιτούρα µετά την προσθήκη του µέτρου και την οµαδοποίηση. X:0 T:A simple scale exercise M:4/4 K:D CD EF|GA Bc|de fg|gf ed|cB AG|FE DC Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 10 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 2-4: Απεικόνιση παρτιτούρας ABC4J µετά την προσθήκη του µέτρου. Το µόνο που µένει για να επιτευχθεί το επιθυµητό αποτέλεσµα είναι η εισαγωγή tie και slurs. Αυτό επιτυγχάνετε µε χρήση κατάλληλων παρενθέσεων που εξηγούν από ποια νότα να ανοίγει ένα slur και σε ποια να κλείνει. Να σηµειωθεί ότι για γειτονικές νότες η χρήση µιας απλής παύλας αρκεί. Έτσι χρησιµοποιώντας το παρακάτω abc notation πετυχαίνουµε το οπτικό αποτέλεσµα που φαίνεται στο Σχήµα 2-5 και επιθυµούσαµε εξ αρχής. X:0 T:A simple scale exercise M:4/4 K:D (CD EF|G)A Bc|de fg-|gf ed|cB A(G|FE DC) Σχήµα 2-5: Απεικόνιση παρτιτούρας ABC4J µετά την εισαγωγή tie και slurs. Η βιβλιοθήκη abc4j παρέχει πολλές περισσότερες δυνατότητες από αυτές που ειπώθηκαν µέχρι τώρα. Πρώτον µπορεί να δηµιουργήσει παρτιτούρες µε χρήση Java αντικειµένων π.χ.: Note note_d = new Note(Note.D); Η παραπάνω δυνατότητα δεν θεωρείται κάτι το αξιοσηµείωτο και δε θα γίνει επέκταση της τεχνοτροπίας στην πτυχιακή εργασία. Μια πολύ ενδιαφέρον δυνατότητα της abc4j είναι η διαχείριση παρτιτούρων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 11 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 2-6: Παρτιτούρα ABC4J χωρίς στοίχιση. Η παρτιτούρα που παρουσιάζεται στο Σχήµα 2-6 θα µπορούσε να θεωρηθεί άψογη (οπτικά) από πολλές απόψεις. Όµως υπάρχει χώρος για βελτίωση της παρουσίασής της. Και συγκεκριµένα όσον αφορά στο justification. ∆ηλαδή στο να ξεκινάει η κάθε γραµµή της παρτιτούρας και να τελειώνει στο ίδιο σηµείο. Αυτό που στα ελληνικά ονοµάζουµε ‘Πλήρη στοίχιση’. Αυτό µπορεί να επιτευχθεί µε τη βιβλιοθήκη abc4j µε τον παρακάτω κώδικα: JScoreComponent jscore = new JScoreComponent(); jscore.setJustification(true); jscore.setTune(tune); Μετά την προσθήκη του παραπάνω κώδικα έχουµε το οπτικό αποτέλεσµα στο Σχήµα 2-7 που είναι πραγµατικά άψογο. Σχήµα 2-7: Παρτιτούρα ABC4J µε πλήρη στοίχιση. Τέλος αξίζει να αναφερθεί ότι η abc4j διαθέτει δυνατότητες για αναπαραγωγή των παρτιτούρων αλλά και για µετατροπή τους σε αρχεία MIDI [5]. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 12 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.2 Impro-Visor Η εφαρµογή Impro-Visor (βλέπε Σχήµα 2-8) είναι µια αξιέπαινη προσπάθεια που συντελείται εδώ και µερικά χρόνια στο κολλέγιο Harvey Mudd από καθηγητή και οµάδα φοιτητών που έχει καταπλήξει τον κόσµο της µουσικής τεχνολογίας και της Java. Η οµάδα αυτή έχει δηµιουργήσει λογισµικό ανοιχτού κώδικα σχεδιασµένο για να βοηθάει µουσικούς jazz να συνθέτουν και να ακούν solo. Στηρίζεται στη θεωρία ότι τα ρυθµικά κοµµάτια (π.χ. πιάνο, µπάσο, drums) που συνοδεύουν τη µουσική jazz µπορούν να δηµιουργηθούν αυτόµατα από chords. Σχήµα 2-8: ∆ιεπαφή της εφαρµογής Impro-Visor. Το πλήθος των δυνατοτήτων του προγράµµατος αυτό είναι τεράστιο και πιθανώς καλύπτει τις ανάγκες και του πιο απαιτητικού συνθέτη. ∆ιαθέτει δυνατότητες για αρµονίες για διαφοροποιηµένα pitches, για loops, drag and drop functionality για µεταφορά στοιχείων, αλλαγή κλειδιού και πολλά άλλα. Ένα από τα πιο ενδιαφέροντα tricks του λογισµικού αυτού είναι η χρήση slots. Με τρόπο παρόµοιο µε το Macromedia Flash, η παρτιτούρα αποτελείται από έναν αριθµό από άδεια slots µέσα στα οποία ο χρήστης µπορεί να τοποθετήσει µουσικά σύµβολα (νότες, παύσεις κτλ). Τέλος τονίζεται ότι χρησιµοποιεί τη βιβλιοθήκη JMusic που θα αναφερθεί στη συνέχεια. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 13 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.3 JFugue Το JFugue είναι ένα API (Application Programming Interface) που µπορεί να χρησιµοποιηθεί για πολλαπλές χρήσεις: εµφάνιση παρτιτούρας, αναπαραγωγή και ηχογράφηση σε MIDI, χρήση πολλαπλών οργάνων και χρησιµοποιεί δική του υλοποίηση από MusicStrings. Γενικά, αποτελεί µια ολοκληρωµένη πλατφόρµα που µπορεί να δώσει λύσεις ακόµα και στις πιο απαιτητικές εργασίες. Το βάθος στο οποίο έχει µελετηθεί η µουσική τεχνολογία και λογική είναι αξιοσηµείωτο, καθώς παρέχει δυνατότητες για ταυτόχρονη χρήση πολλαπλών οργάνων. Χρησιµοποιεί δική της υλοποίηση από MusicStrings, ένα notation παρόµοιο µε το ABC notation. Το ξεπερνάει όµως από πολλές απόψεις. Άλλωστε το abc notation δηµιουργήθηκε το 1991 µε σκοπό τη διάδοση µελωδιών και ρυθµών που «έγραψαν ιστορία» µε όσο το δυνατόν πιο απλό τρόπο. Ναι µεν στηρίζεται το abc σε κανονικοποιηµένες οντότητες που γίνονται parsed και validate από BNF συντακτικούς κανόνες [11], αλλά δεν υποστηρίζει πολλαπλά voices, καθώς και ορισµό µουσικών οργάνων. Βέβαια έχουν δηµιουργηθεί extensions του ABC notation που υλοποιούν τις παραπάνω δυνατότητες. Συµπερασµατικά το notation του JFugue είναι σαφώς ανώτερο και καλύπτει τεράστια γκάµα δυνατοτήτων. Στον Πίνακα 2-2 παρουσιάζεται η χαρακτηριστική λίστα από µουσικά όργανα που υποστηρίζει. Piano 0 PIANO or ACOUSTIC_GRAND 1 BRIGHT_ACOUSTIC 2 ELECTRIC_GRAND 3 HONKEY_TONK 4 ELECTRIC_PIANO or ELECTRIC_PIANO1 5 ELECTRIC_PIANO2 6 HARPISCHORD 7 CLAVINET Guitar 24 GUITAR or NYLON_STRING_GUITAR 25 STEEL_STRING_GUITAR 26 ELECTRIC_JAZZ_GUITAR 27 ELECTRIC_CLEAN_GUITAR 28 ELECTRIC_MUTED_GUITAR 29 OVERDRIVEN_GUITAR 30 DISTORTION_GUITAR 31 GUITAR_HARMONICS Chromatic Percussion 8 CELESTA 9 GLOCKENSPIEL 10 MUSIC_BOX 11 VIBRAPHONE 12 MARIMBA 13 XYLOPHONE 14 TUBULAR_BELLS 15 DULCIMER Organ 16 DRAWBAR_ORGAN 17 PERCUSSIVE_ORGAN 18 ROCK_ORGAN 19 CHURCH_ORGAN 20 REED_ORGAN 21 ACCORIDAN 22 HARMONICA 23 TANGO_ACCORDIAN Bass 32 ACOUSTIC_BASS 33 ELECTRIC_BASS_FINGER 34 ELECTRIC_BASS_PICK 35 FRETLESS_BASS 36 SLAP_BASS_1 37 SLAP_BASS_2 38 SYNTH_BASS_1 39 SYNTH_BASS_2 Strings 40 VIOLIN 41 VIOLA 42 CELLO 43 CONTRABASS 44 TREMOLO_STRINGS 45 PIZZICATO_STRINGS 46 ORCHESTRAL_STRINGS 47 TIMPANI Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 14 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Ensemble 48 STRING_ENSEMBLE_1 49 STRING_ENSEMBLE_2 50 SYNTH_STRINGS_1 51 SYNTH_STRINGS_2 52 CHOIR_AAHS 53 VOICE_OOHS 54 SYNTH_VOICE 55 ORCHESTRA_HIT Brass 56 TRUMPET 57 TROMBONE 58 TUBA 59 MUTED_TRUMPET 60 FRENCH_HORN 61 BRASS_SECTION 62 SYNTHBRASS_1 63 SYNTHBRASS_2 Reed 64 SOPRANO_SAX 65 ALTO_SAX 66 TENOR_SAX 67 BARITONE_SAX 68 OBOE 69 ENGLISH_HORN 70 BASSOON 71 CLARINET Pipe 72 PICCOLO 73 FLUTE 74 RECORDER 75 PAN_FLUTE 76 BLOWN_BOTTLE 77 SKAKUHACHI 78 WHISTLE 79 OCARINA Synth Lead 80 LEAD_SQUARE or SQUARE 81 LEAD_SAWTOOTH or SAWTOOTH 82 LEAD_CALLIOPE or CALLIOPE 83 LEAD_CHIFF or CHIFF 84 LEAD_CHARANG or CHARANG 85 LEAD_VOICE or VOICE 86 LEAD_FIFTHS or FIFTHS 87 LEAD_BASSLEAD or BASSLEAD Ethnic 104 SITAR 105 BANJO 106 SHAMISEN 107 KOTO 108 KALIMBA 109 BAGPIPE 110 FIDDLE 111 SHANAI Synth Pad 88 PAD_NEW_AGE or NEW_AGE 89 PAD_WARM or WARM 90 PAD_POLYSYNTH or POLYSYNTH 91 PAD_CHOIR or CHOIR 92 PAD_BOWED or BOWED 93 PAD_METALLIC or METALLIC 94 PAD_HALO or HALO 95 PAD_SWEEP or SWEEP Synth Effects 96 FX_RAIN OR RAIN 97 FX_SOUNDTRACK or SOUNDTRACK 98 FX_CRYSTAL or CRYSTAL 99 FX_ATMOSPHERE or ATMOSPHERE 100 FX_BRIGHTNESS or BRIGHTNESS 101 FX_GOBLINS or GOBLINS 102 FX_ECHOES Percussive 112 TINKLE_BELL 113 AGOGO 114 STEEL_DRUMS 115 WOODBLOCK 116 TAIKO_DRUM 117 MELODIC_TOM 118 SYNTH_DRUM 119 REVERSE_CYMBAL Πίνακας 2-2: Λίστα µουσικών οργάνων JFugue. Η παραπάνω λίστα οργάνων έχει βρεθεί στο βιβλίο «The complete Guide to JFugue» [12]. Επίσης να τονιστεί ότι για κάθε µουσικό όργανο υπάρχει κατάλληλο notation για κάθε δυνατότητα του οργάνου. Για παράδειγµα στα drums µπορεί ο χρήστης να καθορίσει µέχρι και την ένταση µε την οποία πατιέται το Foot Pedal: X[Foot_Pedal]=1345 Εν τέλει η βιβλιοθήκη JFugue παρέχει όλες τις δυνατότητες, αλλά και πολλές παραπάνω από αυτές που παρέχει το abc4j σε όλους τους τοµείς και αποτελεί σηµείο εκκίνησης για οποιαδήποτε project µουσικής τεχνολογίας που χρειάζεται να υλοποιηθεί σε Java. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 15 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.4 JMusic Η βιβλιοθήκη της JMusic παρέχει µια σειρά από εργαλεία χρήσιµα τόσο σε συνθέτες µουσικής όσο και σε προγραµµατιστές που επιθυµούν να αναπτύξουν εφαρµογές που στηρίζονται στη µουσική τεχνολογία στη Java. Επίσης παρέχει ένα ολόκληρο framework για υποβοηθούµενη σύνθεση µουσικής από υπολογιστή, για σύνθεση µουσικών οργάνων και ανάλυση µουσικής. Παρέχει µεθόδους για οργάνωση, µετάλλαξη και ανάλυση µουσικών δεδοµένων. Οι παρτιτούρες της JMusic µπορούν να γίνουν render σε MIDI αρχεία, αλλά και σε άλλα format µουσικών αρχείων για αποθήκευση και αναπαραγωγή. Μπορεί επίσης να αποθηκεύσει XML αρχεία ή ακόµα και .jm αρχεία και υποστηρίζει real-time JavaSound [14], QuickTime [15] και MidiShare [16] formats. Μάλιστα επειδή είναι γραµµένη εξ’ ολοκλήρου σε Java, µπορεί να χρησιµοποιηθεί σε οποιοδήποτε λειτουργικό σύστηµα. Ο αριθµός των projects που στηρίζονται στη βιβλιοθήκη αυτή είναι τεράστιος. Πέρα από το Impro-Visor που παρουσιάστηκε στο κεφάλαιο 2.2 έχει χρησιµοποιηθεί στα: CodeSounding [17], Red Wine Music [18], JM-Etude [19] και σε πολλά άλλα. Αυτό δείχνει ότι αποτελεί µια δοκιµασµένη υλοποίηση αρκετά ώριµη για να χρησιµοποιηθεί σε πραγµατικές εφαρµογές. Μετά από λεπτοµερή µελέτη των διαθέσιµων τεχνολογιών, η JMusic αποτελεί µια από τις καλύτερες υποψήφιες τεχνολογίες για οποιαδήποτε εφαρµογή που χρησιµοποιεί Java Sounds, και οι δυνατότητές της θα παρουσιαστούν αναλυτικότερα στη συνέχεια της πτυχιακής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 16 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.5 JFrets To JFrets (βλέπε Σχήµα 2-9) είναι ένα πακέτο λογισµικού ανοιχτού κώδικα που δηµιουργήθηκε από τον Matt Warman. Αποτελεί µια προσπάθεια για δηµιουργία ενός εργαλείου που θα βοηθήσει τον χρήστη στην εκµάθηση κιθάρας. Το πρόγραµµα αυτό µπορεί να βοηθήσει έναν αρχάριο χρήστη να εντοπίσει τις θέσεις στις οποίες πρέπει να τοποθετήσει τα δάχτυλά του για να παίξει ένα chord. Μπορεί επίσης να βοηθήσει έναν µουσικό να γράψει παρτιτούρες για τη µουσική που συνθέτει σε ηλεκτρονική µορφή. Σχήµα 2-9: ∆ιεπαφή της εφαρµογής JFrets. Σε αυτή την εφαρµογή ο χρήστης µπορεί να κάνει interact µε το ποντίκι πάνω στην ταστιέρα της κιθάρας. Αν πατήσει αριστερό κλικ σε ένα σηµείο στην ταστιέρα ακούγεται η εκάστοτε νότα στην κιθάρα και εµφανίζεται ένας κύκλος µε το όνοµα της νότας που ακούστηκε. Το αξιόλογο σηµείο της συγκεκριµένης εφαρµογής είναι ο τρόπος µε τον οποίο υλοποιήθηκε η ταστιέρα ώστε να αλληλεπιδρά µε το χρήστη. Αυτό που έγινε περιληπτικά είναι να δηµιουργηθεί ένα πάνελ όπου µέσα φορτώνεται η εικόνα της ταστιέρας της κιθάρας και πάνω από αυτή υλοποιήθηκε ένα JTable το οποίο έχει 6 γραµµές και 23 στήλες (όσα και τα διαστήµατα της ταστιέρας της κιθάρας) όπου κάθε κελί αντιστοιχεί στην νότα της κιθάρας. Το JTable είναι transparent και όταν ο χρήστης πατά πάνω σε ένα σηµείο στην ουσία πατά σε ένα κελί του πίνακα. Επίσης γράφεται αυτόµατα και η ταµπλατούρα από κάτω. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 17 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Η εφαρµογή διαθέτει επιπλέον µετρονόµο όπως φαίνεται στο Σχήµα 2-10, για να βοηθήσει στο «συντονισµό» του µουσικού σε ορισµένο µέτρο. Μετρονόµος είναι ένα µηχάνηµα που κρατάει το µέτρο στο κοµµάτι που παίζει κανείς. Είναι απαραίτητο σε όλα τα µουσικά όργανα και στην ουσία παράγει ήχο κάθε συγκεκριµένα δευτερόλεπτα που καθορίζει ο χρήστης. Ο ήχος είναι σαν τους χτύπους του ρολογιού. Σχήµα 2-10: Ο µετρονόµος του JFrets. Επίσης διαθέτει δυνατότητες αναπαραγωγής και αποθήκευσης της µουσικής που συνθέτει ο συνθέτης. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 18 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2.6 Οδηγίες λήψης κώδικα εφαρμογών Για να αποκτήσει κανείς τον κώδικα των παραπάνω εφαρµογών θα πρέπει να κάνει χρήση CVS [20], Subversion [21] repositories ή να τον κατεβάσει από αντίστοιχες σελίδες στο Ίντερνετ. Το CVS και το Subversion αποτελούν concurrent version systems που δίνουν τη δυνατότητα σε προγραµµατιστές να εργάζονται ταυτόχρονα στον κώδικα ενός project. Για τη λήψη του κώδικα των εφαρµογών που παρουσιάστηκαν µέχρι τώρα, υπάρχει σε ορισµένα projects η δυνατότητα απόκτησης του κώδικα µόνο µέσα από τα συστήµατα αυτά. Όσον αφορά τα repositories υπάρχουν αρκετά εργαλεία τα οποία µπορούν να χρησιµοποιηθούν για τη λήψη του κώδικα. Αναγκαία προϋπόθεση είναι ο χρήστης να ανοίξει λογαριασµό στο http://dev.java.net/ 2.6.1 Λήψη κώδικα JFugue Music Notepad από NetBeans Με τη χρήση του εργαλείου NetBeans [22] η διαδικασία είναι η ακόλουθη: Επιλέγουµε από το µενού Versioning CVS Checkout.. Στο παράθυρο που εµφανίζεται τοποθετούµε τα στοιχεία του λογαριασµού και του project που επιθυµούµε να κατεβάσουµε :pserver:[email protected]:/cvs και πατάµε next. Στο Σχήµα 2-11 εµφανίζεται το παράθυρο µετά την σωστή καταχώρηση των παραπάνω στοιχείων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 19 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 2-11: Παράθυρο Netbeans για το checkout του JFugue Music Notepad. Γράφετε στο πεδίο module nbjfuguesupport και στη συνέχεια επιλέγετε την τοποθεσία που θέλετε να αποθηκευτεί ο κώδικας στον υπολογιστή σας. Πατάτε finish περιµένετε µέχρι να ολοκληρωθεί το checkout και στη συνέχεια τρέχετε τον κώδικα στον υπολογιστή σας. 2.6.2 Λήψη κώδικα JFrets από NetBeans Με τη χρήση του εργαλείου NetBeans η διαδικασία είναι η ακόλουθη: Επιλέγουµε από το µενού Versioning Subversion Checkout.. Στο παράθυρο που εµφανίζεται τοποθετούµε τα στοιχεία του λογαριασµού και του project που επιθυµούµε να κατεβάσουµε https://jfrets.dev.java.net/svn/jfrets και πατάµε next. Στο Σχήµα 2-12 παρουσιάζεται το παράθυρο µετά την σωστή καταχώρηση των παραπάνω στοιχείων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 20 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 2-12: Παράθυρο Netbeans για το checkout του JFrets. Eπιλέγετε την τοποθεσία που θέλετε να αποθηκευτεί ο κώδικας στον υπολογιστή σας και πατάτε finish. Όταν ολοκληρωθεί το checkout προσθέτετε στον κώδικα της εφαρµογής την βιβλιοθήκη jdom και τρέχετε τον κώδικα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 21 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 3. Μουσικοί κανόνες Όπως κάθε γλώσσα έχει τα γράµµατα και τους κανόνες της, το ίδιο έχει και η µουσική µε τη διαφορά ότι στη µουσική αντί για γράµµατα χρησιµοποιούµε επτά διαφορετικούς µουσικούς ήχους (φωνές) µε τις πιο κάτω ονοµασίες : Ελληνική γραφή: ΝΤΟ-ΡΕ-ΜΙ-ΦΑ-ΣΟΛ-ΛΑ-ΣΙ Λατινική γραφή: C-D-E-F-G-A-B Οι µουσικοί αυτοί ήχοι παράγονται από τα διάφορα µουσικά όργανα ή από τη φωνή του ανθρώπου, όταν αυτός τραγουδάει και έχουν ένα ορισµένο ύψος. Τους ήχους αυτούς τους ονοµάζουµε µουσικούς φθόγγους και γράφονται στο πεντάγραµµο µε ειδικά σχήµατα, που εκφράζουν τη διάρκειά τους και λέγονται φθογγόσηµα ή ξενόγλωσσα, νότες (notes). Υπάρχουν και άλλοι ήχοι που δεν έχουν ένα καθορισµένο ύψος, όπως οι θόρυβοι και οι κρότοι. Καταλήγουµε λοιπόν στο συµπέρασµα ότι: α) Μουσικός φθόγγος είναι ο ήχος (φωνή) που έχει ένα καθορισµένο ύψος. β) Θόρυβος - κρότος είναι ο ήχος που δεν έχει ένα καθορισµένο ύψος. γ) Φθογγόσηµο ή νότα είναι το σχήµα µε το οποίο γράφουµε το µουσικό φθόγγο στο πεντάγραµµο. Πεντάγραμμο Πεντάγραµµο είναι το σύνολο από 5 οριζόντιες και παράλληλες γραµµές που απέχουν η µια από την άλλη το ίδιο (βλέπε Σχήµα 3-1). Σχήµα 3-1: Μουσικό πεντάγραµµο. Από τις πέντε γραµµές του πενταγράµµου σχηµατίζονται τέσσερα διαστήµατα, όπως φαίνεται στο Σχήµα 3-2. ∆ιάστηµα λέγεται η απόσταση που χωρίζει τη µια γραµµή από την άλλη. Σχήµα 3-2: ∆ιαστήµατα µουσικού πενταγράµµου. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 22 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Από πού εξαρτάται η ονομασία των φθόγγων Η ονοµασία των φθόγγων εξαρτάται από τη θέση που είναι γραµµένοι στο πεντάγραµµο και από το όνοµα και τη θέση του Κλειδιού. Κλειδί (Γνώμονας) Κλειδί της µουσικής ή γνώµονας είναι ένα σηµείο που γράφεται στην αρχή κάθε πενταγράµµου και, ανάλογα µε τη θέση του, προσδιορίζει το ύψος και το όνοµα ενός ορισµένου φθόγγου, και σύµφωνα µε αυτόν ονοµάζουµε και τους άλλους. Τα Κλειδιά της µουσικής είναι τα εξής: 1. ∆ύο κλειδιά του ΣΟΛ (στη 2η και στην 1η γραµµή) τα οποία φαίνονται στο Σχήµα 3-3. α) 2ης γραµµής β)1ης γραµµής Σχήµα 3-3: Κλειδιά του ΣΟΛ. 2. ∆ύο κλειδιά του ΦΑ (στην 4η και στην 3η γραµµή) που παρουσιάζονται στο Σχήµα 3-4. α) 4ης γραµµής β)3ης γραµµής Σχήµα 3-4: Κλειδιά του ΦΑ. 3. Τέσσερα κλειδιά του ΝΤΟ(στην 1η, 2η, 3η και 4η γραµµή) που απεικονίζονται στο Σχήµα 3-5. α) 1ης γραµµής β)2ης γραµµής γ)3ης γραµµής δ)4ης γραµµής Σχήµα 3-5: Κλειδιά του ΝΤΟ. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 23 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Κλειδί του ΣΟΛ της 2ης γραμμής Σχήµα 3-6: Κλειδί του ΣΟΛ της 2ης γραµµής. Λέγεται της δεύτερης γραµµής γιατί στο γράψιµό του κάνει ένα τύλιγµα πάνω στη δεύτερη γραµµή του πενταγράµµου, παριστάνοντας έτσι ένα φθογγόσηµο στρογγυλό πάνω στη δεύτερη γραµµή (βλέπε Σχήµα 3-6). Όλοι οι φθόγγοι Στο Σχήµα 3-7 παρουσιάζονται όλοι οι φθόγγοι. α) Κάτω από το πεντάγραµµο β) µέσα στο πεντάγραµµο γ) πάνω από το πεντάγραµµο Σχήµα 3-7: Οι φθόγγοι του πενταγράµµου. Οκτάβα (Ογδόη) Η απόσταση ανάµεσα σε δύο φθόγγους που έχουν το ίδιο όνοµα και βρίσκονται σε δύο γειτονικές σειρές λέγεται οκτάβα (ογδόη) γιατί η απόσταση αυτή περιλαµβάνει 8 φθόγγους, δηλαδή στο Σχήµα 3-8, η απόσταση από το ΝΤΟ της πρώτης σειράς µέχρι το ΝΤΟ της δεύτερης σειράς λέγεται οκτάβα ή ογδόη κ.ο.κ. Σχήµα 3-8: Οκτάβα ή ογδόη. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 24 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Κλίμακα (Σκάλα) Κλίµακα λέγεται η διαδοχή 8 συνεχών φθόγγων που ανεβαίνουν και κατεβαίνουν. Στο Σχήµα 3-9 παρουσιάζεται η κλίµακα του ΝΤΟ. Σχήµα 3-9: Κλίµακα (σκάλα του ΝΤΟ). Ηχητικές αποστάσεις στους φθόγγους της κλίμακας Οι ηχητικές αποστάσεις σε δύο γειτονικούς φθόγγους της κλίµακας είναι µεγάλες και µικρές. Οι µεγάλες λέγονται τόνοι και οι µικρές ηµιτόνια (βλέπε Σχήµα 3-10). Ηµιτόνιο είναι η µισή φωνή, και τόνος η ολόκληρη φωνή. Ο τόνος έχει δύο ηµιτόνια δηλαδή 2 µισές φωνές. Στην κλίµακα ΝΤΟ όλες οι αποστάσεις µεταξύ τους είναι µεγάλες (τόνοι), εκτός από το ΜΙ - ΦΑ και το ΣΙ - ΝΤΟ που είναι µικρές (ηµιτόνια). Γι’ αυτό το λόγο όταν τραγουδάµε την κλίµακα ανεβαίνοντας, στο ΜΙ - ΦΑ και στο ΣΙ - ΝΤΟ δεν υψώνουµε πολύ τη φωνή µας γιατί οι αποστάσεις αυτές είναι µικρές (ηµιτόνια). Το ίδιο γίνεται και στο κατέβασµα, που βρίσκουµε αυτούς τους φθόγγους ανάποδα, ΦΑ ΜΙ και ΝΤΟ - ΣΙ, και εδώ δεν χαµηλώνουµε πολύ τη φωνή µας. Σχήµα 3-10: Τόνοι και ηµιτόνια. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 25 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Τις µικρές αποστάσεις τις βλέπουµε καλύτερα στο πιο πάνω παραστατικό σχήµα της κλίµακας (σκάλας), όπου την παρουσιάζουµε σαν σκάλα σπιτιού. Αν προσέξουµε, βλέπουµε ότι οι αποστάσεις από το ΜΙ ως το ΦΑ και από το ΣΙ ως το ΝΤΟ είναι µικρότερες, γιατί τα σκαλοπάτια ΦΑ και ΝΤΟ είναι κατά το µισό χαµηλότερα από τα άλλα. Αξίες φθογγοσήμων Παρατηρούµε ότι οι φθόγγοι που ανήκουν σε µία µουσική σύνθεση, όταν παίζονται από διάφορα µουσικά όργανα ή τραγουδιούνται από ανθρώπινες φωνές, διαρκούν άλλοι περισσότερο και άλλοι λιγότερο. Η διάρκειά τους αυτή - η αξία τους - είναι ανάλογη µε το σχήµα µε το οποίο τα φθογγόσηµα είναι γραµµένα στο πεντάγραµµο. Άρα καταλήγουµε στον εξής ορισµό: Αξίες είναι οι διάρκειες των φθόγγων που παριστάνονται στο πεντάγραµµο µε διάφορα σχήµατα φθογγοσήµων, και ανάλογα µε το σχήµα κάθε φθογγοσήµου καθορίζεται η διάρκεια κάθε φθόγγου. Στο Σχήµα 3-11 απεικονίζονται τα σχήµατα των φθογγοσήµων. Συνολικά είναι επτά. Σχήµα 3-11: Σχήµατα φθογγοσήµων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 26 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Διάρκεια φθογγοσήμων Από τα φθογγόσηµα που αναφέραµε πιο πάνω, τη µεγαλύτερη διάρκεια την έχει το ολόκληρο, µετά ακολουθεί το µισό, που έχει τη µισή διάρκεια από το ολόκληρο, µετά το τέταρτο που έχει διάρκεια ίση µε το ένα τέταρτο του ολόκληρου κ.ο.κ. Έτσι φτάνουµε στη µικρότερη διάρκεια, που είναι το εξηκοστό τέταρτο και έχει διάρκεια ίση µε το ένα εξηκοστό τέταρτο του ολόκληρου. Διαιρέσεις ολόκληρου και υποδιαιρέσεις αυτών 1. Ένα oλόκληρο µπορούµε να το διαιρέσουµε σε µισά, σε τέταρτα, σε όγδοα, σε δέκατα έκτα, σε τριακοστά δεύτερα και σε εξηκοστά τέταρτα. Για να καταλάβουµε πιο καλά αυτές τις διαιρέσεις, παροµοιάζουµε το ολόκληρο µε ένα στρογγυλό πορτοκάλι όπως φαίνεται στο Σχήµα 3-12, π.χ. Σχήµα 3-12: Ολόκληρο. 2. Αν τώρα το πορτοκάλι το κόψουµε στη µέση όπως µας δείχνει το πιο κάτω σχήµα, θα έχουµε 2 κοµµάτια από το µισό πορτοκάλι. Άρα το ολόκληρο διαιρείται σε 2 µισά όπως απεικονίζεται στο Σχήµα 3-13. Σχήµα 3-13: ∆ιαίρεση ολόκληρου σε 2 µισά. 3. Αν το κόψουµε στα τέσσερα θα έχουµε 4 κοµµάτια και το κάθε κοµµάτι το λέµε τέταρτο, όπως φαίνεται στο Σχήµα 3-14. Άρα το ολόκληρο διαιρείται σε 4 τέταρτα όπως µας δείχνει το πιο κάτω σχήµα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 27 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-14: ∆ιαίρεση ολόκληρου σε 4 τέταρτα. 4. Αν το κόψουµε στα οκτώ θα έχουµε 8 κοµµάτια όπως απεικονίζεται στο Σχήµα 3-15, και το κάθε κοµµάτι το λέµε όγδοο. Άρα το ολόκληρο διαιρείται σε 8 όγδοα όπως µας δείχνει το πιο κάτω σχήµα. Σχήµα 3-15: ∆ιαίρεση ολόκληρου σε 8 όγδοα. Το ανάλογο µπορεί να γίνει αν κόψουµε το πορτοκάλι σε 16, σε 32 και σε 64 κοµµάτια. ∆ηλαδή το ολόκληρο διαιρείται σε 16 δέκατα έκτα, ή σε 32 τριακοστά δεύτερα ή σε 64 εξηκοστά τέταρτα. Πιο κάτω παρουσιάζουµε αναλυτικά µε τι ισούται η κάθε αξία φθογγοσήµου και την κανονική τους γραφή στο πεντάγραµµο. 1. Με τι ισούται το ένα ολόκληρο 2 4 8 ), ή µε 4 τέταρτα ( ), ή µε 8 όγδοα ( ), ή 2 4 8 16 32 δέκατα έκτα ( ), ή µε 32 τριακοστά δεύτερα ( ), ή µε 64 εξηκοστά τέταρτα 16 32 (βλέπε Σχήµα 3-16). Το ολόκληρο ισούται µε τα 2 µισά ( Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων µε 16 ( 64 ) 64 28 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-16: Ισοδυναµίες ολόκληρου. ΣΗΜΕΙΩΣΗ: Τα όγδοα, τα δέκατα έκτα, τα τριακοστά δεύτερα και τα εξηκοστά τέταρτα, για καλή οπτική ανάγνωση και για συντοµία στο γράψιµό τους, ενώνονται µεταξύ τους µε γραµµές. Με µία γραµµή τα όγδοα, µε 2 τα δέκατα έκτα, µε 3 τα τριακοστά δεύτερα και µε 4 τα εξηκοστά τέταρτα, όπως τα βλέπουµε στο πιο πάνω σχήµα. Έχουµε περιθώριο να ενώσουµε από δυο και πάνω. 2. Με τι ισούται το ένα μισό 2 4 8 ), ή µε 4 όγδοα ( ), ή µε 8 δέκατα έκτα ( ), ή µε 16 4 8 16 16 32 τριακοστά δεύτερα ( ), ή µε 32 εξηκοστά τέταρτα ( ) (βλέπε Σχήµα 3-17). 32 64 Το µισό ισούται µε 2 τέταρτα ( Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 29 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-17: Ισοδυναµίες µισού. 3. Με τι ισούται το ένα τέταρτο 2 4 ), ή µε 4 δέκατα έκτα ( ), ή µε 8 τριακοστά 8 16 8 16 δεύτερα ( ), ή µε 16 εξηκοστά τέταρτα ( )(βλέπε Σχήµα 3-18). 32 64 Το ένα τέταρτο ισούται µε 2 όγδοα ( Σχήµα 3-18: Ισοδυναµίες τετάρτου. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 30 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 4. Με τι ισούται το ένα όγδοο Το ένα όγδοο ισούται µε 2 δέκατα έκτα ( εξηκοστά τέταρτα ( 2 4 ), ή µε 4 τριακοστά δεύτερα ( ), ή µε 8 16 32 8 )(βλέπε Σχήµα 3-19). 64 Σχήµα 3-19: Ισοδυναµίες ογδόου. 5. Με τι ισούται το ένα δέκατο έκτο Το ένα δέκατο έκτο ισούται µε 2 τριακοστά δεύτερα ( 2 4 ), ή µε 4 εξηκοστά τέταρτα ( ) 32 64 (βλέπε Σχήµα 3-20). Σχήµα 3-20: Ισοδυναµίες δεκάτου έκτου. 6. Με τι ισούται το ένα τριακοστό δεύτερο Το ένα τριακοστό δεύτερο ισούται µε 2 εξηκοστά τέταρτα ( 2 ) όπως φαίνεται στο Σχήµα 64 3-21. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 31 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-21: Ισοδυναµία τριακοστού δευτέρου. Με τι ισούται η αξία του φθογγοσήμου (από τη μεγαλύτερη στην αμέσως μικρότερη) Από ότι είδαµε σε όλες τις διαιρέσεις αξιών, καταλήγουµε στο συµπέρασµα ότι η µεγαλύτερη αξία ισούται µε δύο της αµέσως µικρότερης όπως απεικονίζεται στο Σχήµα 3.22. Σχήµα 3-22: Ισοδυναµία αξίας κάθε φθογγοσήµου. Γραφή φθογγοσήμων Οι φθόγγοι, σε όποια θέση και αν γραφτούν στο πεντάγραµµο, µπορούν να πάρουν, για τον καθορισµό της διάρκειάς τους, όλα τα σχήµατα των φθογγοσήµων. Οι ουρές των σχηµάτων µισού, τετάρτου, ογδόου, δεκάτου έκτου, τριακοστού δεύτερου και εξηκοστού τετάρτου, όπως απεικονίζεται στο Σχήµα 3-23. ∆ηλαδή: Οι ουρές των φθογγοσήµων, που βρίσκονται από την τρίτη γραµµή του πενταγράµµου και κάτω, γράφονται προς τα πάνω και δεξιά του φθογγοσήµου. Και στα φθογγόσηµα που βρίσκοντα από την τρίτη γραµµή του πενταγράµµου και πάνω γράφονται προς τα κάτω κι αριστερά του φθογγοσήµου, αρκεί να αποτελούν µονοφωνία. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 32 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-23: Γραφή φθογγοσήµων στο πεντάγραµµο. Μουσικό μέτρο - Διαστολή Κάθε µουσική σύνθεση που γράφεται στο πεντάγραµµο χωρίζεται κατά διαστήµατα µε κάθετες γραµµές που λέγονται διαστολές. Η απόσταση, που χωρίζει τη µια διαστολή από την άλλη, λέγεται µουσικό µέτρο· αλλά και η απόσταση από το κλειδί ως τη διαστολή λέγεται και αυτή µέτρο. Το πρώτο, λοιπόν, µέτρο του πενταγράµµου γίνεται από µια διαστολή. Μουσικό µέτρο λέγεται η απόσταση µεταξύ δύο διαστολών, και διαστολή λέγεται η κάθετη διαχωριστική γραµµή. Όταν τελειώνει µία µουσική σύνθεση, στο τελευταίο της µέτρο η δεξιά διαστολή γράφεται διπλή, όπως φαίνεται στο Σχήµα 3-24. Η δεύτερη γραµµή της διπλής διαστολής γράφεται παχύτερη. Σχήµα 3-24: Μουσικό µέτρο στο πεντάγραµµο. ΣΗΜΕΙΩΣΗ: Η διπλή διαστολή µπορεί να γραφτεί και σε ένα ενδιάµεσο µέρος µιας µουσικής σύνθεσης. Αυτό γίνεται για να χωρίσει το τέλος µιας µουσικής φράσης. Αριθμός μέτρου Στην αρχή κάθε µουσικής σύνθεσης, µετά το κλειδί και στα αριστερά του πρώτου µέτρου, 2 3 4 γράφεται ένας κλασµατικός αριθµός, π.χ. , , κ.λ.π. 4 4 4 Αυτός ο αριθµός δίνει: πρώτον, το όνοµα του µέτρου και, δεύτερον, το ίσο άθροισµα των 2 αξιών που θα έχει κάθε µέτρο. Αν π.χ. έχουµε στην αρχή µιας µουσικής σύνθεσης λέµε 4 ότι η σύνθεση αυτή είναι γραµµένη σε µέτρο δύο τετάρτων και το κάθε µέτρο θα έχει Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 33 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2 3 , αν έχουµε λέµε ότι η σύνθεση είναι γραµµένη σε µέτρο τριών 4 4 3 τετάρτων και το κάθε µέτρο θα έχει άθροισµα αξιών κ.ο.κ. 4 άθροισµα αξιών Βλέπουµε λοιπόν ότι ο κλασµατικός αυτός αριθµός, επιβάλει να έχουν όλα τα µέτρα το ίδιο άθροισµα αξιών σύµφωνα µε την δική του αξία. 1ο µέτρο 2ο µέτρο 3ο µέτρο 4ο µέτρο Σχήµα 3-25: Μουσική σύνθεση σε µέτρο 2:4. 2 , και τα 4 µέτρα της έχουν διάφορες αξίες. Κάνουµε τώρα την επαλήθευση για να δούµε µήπως υπάρχει κάποιο λάθος. Εξετάζουµε το 1ο µέτρο και βλέπουµε ότι έχει ένα µισό. Το µισό 2 έχει . Άρα είναι σωστό. Το 2ο µέτρο έχει δύο τέταρτα χωρισµένα, άρα είναι σωστό. Το 4 3ο µέτρο έχει 2 όγδοα και ένα τέταρτο. Τα δύο όγδοα δεν µας κάνουν ένα τέταρτο; Και ένα 2 τέταρτο χωριστά, άρα . Το 4ο µέτρο έχει 4 όγδοα. Τα δύο όγδοα µας κάνουν ένα 4 2 τέταρτο. Και ένα τέταρτο που µας κάνουν τα άλλα δύο όγδοα, γίνονται . Άρα και αυτό 4 το µέτρο είναι σωστό. Όπως βλέπουµε στο Σχήµα 3-25, η µουσική σύνθεση είναι γραµµένη σε µέτρο Παρεστιγμένα φθογγόσημα Το φθογγόσηµο που έχει δεξιά του µια στιγµή ( ) λέγεται παρεστιγµένο. Η στιγµή αυτή λέγεται στιγµή διαρκείας και αυξάνει τη διάρκεια του φθογγοσήµου κατά το µισό της πραγµατικής του αξίας. Στο Σχήµα 3-27 απεικονίζονται οι ισοδυναµίες των παρεστιγµένων φθογγοσήµων. Αν π.χ. έχουµε ένα φθογγόσηµο σε µισό παρεστιγµένο ( ) και θέλουµε να µάθουµε πόσα τέταρτα έχει, για να το βρούµε θα κάνουµε το εξής: Θα βρούµε πρώτα πόσα τέταρτα έχει η πραγµατική του αξία και µετά θα προσθέσουµε από αυτό που θα βρούµε το µισό. Εδώ 2 λοιπόν το µισό έχει πραγµατική αξία και ένα τέταρτο που µας αυξάνει η στιγµή (γιατί 4 2 1 3 3 το µισό είναι από τα το ) γίνεται . Άρα ένα µισό παρεστιγµένο έχει . 4 4 4 4 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 34 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Αν τώρα θέλουµε να µάθουµε ένα ολόκληρο παρεστιγµένο ( ) πόσα τέταρτα έχει θα 4 2 πούµε: Το ολόκληρο χωρίς στιγµή µας κάνει και που µας αυξάνει η στιγµή σύνολο 4 4 6 , όπως φαίνεται στο Σχήµα 3-26. 4 Σχήµα 3-26: Ισοδυναµία ολόκληρου παρεστιγµένου. Σχήµα 3-27: Ισοδυναµίες παρεστιγµένων φθογγοσήµων. Παύσεις Παύσεις ονοµάζουµε τα σηµεία εκείνα που µας δείχνουν τον ανάλογο χρόνο διακοπής (σιωπής), στην πορεία ενός µουσικού κοµµατιού. Όσα σχήµατα έχουµε για τις διάρκειες των φθογγοσήµων άλλα τόσα έχουµε και για τις διάρκειες των παύσεων. Η διαφορά είναι ότι τα σχήµατα των παύσεων µας καθορίζουν διάρκεια διακοπής χωρίς ήχο, ενώ τα σχήµατα των φθογγοσήµων µας καθορίζουν διάρκεια µε ήχο. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 35 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Κάθε διάρκεια φθογγοσήµου έχει και την αντίστοιχη παύση της. Στο Σχήµα 3-28 παρουσιάζονται αναλυτικά τα σχήµατα των παύσεων µαζί µε τα σχήµατα διαρκείας των φθογγοσήµων. Σχήµα 3-28: Πίνακας σχηµάτων φθογγοσήµων και παύσεων. Η παύση ολόκληρου και η παύση µισού κατά το σχήµα είναι ίδιες µε τη διαφορά ότι η παύση του ολόκληρου γράφεται κάτω ακριβώς από την τέταρτη γραµµή του πενταγράµµου, ενώ του µισού πάνω ακριβώς από την τρίτη γραµµή µε µια παύλα. Οι άλλες παύσεις δεν έχουν ορισµένη θέση για το γράψιµό τους στο πεντάγραµµο. Ανάλογα µε τη θέση που έχουν τα φθογγόσηµα πριν ή µετά την παύση, γράφεται και η παύση στο ανάλογο ύψος του πενταγράµµου. Όπως κάθε αξία φθογγοσήµου υποδιαιρείται σε 2 ή και περισσότερες αξίες, το ίδιο γίνεται και µε τις αξίες των παύσεων. Π.χ. µια παύση ολόκληρου είναι ίση µε 2 παύσεις µισού ή µε 4 παύσεις τετάρτου ή µε 8 παύσεις ογδόου ή µε 16 παύσεις δεκάτου έκτου κ.ο.κ. Οι παύσεις επίσης µπορούν να γραφούν παρεστιγµένες και δις παρεστιγµένες όπως και τα φθογγόσηµα όπως στο Σχήµα 3-29. Σχήµα 3-29: Παρεστιγµένες και δις παρεστιγµένες παύσεις. Σημεία αλλοίωσης Σηµεία αλλοίωσης λέγονται τα σηµεία αυτά που γράφονται αριστερά των φθόγγων και µεταβάλλουν (αλλοιώνουν) το ύψος τους. Τα σηµεία αυτά είναι απλά και διπλά. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 36 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Απλά σημεία αλλοίωσης Τα απλά σηµεία αλλοίωσης ανεβάζουν ή κατεβάζουν το ύψος των φθόγγων κατά ένα ηµιτόνιο (µισή φωνή) και είναι: α) η δίεση (#) που ανεβάζει το φθόγγο ένα ηµιτόνιο, β) η ύφεση ή µπεµόλ που κατεβάσει το φθόγγο ένα ηµιτόνιο και γ) η αναίρεση που επαναφέρει ένα αλλοιωµένο φθόγγο στο φυσικό του ύψος. Διπλά σημεία αλλοίωσης Τα διπλά σηµεία αλλοίωσης ανεβάζουν ή κατεβάζουν το ύψος των φθόγγων κατά δύο ηµιτόνια, δηλαδή ένα τόνο και είναι: α) η διπλή δίεση ( ) που ανεβάζει το φθόγγο κατά δύο ηµιτόνια (ένα τόνο), β) η διπλή ύφεση που κατεβάζει το φθόγγο κατά δύο ηµιτόνια (ένα τόνο) και που επαναφέρει ένα αλλοιωµένο φθόγγο µε διπλή δίεση ή διπλή γ) η διπλή αναίρεση ύφεση, στο φυσικό του ύψος. (Η διπλή αναίρεση σπάνια χρησιµοποιείται). Για να ονοµάσουµε ένα φθόγγο µε δίεση, µε ύφεση ή αναίρεση πρέπει το σηµείο αλλοίωσης να βρίσκεται στα αριστερά του φθόγγου, όπως απεικονίζεται στο Σχήµα 3-30. Σχήµα 3-30: Απεικόνιση φθογγοσήµου ΝΤΟ δίεση στο πεντάγραµµο. Η δίεση ανήκει στο δεύτερο ΝΤΟ. Για να προφέρουµε τώρα αυτούς τους φθόγγους θα πούµε: ΝΤΟ, ΝΤΟ#. Όπως βλέπουµε για να πούµε το φθόγγο µε τη δίεση, λέµε πρώτα το φθόγγο και µετά τη δίεση και ας είναι γραµµένη πρώτα η δίεση, δηλαδή δε λέµε ποτέ δίεση ΝΤΟ αλλά ΝΤΟ δίεση. Το ίδιο γίνεται και για την ύφεση, την αναίρεση καθώς και για τα διπλά σηµεία αλλοίωσης. Αντιστοιχία φθογγοσήμων στην ταστιέρα της κιθάρας Στο Σχήµα 3-31 παρουσιάζεται η αντιστοιχία των φθογγοσήµων (νοτών) του πενταγράµµου στις θέσεις της ταστιέρας (µπράτσου) της κιθάρας. Συγκεκριµένα απεικονίζεται η οπτική αναπαράσταση της ταστιέρας της κιθάρας µε τις χορδές, τα τάστα και τα φθογγόσηµα που αντιστοιχούν σε κάθε τάστο της χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 37 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 3-31: Αντιστοιχία φθογγοσήµων στην ταστιέρα της κιθάρας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 38 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Τρόπος ανάγνωσης του Chord - Lexicon Ονοµατολογία: Το αρχικό λατινικό στοιχείο ενός ακκόρντου αντιπροσωπεύει την ονοµατολογία του, όπως απεικονίζεται στον Πίνακα 3-1. Α Β C D : : : : Λα Σι Ντο Ρε E : Μι F : Φα G : Σολ Πίνακας 3-1: ∆ιεθνής ονοµατολογία ακκόρντων. Τα στοιχεία που παραθέτονται στον Πίνακα 3-2 αντιπροσωπεύουν το είδος του: A Am A maj7 Am maj 7 A7 Αm 7 Α6 Αm 6 Α9 Αm 9 Α6 : Λα (µατζόρε) (όταν η ονοµατολογία του ακκόρντου δεν συνοδεύεται από άλλη ένδειξη εννοείται ότι το ακκόρντο είναι µατζόρε). : Λα µινόρε (minor). : Λα µατζόρε µεγάλης εβδόµης (major 7). : Λα µινόρε µεγάλης εβδόµης. : Λα (µατζόρε) εβδόµης (µικρής). : Λα µινόρε εβδόµης. : Λα (µατζόρε) έκτης. : Λα µινόρε έκτης. : Λα (µατζόρε) ενάτης. : Λα µινόρε ενάτης. : Λα (µατζόρε) έκτης - ενάτης. 9 Α 11 Α 13 Α+ Α +7 Α 7 Α° 7 A#9 Α sus4 Α 7sus4 : : : : : : : : : Λα (µατζόρε) ενδεκάτης. Λα (µατζόρε) δεκάτης τρίτης. Λα αυξηµένη. Λα αυξηµένη µε εβδόµη. Λα ελαττωµένη µε εβδόµη. Λα ελαττωµένη µε εβδόµη ελαττωµένη (diminuita). Λα (µατζόρε) µε µεγάλη ενάτη. Λα (µατζόρε) µε τετάρτη (suspended 4th - πρόσθετη 4η). Λα (µατζόρε) εβδόµης µε τετάρτη. Πίνακας 3-2: Είδη ακκόρντων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 39 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Τα ακκόρντα που δίνονται στο CHORD – LEXICON µπορεί σε κάποιες εκδόσεις να τα συναντήσετε µε διαφορετική σηµειογραφία, για το λόγο αυτό παραθέτουµε στον Πίνακα 33 τις γνωστότερες αντιστοιχίες. A maj 7 Α 7 Α° 7 Α +7 A9 ΑM 7 Αm 7 (b5) Α° Α 7+ A add9 Α6 A 6 (add9) A#7 Am 7(-5) A dim A 7 (#5) A 7 (add9) A dim A 7(+5) 9 Am A+ Ab5 A 7 (add11) A aug A (dim5) A #5 A (-5) A +5 Πίνακας 3-3: Σηµειογραφίες ακκόρντων. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 40 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 4. Προδιαγραφές πτυχιακής εργασίας Η πτυχιακή αυτή εργασία έχει ως στόχο την δηµιουργία µιας ηλεκτρονικής µουσικής κοινότητας για την εκµάθηση κιθάρας. Έχοντας ως γνώµονα την εύκολη και γρήγορη εκµάθηση της κιθάρας ακόµα και από έναν αρχάριο µαθητή, καταλήξαµε στη δηµιουργία µιας διεπάφης που θα παρέχει στο χρήστη οπτική απεικόνιση των νοτών που υπάρχουν στο πεντάγραµο στην ταστιέρα της κιθάρας. Με αυτό τον τρόπο πετύχαµε την γρήγορη εξοικείωση του µαθητή µε τις νότες του πενταγράµµου και την αντιστοιχία τους στην κιθάρα. Συγκεκριµένα ο score editor όπως µας δόθηκε παρείχε την εξής λειτουργικότητα στο χρήστη: 1. Σύνθεση σε µουσικό πεντάγραµµο 2. Επιλογή µουσικού οργάνου από τη λίστα µουσικών οργάνων που παρέχει η βιβλιοθήκη JMusic 3. Εναλλαγή παρτιτούρας 4. Επιλογή νότας/ων από το πεντάγραµο( multiple selection) µε κόκκινη ένδειξη της επιλογής στο πάνω µέρος της παρτιτούρας 5. Προσθήκη / ∆ιαγραφή / Μετακίνηση νότας στην παρτιτούρα 6. Καθαρισµό παρτιτούρας 7. Αναπαραγωγή παρτιτούρας Αν και ο score editor φαίνεται να καλύπτει ένα ευρύ φάσµα λειτουργιών, στην πράξη υπήρχαν αδυναµίες. Όταν τρέξαµε την εφαρµογή για πρώτη φορά διαπιστώσαµε ότι είχε bugs. Επίσης, ο σχεδιασµός και η επιλογή των components που συνθέτουν την διεπαφή του score editor που παρουσιάζεται στο Σχήµα 4-1, δεν ήταν ο καλύτερος για την λειτουργικότητα που παρείχε η εφαρµογή. Σχήµα 4-1: ∆ιεπαφή αρχικού score editor. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 41 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Μετά από λεπτοµερή µελέτη του κώδικα που µας δόθηκε, αποφασίσαµε να διορθώσουµε τις αδυναµίες που υπήρχαν, και να βελτιώσουµε τον κώδικα της εφαρµογής ώστε να παρέχει την ίδια λειτουργικότητα, µειώνοντας παράλληλα τις γραµµές του κώδικα. Έτσι µετά την ολοκλήρωση της πτυχιακής αυτής εργασίας η εφαρµογή που θα έχει υλοποιηθεί θα δίνει στο χρήστη τις εξής δυνατότητες: 1. Σύνθεση σε µουσικό πεντάγραµµο 2. Επιλογή µουσικού οργάνου από λίστα µουσικών οργάνων που ανήκουν στην κατηγορία «κιθάρα» 3. Εστίαση στο πάνω και το κάτω µέρος της παρτιτούρας µε εκάστοτε επιλογή από το χρήστη (εναλλαγή παρτιτούρας σε Treble / Bass) 4. Επιλογή νότας από το πεντάγραµο µε ταυτόχρονη αλλαγή του χρώµατος της επιλεγµένης νότας (κόκκινο) και εµφάνιση των θέσεων της νότας στην ταστιέρα της κιθάρας 5. Προσθήκη / ∆ιαγραφή / Μετακίνηση νότας στην παρτιτούρα 6. Χρήση µετρονόµου 7. Καθαρισµό παρτιτούρας 8. Αναπαραγωγή παρτιτούρας 9. Αποθήκευση παρτιτούρας Η διεπαφή της εφαρµογής θα περιέχει µενού File όπου µέσα θα υπάρχει ‘Clear Stave’, ‘Save Stave’, ‘Play Stave’ και ‘Exit’. Θα σχεδιαστεί µενού Τοοls όπου µέσα σε αυτό θα τοποθετηθούν εργαλεία όπως ο Μετρονόµος καθώς και µενού Help. Μόλις τρέχει η εφαρµογή θα δηµιουργείται ένα κεντρικό Panel που θα αποτελείται από δύο Panels.Το πάνω θα είναι ο editor (η παρτιτούρα) και από κάτω θα είναι το Panel µε την ταστιέρα της κιθάρας. Τέλος, η εφαρµογή θα ενσωµατωθεί σε ιστοσελίδα που θα καλύπτει τις ανάγκες µιας ηλεκτρονικής κοινότητας που αφορά την εκµάθηση κιθάρας. Συγκεκριµένα θα παρέχει τις εξής δυνατότητες στο χρήστη: • • • • • Εγγραφή στην σελίδα Ηλεκτρονική τάξη όπου ο χρήστης θα πειραµατίζεται µε την εφαρµογή Λήψη αρχείων θεωρίας και ασκήσεων που έχει ανεβάσει ο καθηγητής Φόρουµ, για συζήτηση µε µαθητές και καθηγητές Βοήθεια µε οδηγίες χρήσης της εφαρµογής Η ιστοσελίδα θα σχεδιαστεί ώστε µόνο οι εγγεγραµένοι χρήστες (µαθητές ή καθηγητές) να έχουν πρόσβαση σε όλες τις ενότητες της ιστοσελίδας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 42 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5. Ανάλυση του κώδικα score-editor Ο score editor που µας δόθηκε αποτελείται από εννιά αρχεία Java, ένα φάκελο που περιέχει τις εικόνες της εφαρµογής, δύο αρχεία τύπου form και τη βιβλιοθήκη JMusic.jar που βρίσκεται στο φάκελο Libraries. Στο Σχήµα 5-1 παρουσιάζεται η δοµή των αρχείων του score editor. Σχήµα 5-1: ∆οµή αρχείων του score editor. Είναι απαραίτητο πριν τρέξουµε την εφαρµογή για πρώτη φορά να ορίσουµε την ύπαρξη της βιβλιοθήκης JMusic.jar όπως φαίνεται στο Σχήµα 5-2. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 43 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 5-2: Προσθήκη βιβλιοθήκης JMusic στον score editor. Αφού τρέξουµε το πρόγραµµα θα παρατηρήσουµε σταδιακά κάποιες αδυναµίες του. Το πιο προφανές bug της εφαρµογής που µάλιστα πετάει και exceptions(βλέπε Σχήµα 5-2) εµφανίζεται όταν έχουν αφαιρεθεί όλες οι νότες και ο χρήστης πατήσει το κουµπί Remove Last Note. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 44 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 5-3: Exception του score editor. Μια εύκολη λύση στο παραπάνω πρόβληµα είναι να γίνεται disabled το κουµπί ‘Remove Last Note’ όταν δεν υπάρχουν νότες στο πεντάγραµµο. Ας αφήσουµε όµως τις αδυναµίες της εφαρµογής και ας µελετήσουµε τον τρόπο µε τον οποίο σχεδιάστηκε. Ας δούµε κάθε αρχείο Java ξεχωριστά. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 45 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5.1 Αρχείο: Main.java Εδώ βρίσκεται ο κώδικας που είναι απαραίτητος για την εκκίνηση της εφαρµογής. Η κλάση Main είναι τύπου JFrame και στη main µέθοδο, την µέθοδο που καλείται όταν τρέχει η κλάση αυτή καλείται ο constructor της Main, ο οποίος αρχικοποιεί τον ScoreEditor και κατόπιν τον τοποθετεί στην οθόνη του χρήστη. public class Main extends javax.swing.JFrame { /** Creates new form Main */ public Main() { initComponents(); ScoreEditor score = new ScoreEditor(); getContentPane().add(score, BorderLayout.CENTER); pack(); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc=" Generated Code "> private void initComponents() { setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE ); pack(); }// </editor-fold> /** * @param args the command line arguments */ public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new Main().setVisible(true); } }); } // Variables declaration - do not modify // End of variables declaration } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 46 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5.2 Αρχεία: DPianoStave,Dstave,DGrandStave,DTrebleStave Ας δούµε τα επόµενα 4 αρχεία µε χρήση ενός λογισµικού που δηµιουργεί και παρουσιάζει UML Diagrams [1] το Rational Rose [23]. DPianoStave DGrandStave DStave DTrebleStave Σχήµα 5-4: Uml διάγραµµα κλάσεων των staves του score editor. Τα παραπάνω 4 αρχεία παρουσιάζονται µαζί για να εξηγηθεί η τεχνοτροπία και ο αρχικός σχεδιασµός τους. Παρατηρούµε ότι υπάρχουν 3 διαφορετικές παρτιτούρες. Η πρώτη για πιάνο, η δεύτερη για stave και η τρίτη για τρέµπλο. Οι τρεις παραπάνω κλάσεις (βλέπε Σχήµα 5-4) που αναπαριστούν παρτιτούρες υλοποιούν το αντικείµενο DStave. Εδώ πρέπει να παρουσιάσουµε µια από τις ιδιότητες της Java καθώς και όλων των αντικειµενοστραφών γλωσσών προγραµµατισµού. Η ιδιότητα αυτή ονοµάζεται κληρονοµικότητα [24] ή inheritance στα αγγλικά. Η ιδιότητα αυτή µπορεί να περιγραφεί µε ένα απλό παράδειγµα. Ας υποθέσουµε ότι θέλουµε να περιγράψουµε όλους τους πολιτισµούς του γαλαξία µας και ότι υπάρχουν άνθρωποι, αρειανοί και δεκάδες άλλοι πολιτισµοί. Στην αντικειµενοστραφή γλώσσα πρέπει να δηµιουργήσουµε ένα αντικείµενο για κάθε είδος πολιτισµού. Μπορούµε εύκολα να παρατηρήσουµε ότι όλα τα όντα έχουν κάποια κοινά χαρακτηριστικά, όπως όνοµα, ηλικία και φύλλο. Αντί λοιπόν στο κάθε αντικείµενο να έχουµε τον παρακάτω κώδικα: // Variables int age; String name; String sex; // Setters & Getters public int getAge() { return age; } public void setAge(int age) { this.age = age; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 47 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } Τον τοποθετούµε σε ένα αντικείµενο το οποίο ονοµάζουµε On.java για παράδειγµα. Για κάθε πολιτισµό χρειάζεται ένα αντικείµενο. Το κάθε αντικείµενο κάνει extend το αντικείµενο «µπαµπά» (το parent object On.java) και «κληρονοµεί» τον κώδικα που παρουσιάσαµε παραπάνω. Έτσι αποφεύγεται η ύπαρξη του ίδιου κώδικα σε 10αδες ή ακόµα και εκατοντάδες αρχείααντικείµενα Java. Το χαρακτηριστικό της κληρονοµικότητας έχει χρησιµοποιηθεί και στον αρχικό scoreeditor. Τα κοινά χαρακτηριστικά όλων των παρτιτούρων έχουν τοποθετηθεί στο αρχείο DStave.java και κάθε ιδιαίτερη παρτιτούρα κάνει extend το αρχείο αυτό και κληρονοµεί τον κώδικα του. Έτσι στο DStave.java έχουµε τα ακόλουθα attributes τα οποία είναι κοινά για όλα τα πεντάγραµµα και απεικονίζονται στο Σχήµα 5-5. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 48 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Το Dstave είναι ένα αρκετά περίπλοκο αντικείµενο µε 650 γραµµές κώδικα, δεκάδες µεταβλητές και µεθόδους. Ένα από τα χαρακτηριστικά του είναι ότι περιέχει 33 µεταβλητές τύπου java.awt.Image. Περιέχει µεταβλητές για τα offset, τα margin, το µέτρο, το κλειδί, το τίτλο, µεθόδους για το pitch, το beat για προσθήκη και αφαίρεση παύσεων, για διαγραφή της τελευταίας νότας και άλλες χρήσιµες µεταβλητές και µεθόδους. Πρέπει να τονισθεί µια από τις µεταβλητές που περιέχει και είναι τύπου jm.music.data.Phrase. Μέσα σε αυτή τη µεταβλητή τοποθετείται το JMusicString, ένα String στο notation της βιβλιοθήκης JMusic που περιέχει τα µουσικά δεδοµένα. Ένας αποτελεσµατικότερος τρόπος σχεδίασης του DStave θα ήταν τοποθετώντας σε άλλα αντικείµενα µέρος του κώδικα ώστε να υπάρχουν περισσότερα, αλλά µικρότερα σε όγκο αντικείµενα, όπου το καθένα θα περιέχει συγκεκριµένη δόµη και χρήση. Σχήµα 5-5: Attributes του DStave.java. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 49 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Μελετώντας τα child-objects παρατηρούµε ότι το κάθε ένα περιέχει περίπου 300 γραµµές κώδικα εκ των οποίων οι 250 βρίσκονται στη µέθοδο paint και αφορούν την εµφάνιση της παρτιτούρας στην οθόνη, και την αντίστοιχη θέση του κάθε µουσικού συµβόλου. Τέλος, να σχολιαστεί ότι στην εφαρµογή ενώ το JComboBox δείχνει 4 παρτιτούρες (βλέπε Σχήµα 5-6) Σχήµα 5-6: Απεικόνιση του JComboBox του score editor. Στην πράξη υπάρχουν µόνο 3 διαφορετικές παρτιτούρες. if(item.equalsIgnoreCase("Grand Stave")){ scorePanel.remove(stave); stave = new DGrandStave(phr); scorePanel.add(stave,BorderLayout.CENTER); }else if(item.equalsIgnoreCase("Piano Stave")){ scorePanel.remove(stave); stave = new DPianoStave(phr); scorePanel.add(stave,BorderLayout.CENTER); }else if(item.equalsIgnoreCase("Treble Stave")){ scorePanel.remove(stave); stave = new DTrebleStave(phr); scorePanel.add(stave,BorderLayout.CENTER); }else if(item.equalsIgnoreCase("Bass Stave")){ scorePanel.remove(stave); stave = new DPianoStave(phr); scorePanel.add(stave,BorderLayout.CENTER); } Το DPianoStave.java χρησιµοποιείται και στο ‘Bass Stave’ και στο ‘Piano Stave’. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 50 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5.3 Αρχεία: DNoteEditor και DStaveActionHandler Ξεκινώντας από το αρχείο DNoteEditor.java παρατηρούµε ότι δε χρησιµοποιήθηκε ποτέ στον score-editor, καθώς είναι commented out ο κώδικας που εµφανίζει το control που ανοίγει το παράθυρο που απεικονίζεται στο Σχήµα 5-7. Για να εµφανιστεί το παράθυρο χρειάστηκε να κάνουµε uncomment ορισµένες γραµµές κώδικα στο DStaveActionHandler. Αυτό που παρατηρούµε στο DNoteEditor.java είναι ότι εµφανίζει στοιχεία ώστε ο χρήστης να κάνει edit τις παραµέτρους µιας νότας και συγκεκριµένα: Να αλλάξει τη µουσική νότα Να τροποποιήσει την τιµή του ρυθµού της νότας Να αλλάξει την ένταση µε την οποία θα παίξει η νότα Να αλλάξει το duration της Να αλλάξει το offset Να αλλάξει το pan Ο κώδικας αυτού του αρχείου είναι µέρος της βιβλιοθήκης JMusic. Σχήµα 5-7: Παράθυρο επεξεργασίας των παραµέτρων µιας νότας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 51 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» O κώδικας που έπρεπε να γίνει uncomment για να εµφανιστεί ο DNoteEditor ακολουθεί. Το DStaveActionHandler.java χρησιµοποιείται από το parent-class DStave.java και αποτελεί τον ActionHandler της παρτιτούρας. Περιέχει τον κώδικα που κάνει control την εφαρµογή, δηλαδή χειρίζεται τα mouse-clicks, υλοποιεί το drag & drop και περιέχει κώδικα για τα κουµπιά και τις επιλογές της εφαρµογής. Επίσης περιέχει κώδικα που εµφανίζει το Popup menu που φαίνεται στο Σχήµα 5-8. Σχήµα 5-8: Popup menu του score editor. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 52 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5.4 Αρχεία: Dot1.java, Main.form, ScoreEditor.form Το αρχείο Dot1.java περιέχει κώδικα που καλεί το View.notate(), µια µέθοδο της JMusic βιβλιοθήκης που παρουσιάζει στο χρήστη µια φράση του JMusic notation και το ολοκληρωµένο γραφικό περιβάλλον που παρέχει η κλάσση View το οποίο απεικονίζεται στο Σχήµα 5-9. package diamouses.ui.scoreEditor; import jm.music.data.*; import jm.JMC; import jm.util.*; public class Dot1 implements JMC { public static void main(String[] args) { Note n; n = new Note(C4, QUARTER_NOTE); Phrase phr = new Phrase(); phr.addNote(n); //View.sketch(phr); View.notate(phr); //View.histogram(new Score(new Part(phr))); } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 53 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 5-9: Γραφικό περιβάλλον κλάσης View. Τα δύο αρχεία τύπου .form περιέχουν User Interface δεδοµένα, όπως αυτά σχεδιάστηκαν από το γραφικό περιβάλλον του εργαλείου Netbeans. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 54 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 5.5 Αρχείο: ScoreEditor.java To αρχείο ScoreEditor αν και περιέχει 600 γραµµές κώδικα µπορεί εύκολα να κατανοηθεί. Περιέχει τον κώδικα που «χτίζει» το γραφικό περιβάλλον, εµφανίζει την επιλογή των µουσικών οργάνων όταν ο χρήστης επιλέγει ‘Select Instrument’ (βλέπε Σχήµα 5-10). Σχήµα 5-10: Παράθυρο επιλογής µουσικού οργάνου. Επίσης περιέχει control κώδικα για όλα τα κουµπιά που εµφανίζονται στο πάνω µέρος της εφαρµογής (βλέπε Σχήµα 5-11). Σχήµα 5-11: Απεικόνιση κουµπιών του score editor. Τέλος, περιέχει κώδικα που τοποθετεί 40 τυχαίες νότες στην παρτιτούρα όπως φαίνεται στο Σχήµα 5-12. Σχήµα 5-12: Κώδικας εισαγωγής τυχαίων νοτών στην παρτιτούρα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 55 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6. Υλοποίηση Στο κεφάλαιο αυτό αναλύονται τα κοµµάτια του κώδικα που υλοποιήθηκαν στα πλαίσια της πτυχιακής εργασίας, καθώς και οι τροποποιήσεις που έγιναν στον αρχικό κώδικα. Επίσης, παρουσιάζονται εργαλεία ανοιχτού κώδικα που χρησιµοποιήθηκαν όπως το Apache Ant, για τη διαδικασία packaging την εφαρµογής, και το Joomla, για την δηµιουργία της ιστοσελίδας και την ενσωµάτωση της εφαρµογής σε αυτή. 6.1 Μετρονόμος Για την υλοποίηση του µετρονόµου θα χρησιµοποιηθεί κώδικας από το project JFrets [11]. Τα απαραίτητα αρχεία για την εµφάνιση του παραθύρου του Μετρονόµου είναι τα Metronome και PlayNote. Αφού εντοπίστηκε ο κώδικας των δύο αυτών αρχείων και αντιγράφηκε στο project της πτυχιακής εργασίας, δηµιουργήθηκε ένα package ώστε να είναι τακτοποιηµένα τα αρχεία της εφαρµογής. Το νέο πακέτο ονοµάστηκε diamouses.ui.Metronome και θα περιέχει όλα τα αρχεία τα σχετικά µε το Μετρονόµο. Επίσης υλοποιήθηκε ένα νέο αρχείο στο ίδιο πακέτο µε το όνοµα TestMetronome το οποίο αρχικοποιεί το µετρονόµο για να γίνουν δοκιµές. Έτσι το τελικό αποτέλεσµα – package structure καθώς και ο κώδικας του TestMetronome.java παρουσιάζονται στο Σχήµα 6-1. Σχήµα 6-1: Package structure µετρονόµου. package diamouses.ui.Metronome; import javax.swing.JFrame; public class TestMetronome { public static void main (String [] args) { Metronome met = new Metronome(new JFrame(), null, true); met.setVisible(true); } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 56 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Στην παρακάτω εικόνα ο χρήστης επιλέγει το κουµπί Start και κατόπιν µπορεί να κάνει τις απαραίτητες ρυθµίσεις όσον αφορά στο τέµπο του µετρονόµου και στην ταχύτητά του (Beats Per Minute) όπως φαίνεται στο Σχήµα 6-2. Σχήµα 6-2: Παράθυρο µετρονόµου. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 57 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.2 Βελτίωση αρχείου: ScoreEditor.java Η κύρια δουλειά του αρχείου ScoreEditor.java είναι να εµφανίζει menu µε κουµπιά που απεικονίζεται στο Σχήµα 6-3, για επιλογή νότας ή παύσης καθώς και µουσικού οργάνου. Επίσης διαθέτει controls για επιλογή παρτιτούρας, για διαγραφή της τελευταίας νότας στην παρτιτούρα και για καθαρισµό όλης της παρτιτούρας. Σχήµα 6-3: Μενού µε κουµπιά του score editor. Η επιλογή νότας και παύσης δεν λειτουργούσε σωστά. Ήταν υλοποιηµένες σαν JToggleButtons αλλά ο χρήστης µπορούσε να κάνει πολλαπλές επιλογές και η δεύτερη νότα στο duration ήταν µόνιµα επιλεγµένη. Η διαγραφή της τελευταίας νότας κράσαρε την εφαρµογή, όταν η παρτιτούρα ήταν άδεια όπως φαίνεται στο Σχήµα 6-4. Σχήµα 6-4: Exception διαγραφής τελευταίας νότας σε άδεια παρτιτούρα. Το αρχείο ScoreEditor.java περιείχε παραπάνω από 600 γραµµές κώδικα µε δεκάδες µεταβλητές και µεθόδους, οι οποίες δεν ήταν ονοµατισµένες έτσι ώστε να αποκαλύπτουν τη χρήση τους. Η βελτίωση του αρχείου αυτού, τόσο σχεδιαστικά όσο και λειτουργικά κρίθηκε απαραίτητη. Βελτίωση 1 Ας δούµε τις δεκάδες µεθόδους jButton_ActionPerformed. Στην ισχύουσα υλοποίηση για κάθε κουµπί του παραπάνω µενού υπήρχε ο παρακάτω κώδικας: Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 58 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Και κατόπιν η αντίστοιχη µέθοδος: Αντί να έχουµε δεκάδες µεθόδους, µπορούµε να χρησιµοποιήσουµε έναν άλλο τρόπο υλοποίησης. Σε κάθε κουµπί προσθέτουµε σαν ActionListener την ίδια την κλάση και θέτουµε ένα ActionCommand, ένα string βάση του οποίου θα αναγνωρίσουµε στη συνέχεια ποιο κουµπί πατήθηκε. Εφόσον για κάθε κουµπί αντικαταστήσαµε την πρώτη από την παραπάνω µέθοδο µε τις παραπάνω δύο γραµµές και τη δεύτερη µέθοδο µε δική µας υλοποίηση που φαίνεται παρακάτω, µειώθηκε κατά 150 γραµµές το αρχείο µας και οι απαραίτητες µέθοδοι κατά 8. Βελτίωση 2 Παρατηρώντας τον κώδικα τώρα που άρχισε να είναι readable, αρκετός κώδικας επαναλαµβανόταν σε αρκετά σηµεία του αρχείου. Για παράδειγµα στα rests 1/16 , 1/8 κτλ 12 γραµµές κώδικα επαναλαµβάνονται 6 φορές, µια για κάθε rest. Η µόνη διαφορά είναι µια τιµή τύπου double. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 59 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Ο παραπάνω κώδικας µπορούσε να βελτιωθεί µε τη δηµιουργία µιας µεθόδου που παίρνει σαν παράµετρο την επίµαχη τιµή και τρέχει τις δώδεκα εντολές. Με τη δηµιουργία της παραπάνω µεθόδου µειώθηκε ο κώδικας του αρχείου ακόµα περισσότερο (50 γραµµές λιγότερες). Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 60 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Βελτίωση 3 ∆όθηκαν ονόµατα στις µεταβλητές και δηµιουργήθηκαν µέθοδοι για κάθε λειτουργία. Κατόπιν το actionPerformed, η µέθοδος που είναι υπεύθυνη για κάθε control (κουµπί της εφαρµογής) έγινε τόσο απλή και ξεκάθαρη. Βελτίωση 4 Όταν ο χρήστης προσπαθούσε να σβήσει την τελευταία νότα µιας παρτιτούρας ενώ η παρτιτούρα ήταν άδεια, η εφαρµογή κόλλαγε. Μια µικρή αλλαγή στον κώδικα (που πλέον τοποθετήθηκε σε µια µέθοδο) ήταν αρκετή για να διορθωθεί το λάθος αυτό. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 61 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Το if (phrase.getSize() > 0) ήταν αρκετό για να διορθωθεί το παραπάνω λάθος. Βελτίωση 5 ∆ηµιουργήθηκε µέθοδος για να δηµιουργεί τα κουµπιά της εφαρµογής. Ξανά κώδικας επαναλαµβανόταν για κάθε κουµπί του ScoreEditor. /** * Method creates a "smart" JToggleButton * @param imageName The location of the image of this toggle-button * @param selected_imageName The location of the image of this toggle-button * when the toggle button is selected * @param actionCommand The action-command to set for this components * @param toolTipText the tooltip of the component * @param altText Alternative text in case the image is missing * @return A JToggleButton component */ protected JToggleButton makeToggleButton(String imageName, String selected_imageName, String actionCommand, String toolTipText, String altText) { // Look for the image. String imgLocation = "images/menu_images/" + imageName + ".gif"; String imgLocation2 = "images/menu_images/" + selected_imageName + ".gif"; URL imageURL = Main.class.getResource(imgLocation); URL imageURL2 = Main.class.getResource(imgLocation2); // Create and initialize the button. JToggleButton button = new JToggleButton(); button.setActionCommand(actionCommand); button.setToolTipText(toolTipText); button.addActionListener(this); if ( (imageURL != null) & (imageURL2 != null) ){ // image found button.setIcon(new ImageIcon(imageURL, altText)); button.setSelectedIcon(new ImageIcon(imageURL2, altText)); } else { // no image found button.setText(altText); System.err.println("Resource not found: " + imgLocation + "" + (imageURL==null) + " " + (imageURL2==null)); } return button; } Γενικά το αρχείο ScoreEditor.java είχε παραπάνω από 600 γραµµές κώδικα. Μετά από σειρά αλλαγών και βελτιώσεων κατέληξε να κάνει ακριβώς την ίδια δουλειά πολύ πιο αποτελεσµατικά τόσο από πλευράς χρήσης µνήµης όσο και από λειτουργικότητα µε λιγότερο από 300 γραµµές κώδικα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 62 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.3 Βελτίωση αρχείου: Main.java Μια αδυναµία που εντοπίζεται αµέσως στο αρχείο αυτό, είναι ότι το Look & Feel είναι το default της Java. Γνωρίζοντας ότι η Java παρέχει δυνατότητες να προσοµοιάσει το γραφικό περιβάλλον πολλών λειτουργικών συστηµάτων µπορούµε να βελτιώσουµε αισθητά το Look & Feel της εφαρµογής προσθέτοντας την παρακάτω µέθοδο, και καλώντας την στη main προτού εµφανίσουµε το γραφικό περιβάλλον. /** * setDefault() , Sets the default Look and Feel. * On Microsoft Windows platforms, this specifies the Windows Look & Feel. * On Mac OS platforms, this * specifies the Mac OS Look & Feel. On Sun platforms, it specifies the CDE/Motif * Look & Feel. * * With JDK1.4.2 upwards it sets the 'Luna' look and feel in Windows XP and * 'Bluecurve' in GNOME. */ public static void setDefault() { try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); } catch (java.lang.ClassNotFoundException classE) { // Handle exception } catch (java.lang.InstantiationException insE) { //Handle exception } catch (java.lang.IllegalAccessException illE) { //Handle exception } catch (javax.swing.UnsupportedLookAndFeelException unE) { //Handle exception } } Η παραπάνω µέθοδος «κοιτάει» σε ποιο λειτουργικό σύστηµα τρέχει η εφαρµογή και θέτει το κατάλληλο Look & Feel. Σχήµα 6-5: Look & Feel εφαρµογής. Στο Σχήµα 6-5 παρουσιάζεται στο πάνω µέρος το νέο Look & Feel που µοιάζει περισσότερο σε αυτό που χρησιµοποιούν τα Windows XP/Vista από το αρχικό το οποίο φαίνεται από κάτω σαν µέτρο σύγκρισης. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 63 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Στο αρχείο αυτό προστέθηκαν επίσης αρκετές γραµµές κώδικα (περίπου 150), µε σκοπό τη δηµιουργία µενού επιλογών. Στο µενού File υπάρχουν επιλογές για ‘Καθαρισµό Παρτιτούρας’ και ‘Παίξιµο παρτιτούρας’ όπως φαίνεται στο Σχήµα 6-6. Σχήµα 6-6: Μενού File. Στο Σχήµα 6-7 απεικονίζεται το µενού Tools όπου υπάρχει η επιλογή για τον ‘Μετρονόµο’. Σχήµα 6-7: Μενού Tools. Ενώ στο µενού Help υπάρχει η επιλογή ‘About’(Σχήµα 6-8). Σχήµα 6-8: Μενού Help. Όταν ο χρήστης επιλέξει το Help About εµφανίζεται στο χρήστη το µήνυµα που παρουσιάζεται στο Σχήµα 6-9. Σχήµα 6-9: Μήνυµα στο ‘About’. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 64 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Τα short-cuts που εµφανίζονται στο About Dialog λειτουργούν κανονικά στην εφαρµογή καθώς υλοποιήθηκαν στο Main.java O κώδικας του Main.java αρχικοποιεί την εφαρµογή. Είναι τύπου JFrame και στη main µέθοδο, την µέθοδο που καλείται όταν τρέχει η κλάση αυτή καλείται ο constructor της Main, ο οποίος θέτει τίτλο στο JFrame αρχικοποιεί τις µεταβλητές και κατόπιν τοποθετεί τα τρία κοµµάτια που αποτελούν την εργασία αυτή (το πάνελ επιλογών, το score που είναι η παρτιτούρα και την εικονική κιθάρα) στην οθόνη του χρήστη. /** * Constructor */ public Main() { this.setTitle("Πτυχιακή ΤΕΙ Κρήτης Γρηγορακάκη Αιµιλία"); initComponents(); score = new ScoreEditor(); setJMenuBar(getJMenuBar()); Ευθυµίου Μιχαλίτσα, Toolkit kit = getToolkit(); Dimension screenSize = kit.getScreenSize(); int screenWidth = screenSize.width; //-System.out.println("screenwidth" + screenWidth); GuitarNeck ff = GuitarNeck.getInstance(screenWidth); getContentPane().add(score, BorderLayout.CENTER); getContentPane().add(score.getJToolBar(), BorderLayout.PAGE_START); getContentPane().add(ff, BorderLayout.SOUTH); setVisible(true); setExtendedState(JFrame.MAXIMIZED_HORIZ); pack(); } Το πάνελ επιλογών παρουσιάστηκε στο προηγούµενο κεφάλαιο 7.2. Η εικονική κιθάρα και η παρτιτούρα θα παρουσιαστούν σε επόµενα κεφάλαια. Η άλλη εργασία που κάνει η Main είναι η προσθήκη του Μενού Επιλογών. Η µέθοδος που δηµιουργεί το µενού επιλογών ακολουθεί: /** * Creates a JMenuBar (File/Tools/Help) with appropriate * menu items */ public JMenuBar getJMenuBar() { // Local Variables JMenuBar menuBar; JMenu menu; JMenuItem menuItem; // Create the menu bar. menuBar = new JMenuBar(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 65 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // 1. Build the 'File' menu. menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menuBar.add(menu); // 'Clear Stave' MenuItem menuItem = new JMenuItem("Clear Stave", KeyEvent.VK_N); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,Action Event.CTRL_MASK)); menuItem.setActionCommand("Clear_Stave"); menuItem.addActionListener(this); menu.add(menuItem); Κάθε µενού αποτελείται από menu-items. Τα menu-items έχουν µια ονοµασία, «ακούν» σε ένα KeyEvent και έχουν κάποιο Accelarator. Με τη χρήση των accelerators ο χρήστης µπορεί να πατήσει ένα συνδυασµό πλήκτρων στο πληκτρολόγιό του π.χ. Ctrl-P για να κάνει Play το tune της παρτιτούρας. Το κάθε menu-item κάνει register στον ActionListener o οποίος περιέχει τον απαραίτητο κώδικα για την υλοποίηση της επιλογής του χρήστη. Για παράδειγµα για την αποθήκευση της παρτιτούρας ως MIDI αρχείο στο δίσκο απαιτείται ο παρακάτω κώδικας. if (action_command.equals("Save_Stave")) { JFileChooser fc = new JFileChooser(new File(filename)); // Show save dialog; this method does not return until the dialog is closed fc.showSaveDialog(this); File selected_file = fc.getSelectedFile(); System.out.println("Going to save score into file : " + selected_file.toString()); Score s = new Score(new Part(score.stave.getPhrase())); Write.midi(s, selected_file.toString()); } Κώδικας από [30] και [31]. Εµφανίζει στο χρήστη το παράθυρο επιλογής αρχείου(βλέπε Σχήµα 6-10) για αποθήκευση και κατόπιν αποθηκεύει σε αυτό το αρχείο το MIDI που αντιστοιχεί στην παρτιτούρα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 66 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-10: Παράθυρο επιλογής αρχείου για αποθήκευση. Μετά την επιλογή του φακέλου αλλά και του ονόµατος του αρχείου που έχει την κατάληξη .midi εµφανίζεται στην κονσόλα το µήνυµα: Going to save score into file : C:\ test2.midi ----------------------- Writing MIDI File -----------------------------Converting to SMF data structure... Part 0 'Untitled Part' to SMF Track on Ch. 0: Phrase 0:...... MIDI file 'C:\test2.midi' written from score 'Untitled Score' in 0.074 seconds. ------------------------------------------------------------------------ Και ένα valid αρχείο µε κατάληξη .midi δηµιουργείται και µπορούµε να το ακούσουµε µε οποιαδήποτε media player (Winamp, Windows Media Player, VLC, Real Audio κτλ) Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 67 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.4 Αρχείο: Constants.java Το αρχείο Constants.java (50 γραµµές) υλοποιήθηκε ως βοηθητικό αρχείο για τη διευκόλυνση του loading αρχείων εικόνας. Στο Σχήµα 6-11 παρουσιάζονται οι εικόνες που αποφασίστηκε να χρησιµοποιηθούν στη συνέχεια της εργασίας για να εµφανίζονται στο µπράτσο της κιθάρας στην αντίστοιχη θέση. ∆ηλαδή όταν ο χρήστης επιλέγει την 3η θέση της 2ης χορδής της κιθάρας, η αντίστοιχη νότα θα «ζωγραφίζεται» στο αντίστοιχο σηµείο της κιθάρας. Για προγραµµατιστική διευκόλυνση όλα τα paths των εικόνων, αλλά και ο κώδικας για να τις φορτώνει τοποθετήθηκε στο αρχείο Constants.java Σχήµα 6-11: Οι εικόνες του Constants.java. package diamouses.ui.scoreEditor; import javax.swing.ImageIcon; public final class Constants { public ImageIcon a; public ImageIcon asharp; public ImageIcon b ; public ImageIcon c; public ImageIcon csharp; public ImageIcon d; public ImageIcon dsharp; public ImageIcon e; public ImageIcon f; public ImageIcon fsharp; public ImageIcon g; public ImageIcon gsharp; public Constants() { a = new ImageIcon(getClass().getResource("images/note_images/a.png")); asharp = new ImageIcon(getClass().getResource("images/note_images/asharp.png")); b = new ImageIcon(getClass().getResource("images/note_images/b.png")); c = new ImageIcon(getClass().getResource("images/note_images/c.png")); csharp = new ImageIcon(getClass().getResource("images/note_images/csharp.png")); d = new ImageIcon(getClass().getResource("images/note_images/d.png")); dsharp = new ImageIcon(getClass().getResource("images/note_images/dsharp.png")); e = new ImageIcon(getClass().getResource("images/note_images/e.png")); f = new ImageIcon(getClass().getResource("images/note_images/f.png")); fsharp = new ImageIcon(getClass().getResource("images/note_images/fsharp.png")); g = new ImageIcon(getClass().getResource("images/note_images/g.png")); gsharp = new ImageIcon(getClass().getResource("images/note_images/gsharp.png")); e.setDescription("e"); f.setDescription("f"); fsharp.setDescription("fsharp"); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 68 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» g.setDescription("g"); gsharp.setDescription("gsharp"); a.setDescription("a"); asharp.setDescription("asharp"); b.setDescription("b"); c.setDescription("c"); csharp.setDescription("csharp"); d.setDescription("d"); dsharp.setDescription("dsharp"); } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 69 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.5 Αρχείο: GuitarNeck.java Ένας από τους αρχικούς στόχους της πτυχιακής αυτής εργασίας ήταν η εµφάνιση µέρους της κιθάρας και συγκεκριµένα του µπράτσου της και η υλοποίηση κατάλληλης διεπαφής για την αλληλεπίδραση του χρήστη µε την εικονική απεικόνιση της κιθάρας. Επίσης ένας άλλος στόχος της εφαρµογής της πτυχιακής ήταν η αλληλεπίδραση µεταξύ της παρτιτούρας και της εικονικής κιθάρας. Στο Σχήµα 6-12 εµφανίζεται η εικονική κιθάρα όπως έχει υλοποιηθεί από το project JFrets. Ακριβώς πάνω από την κιθάρα αυτή εµφανίζεται το πρώτο prototype της δικής µας υλοποίησης µε έναν αριθµό κουµπιών (Java JButtons) των οποίων το πλάτος έχει ορισθεί ίσο µε το πλάτος του κάθε τάστου της κιθάρας. Σχήµα 6-12: Εικονική κιθάρα JFrets. Για επιβεβαίωση των µεγεθών των κουµπιών που υλοποιήθηκαν, κόκκινες κάθετες γραµµές εµφανίζονται κατόπιν επεξεργασίας της εικόνας µε πρόγραµµα επεξεργασίας (βλέπε Σχήµα 6-13). Σχήµα 6-13: Απεικόνιση τωνµεγεθών των κουµπιών που υλοποιήθηκαν. Επαναλαµβάνοντας το JPanel του πρώτου prototype 5 φορές (5 * 13 κουµπιά) εµφανίζεται το αποτέλεσµα του Σχήµατος 6-14 που µοιάζει να πλησιάζει την επιθυµητή µορφή. ∆ηλαδή τα κουµπιά αποτελούν τους χώρους που επιθυµούµε να αλληλεπιδρά ο χρήστης. Στόχος είναι ο χρήστης να βλέπει µόνο το µπράτσο της κιθάρας και κάνοντας mouse-click πάνω σε κάποιο σηµείο της να γνωρίζει η εφαρµογή ποια ακριβώς νότα πατήθηκε. Σχήµα 6-14: Πρώτο prototype της εικονικής κιθάρας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 70 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Τα κουµπιά ουσιαστικά θα βρίσκονται «πίσω» από την εικόνα της κιθάρας και ο τελικός χρήστης θα αγνοεί την ύπαρξη τους. Θα είναι όµως ο µηχανισµός µε τον οποίο θα καταλαβαίνει η εφαρµογή τα mouse-clicks. Τα JButtons σαν components της swing library εµφανίζονται στην οθόνη του χρήστη επικαλύπτοντας το υπάρχον JPanel. Με την παρακάτω εντολή ορίζουµε σε κάθε κουµπί να µην «γεµίζει» το content-area. ∆ηλαδή να µην κάνει paint την επιφάνεια του κουµπιού που είναι γύρω από τις εικόνες ή το κείµενο του κουµπιού. Συνήθως αυτό γίνεται σε χρώµα συµβατό µε αυτό του λειτουργικού συστήµατος που τρέχει η εφαρµογή. button.setContentAreaFilled(false); Το οπτικό αποτέλεσµα απεικονίζεται στο Σχήµα 6-15. Σχήµα 6-15: ∆εύτερο prototype της εικονικής κιθάρας. Για να µην εµφανίζεται το διακοσµητικό border γύρω από τα κουµπιά η παρακάτω εντολή είναι απαραίτητη. b1.setBorderPainted(false); Το οπτικό αποτέλεσµα φαίνεται στο Σχήµα 6-16. Σχήµα 6-16: Τρίτο prototype της εικονικής κιθάρας. Θέλοντας να δοκιµάσουµε να εµφανίσουµε µια εικόνα πίσω από τα κουµπιά της παραπάνω εικόνας υλοποιήθηκε η παρακάτω µέθοδος που πρέπει να εµφανίσει ένα µπλε οβάλ σχήµα στο background. public void paintComponent(Graphics g) { int width = getWidth(); int height = getHeight(); g.setColor(Color.blue); g.drawOval(0, 0, width, height); } Η µπλε γραµµή σε οβάλ σχήµα δεν εµφανίζεται. Κάνοντας όµως .setOpaque(false) όλα τα JPanels που περιέχουν τα κουµπιά: Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 71 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» string_1.setOpaque(false); string_2.setOpaque(false); string_3.setOpaque(false); string_4.setOpaque(false); string_5.setOpaque(false); Στο Σχήµα 6-17 βλέπουµε ότι το background πλέον εµφανίζεται και όλα τα components που χρησιµοποιήσαµε είναι TRANSPARENT (διαφανή). Σχήµα 6-17: Απεικόνιση οβάλ σχήµατος στο prototype της εικονικής κιθάρας. ∆ιαθέτοντας την απαραίτητη τεχνογνωσία, ήρθε η ώρα να κάνουµε την πλήρη υλοποίηση της εικονικής κιθάρας. Ξεκινώντας από το wood texture που απεικονίζεται στο Σχήµα 618. Σχήµα 6-18: Wood texture ταστίερας της κιθάρας. Και µε ορισµένη δουλειά σε πρόγραµµα επεξεργασίας εικόνας καταλήγουµε στο Σχήµα 619 που απεικονίζει ένα ξύλινο µπράτσο κιθάρας µε 6 χορδές και τα ανάλογα τάστα. Σχήµα 6-19: Εικονική αναπαράσταση µπράτσου κιθάρας. Η παραπάνω εικόνα θα χρησιµοποιηθεί σαν background για την υλοποίηση της εικονικής κιθάρας. ∆ηµιουργώντας ξανά τα απαραίτητα κουµπιά της εφαρµογής έχουµε το αποτέλεσµα που φαίνεται στο Σχήµα 6-20. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 72 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-20: Πάνελ κουµπιών εικονικής κιθάρας. Εµφανίζουµε το background image µε τον παρακάτω κώδικα πίσω από τα κουµπιά αυτά: /** * Method Paint the Guitar Neck on the Background of this JPanel. */ public void paintComponent(Graphics g) { String imgLocation2 = "images/GuitarNeck.png"; URL imageURL = GuitarNeck.class.getResource(imgLocation2); ImageIcon ii = new ImageIcon(imageURL, "altText"); g.drawImage(ii.getImage(), 0,0,this.getSize().width,this.getSize().height, this); } έχουµε το οπτικό αποτέλεσµα που παρουσιάζεται στο Σχήµα 6-21. Σχήµα 6-21: Πάνελ εικονικής κιθάρας. Μπροστά από την κάθε θέση της κιθάρας «κρύβεται» ένα διαφανές JButton. Έχοντας κατά νου τις θέσεις της κιθάρας και τις νότες που αντιστοιχούν (βλέπε Σχήµα 622), και κάνοντας χρήση της κλάσης Constants.java που παρουσιάστηκε νωρίτερα θέτουµε σαν IconImages το σύµβολο της κατάλληλης νότας στο κατάλληλο σηµείο. Σχήµα 6-22: Νότες στην ταστιέρα της κιθάρας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 73 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-23: ∆ιεπαφή εικονικής κιθάρας. Υλοποιώντας τα παραπάνω σαν JToggleButtons αντί για απλά JButtons και θέτοντας την κατάλληλη εικόνα για τα «πατηµένα» κουµπιά και καµία εικόνα για τα «µη-πατηµένα» κουµπιά, έχουµε µια πρώτη διεπαφή χρήστη (βλέπε Σχήµα 6-23). Η υλοποίηση του κώδικα ακολούθησε τις αρχές του αντικειµενοστραφούς προγραµµατισµού. Αντί να επαναλαµβάνεται κώδικας µε πλήθος συντεταγµένων υλοποιήθηκαν ορισµένες µέθοδοι (4 µέθοδοι) που η κάθε µια κάνει συγκεκριµένη εργασία. 1. Η µέθοδος getIconsForString(int string) παίρνει σαν παράµετρο την χορδή της κιθάρας, τιµές από ένα (1) µέχρι και έξι (6) και επιστρέφει πίνακα αντικειµένων javax.swing.ImageIcon µε τις εικόνες των νοτών. /** * Method getIconsForString returns an Array [] of ImageIcons * with the images of the appropriate Notes for each string of * the Guitar. * * @param string An integer from 1 to 6 representing the First..Sixth string * of a Guitar. * @return an ImageIcon [] array. */ private ImageIcon [] getIconsForString( int string ) { Constants c = new Constants(); switch (string) { case 1: ImageIcon [] string_1_notes = {c.e, c.f, c.fsharp, c.g, c.gsharp,c.a,c.asharp,c.b, c.c, c.csharp,c.d, c.dsharp,c.e,c.f, null, null, null, null,null,null,null,null}; return string_1_notes; case 2: ImageIcon [] string_2_notes = {c.b, c.c,c.csharp,c.d, c.dsharp,c.e,c.f, c.fsharp, c.g, c.gsharp, c.a,c.asharp,c.b, c.c,null,null, null, null,null,null,null,null}; return string_2_notes; case 3: ImageIcon [] string_3_notes = {c.g, c.gsharp,c.a,c.asharp,c.b, c.c,c.csharp,c.d, c.dsharp, c.e,c.f,c.fsharp, c.g, c.gsharp,null,null, null, null,null,null,null,null}; return string_3_notes; case 4: ImageIcon [] string_4_notes = {c.d, c.dsharp, c.e,c.f,c.fsharp, c.g, c.gsharp, c.a,c.asharp,c.b, c.c, c.csharp,c.d, c.dsharp,null,null, null, null,null,null,null,null}; return string_4_notes; case 5: ImageIcon [] string_5_notes = {c.a,c.asharp,c.b, c.c,c.csharp,c.d, c.dsharp, c.e,c.f,c.fsharp, c.g, c.gsharp,c.a, c.asharp,null,null, null, null,null,null,null,null}; return string_5_notes; case 6: ImageIcon [] string_6_notes = { c.e,c.f,c.fsharp, c.g, c.gsharp,c.a, c.asharp,c.b, c.c,c.csharp,c.d, c.dsharp, c.e,c.f,null,null, null, null,null,null,null,null}; return string_6_notes; default: System.out.println("Errornous string " + string + " in method GuitarFrame.getIconsForString"); return null; } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 74 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 2. Η µέθοδος paintComponent() που «ζωγραφίζει» το background της εικονικής κιθάρας: /** * Method Paint the Guitar Neck on the Background of this JPanel. */ public void paintComponent(Graphics g) { String imgLocation2 = "images/GuitarNeck.png"; URL imageURL = GuitarNeck.class.getResource(imgLocation2); ImageIcon ii = new ImageIcon(imageURL, "altText"); g.drawImage(ii.getImage(), 0,0,this.getSize().width,this.getSize().height, this); } 3. Η µέθοδος setSizes() που δίνει στα διάφανα κουµπιά το κατάλληλο µέγεθος ώστε να πιάνουν τα mouse-clicks του χρήστη µε όσο το δυνατόν µεγαλύτερη πιστότητα. /** * This method sets the sizes of the JToggleButtons */ private void setSizes () { int width; for (int i = 0; i <= 21; i++) { switch (i) { case 0: width = 83; break; case 1: width = 89; break; case 2: width = 82; break; case 3: width = 77; break; case 4: width = 75; break; case 5: width = 66; break; case 6: width = 64; break; case 7: width = 59; break; case 8: width = 55; break; case 9: width = 54; break; case 10: width = 49; break; case 11: width = 46; break; case 12: width = 43; break; case 13: width = 40; break; case 14: width = 38; break; case 15: width = 35; break; case 16: width = 32; break; case 17: width = 30; break; case 18: width = 28; break; case 19: width = 26; break; case 20: width = 22; break; default: width = 12; break; } width+=2; width = screen_width*width/1280; string_1_buttons[i] .setPreferredSize(new Dimension(width, string_2_buttons[i] .setPreferredSize(new Dimension(width, string_3_buttons[i] .setPreferredSize(new Dimension(width, string_4_buttons[i] .setPreferredSize(new Dimension(width, string_5_buttons[i] .setPreferredSize(new Dimension(width, string_6_buttons[i] .setPreferredSize(new Dimension(width, } } height)); height)); height)); height)); height)); height)); Μάλιστα στην παραπάνω µέθοδο πέρα από το γεγονός ότι τα διάφανα κουµπιά αποκτούν το κατάλληλο πλάτος σε pixels µε ξεκάθαρο τρόπο έχει υλοποιηθεί δυνατότητα για να µπορεί η εφαρµογή να τρέχει άψογα σε όλες τις δυνατές αναλύσεις. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 75 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Με άλλα λόγια η εικόνα που χρησιµοποιήθηκε για την εφαρµογή µας έχει διαστάσεις 1517 x 144 εικονο-στοιχεία (pixels) έτσι ώστε η εφαρµογή να είναι άριστη οπτικά ακόµα και σε αναλύσεις 1600x1200 ή και µεγαλύτερες. Επειδή όµως η ανάπτυξη της εφαρµογής έγινε σε οθόνη µε ανάλυση 1280x800 και το πλάτος των κουµπιών σε pixels µετρήθηκε στην ανάλυση αυτή, έπρεπε να προβλεφθεί χρήση σε διαφορετικές αναλύσεις. Έτσι µε έξυπνο τρόπο υλοποιήθηκε και δοκιµάστηκε η εφαρµογή και σε άλλες αναλύσεις µε επιτυχία. 4. Μέθοδος private JPanel string_buttons, int string). getStringPanel(JToggleButton [] /** * Method returns a JPanel with JButtons for each note of the Guitar * @param string First..Sixth string of the guitar * @return a JPanel with appropriate JButtons */ private JPanel getStringPanel(JToggleButton [] string_buttons, int string) { JPanel main_panel = new JPanel(); main_panel .setLayout(new FlowLayout()); ImageIcon [] string_images = getIconsForString(string); for (int i = 0; i <= 21; i++) { string_buttons[i] = new JToggleButton(); string_buttons[i].setContentAreaFilled(false); string_buttons[i].setBorderPainted(false); string_buttons[i].setOpaque(false); string_buttons[i].setIcon(new ImageIcon()); string_buttons[i].setSelectedIcon(string_images[i]); main_panel.add(string_buttons[i]); } return main_panel; } H παραπάνω µέθοδος παίρνει σαν παραµέτρους ένα uninitialized πίνακα από Buttons και τον αριθµό µιας χορδής. Περιέχει κώδικα για να αρχικοποιεί τα κουµπιά και καθορίζει τις παραµέτρους για να είναι διαφανή (transparent) τα κουµπιά αυτά. Τοποθετώντας τον κώδικα της εφαρµογής σε µεθόδους έχουµε τη δυνατότητα εύκολα να µετατρέψουµε τα components από JButtons σε JToggleButtons ή JLabels για να πειραµατιστούµε και να ανακαλύψουµε την ιδανικότερη υλοποίηση. Αφού έχουν υλοποιηθεί όλες οι απαραίτητες µέθοδοι παρουσιάζουµε τις µεταβλητές της κλάσης και τον constructor που αρχικοποιεί το νέο αυτό αντικείµενο-component. Οι µεταβλητές του αντικειµένου είναι: /** * Global variables for the application */ Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 76 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private int height = 32; private int screen_width; private private private private private private JToggleButton JToggleButton JToggleButton JToggleButton JToggleButton JToggleButton [] [] [] [] [] [] string_1_buttons string_2_buttons string_3_buttons string_4_buttons string_5_buttons string_6_buttons = = = = = = new new new new new new JToggleButton JToggleButton JToggleButton JToggleButton JToggleButton JToggleButton [22]; [22]; [22]; [22]; [22]; [22]; Ο αριθµός των global µεταβλητών είναι µικρός και µε τέτοιο τρόπο ώστε να παραµετροποιείται εύκολα το αντικείµενο. To height είναι το ύψος των κουµπιών (ή των χορδών) σε pixels, το screen_width είναι το πλάτος της εφαρµογής σε pixes και οι έξης πίνακες µε JToggleButtons περιέχουν τα διάφανα κουµπιά για κάθε µια από τις χορδές της κιθάρας. Τέλος, ο constructor του αντικειµένου είναι ο εξής: /** * Constructor. * Initializes 6 JPanels, one for each guitar string */ public GuitarNeck(int screen_width) { this.screen_width = screen_width; // 1. Create six JPanels JPanel string_1 = getStringPanel(string_1_buttons,1); JPanel string_2 = getStringPanel(string_2_buttons,2); JPanel string_3 = getStringPanel(string_3_buttons,3); JPanel string_4 = getStringPanel(string_4_buttons,4); JPanel string_5 = getStringPanel(string_5_buttons,5); JPanel string_6 = getStringPanel(string_6_buttons,6); // 2. Make them Not-Opaque (Make them TRANSPARENT) string_1.setOpaque(false); string_2.setOpaque(false); string_3.setOpaque(false); string_4.setOpaque(false); string_5.setOpaque(false); string_6.setOpaque(false); // 3. Add each of the JPanel in a stack from top to bottom setLayout(new GridLayout(0,1,0,0)); add(string_1); add(string_2); add(string_3); add(string_4); add(string_5); add(string_6); setSizes(); } Παίρνει σαν παράµετρο το µέγεθος του παραθύρου. Κατόπιν αρχικοποιεί έξι JPanels κάνοντας χρήση της µεθόδου getStringPanel και στη συνέχεια κάνει transparent (διάφανα) τα νέα αυτά JPanels. Τέλος χρησιµοποιεί τον GridLayout Layout Manager µε παράµετρους 0,1,0,0 εννοώντας τοποθέτηση όλων των components σε 1 στήλη µε border 0,0. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 77 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Κάνοντας add τα 6 JPanels, τοποθετούνται όλα τα JPanels της εφαρµογής το ένα κάτω από το άλλο και τέλος καλείται η µέθοδος setSizes() για να βελτιστοποιήσουµε την εµφάνιση της εφαρµογής σε όλες τις πιθανές αναλύσεις. Ένα ακόµα σηµείο που χρήζει αναφοράς είναι ο τρόπος µε τον οποίο θα προστεθεί αυτό το Component µέσα σε ένα JFrame. JFrame f = new JFrame(); f.setVisible(true); f.setExtendedState(JFrame.MAXIMIZED_HORIZ); Toolkit kit = f.getToolkit(); Dimension screenSize = kit.getScreenSize(); int screenWidth = screenSize.width; System.out.println("screenwidth" + screenWidth); GuitarNeck ff = new GuitarNeck(screenWidth); f.getContentPane().add(ff); f.pack(); Αφού αρχικοποιήσουµε ένα νέο JFrame καλούµε την εντολή ώστε να πάρει το µέγιστο δυνατό πλάτος το νέο παράθυρο. Κατόπιν µε χρήση του εργαλείου Toolkit ανακαλύπτουµε το πλάτος της εφαρµογής (δηλαδή της ανάλυσης που θα τρέξει στο περιβάλλον του χρήστη) και αρχικοποιούµε το αντικείµενο GuitarNeck µε την παράµετρο screenWidth. setExtendedState(JFrame.MAXIMIZED_HORIZ); Αυτό το σηµείο αξίζει αναφοράς καθώς οι Layout Managers για να θέσουν σωστά τα µεγέθη (ύψος και πλάτος) σε κάθε component (και στο αντικείµενο αυτό έχουµε να θέσουµε τα µεγέθη ύψος και πλάτος σε 132 κουµπιά, 6 χορδές επί 22) πρέπει να το κάνουν τη στιγµή που τα αρχικοποιούµε για πρώτη φορά. Για αυτό και είναι απαραίτητο να χρησιµοποιηθεί η παραπάνω τεχνική προτού αρχικοποιήσουµε το αντικείµενο µας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 78 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.6 Αρχείο: DStave.java Η πτυχιακή αυτή εργασία απαιτούσε οι νότες στο πεντάγραµµο να αποκτούν κόκκινο χρώµα όταν ο χρήστης τις επέλεγε. Η πρώτη σκέψη ήταν να δηµιουργούσαµε µια σειρά από εικόνες που αντί για µαύρο χρώµα να είχαν κόκκινο (βλέπε Σχήµα 6-24), να τις φορτώναµε στη µνήµη και να τις εµφανίζαµε όταν ο χρήστης κάνει click πάνω στην αντίστοιχη νότα. Σχήµα 6-24: Μουσικά σύµβολα που έπρεπε να επαναδηµιουργηθούν. Η παραπάνω σκέψη απορρίφτηκε αµέσως. Οι λόγοι ήταν πολλοί: 1. Η εφαρµογή θα απαιτούσε πολύ παραπάνω αποθηκευτικό χώρο τόσο στη µνήµη όσο και στον αποθηκευτικό δίσκο. 2. Τι θα συνέβαινε αν κάποιος επέλεγε να τροποποιήσει τις αρχικές εικόνες. Θα έπρεπε να τροποποιήσει και την κόκκινη εκδοχή τους. 3. Τι θα συνέβαινε αν αντί για κόκκινο χρώµα θέλαµε να εµφανίζαµε τα επιλεγµένα µουσικά σύµβολα σε ΠΡΑΣΙΝΟ ή σε ΜΠΛΕ χρώµα; Θα έπρεπε να δηµιουργηθούν νέες σειρές εικόνων, και θα απαιτούνταν περισσότερος κώδικας. Η λύση που επιλέχθηκε και µετά από αρκετή έρευνα υλοποιήθηκε ήταν η µετατροπή του RGB color model δυναµικά όταν αυτό ήταν απαραίτητο. Για να γίνει αυτό, και καθώς η εικόνα που εµφανίζεται στην παρτιτούρα είναι τύπου java.awt.Image έπρεπε να δηµιουργηθούν πρώτα δύο µέθοδοι. Η πρώτη θα µετέτρεπε το java.awt.Image σε java.awt.Image.BufferedImage και η άλλη θα έκανε την αντίστροφη διαδικασία. Ο λόγος είναι ότι η κλάση java.awt.Image είναι µια πολύ απλή υλοποίηση που δεν περιέχει µεθόδους ούτε για Alpha Pixels (Transparency) ούτε για color models (RGB / ARGB κτλ). Ας δούµε τη µέθοδο που µετατρέπει µια κλάση BufferedImage σε Image: // This method returns an Image object from a buffered image public static Image toImage(BufferedImage bufferedImage) { return Toolkit.getDefaultToolkit().createImage(bufferedImage.getSource()); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 79 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Ας δούµε και τη µέθοδο που κάνει το αντίστροφο της παραπάνω, δηλαδή από Image σε BufferedImage: // This method returns a buffered image with the contents of an image public static BufferedImage toBufferedImage(Image image) { if (image instanceof BufferedImage) { return (BufferedImage)image; } // This code ensures that all the pixels in the image are loaded image = new ImageIcon(image).getImage(); // Determine if the image has transparent pixels; for this method's // implementation, see e661 Determining If an Image Has Transparent Pixels // Create a buffered image with a format that's compatible with the screen BufferedImage bimage = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); try { // Determine the type of transparency of the new buffered image // int transparency = Transparency.OPAQUE; //boolean hasAlpha = hasAlpha(image); //if (hasAlpha) int transparency = Transparency.BITMASK; // Create the buffered image GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); bimage = gc.createCompatibleImage( image.getWidth(null), image.getHeight(null), transparency); } catch (HeadlessException e) { // The system does not have a screen } if (bimage == null) { // Create a buffered image using the default color model //int type = BufferedImage.TYPE_INT_RGB; //if (hasAlpha) int type = BufferedImage.TYPE_INT_ARGB; bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); } // Copy image to buffered image Graphics g = bimage.createGraphics(); // Paint the image onto the buffered image g.drawImage(image, 0, 0, null); g.dispose(); return bimage; } H παραπάνω µέθοδος ήταν πολύ πιο απαιτητική. Ο λόγος είναι ότι αντικείµενα τύπου Image δεν µπορούν να µετατραπούν σε αντικείµενα τύπου BufferedImage. Το πιο δυνατό ισοδύναµο είναι να δηµιουργηθεί ένα νέο BufferedImage αντικείµενο και να «ζωγραφίσουµε» προγραµµατιστικά την εικόνα του Image αντικειµένου πάνω σε αυτό, και αυτό ακριβώς υλοποιήσαµε παραπάνω. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 80 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Εφόσον οι δύο παραπάνω µέθοδοι τοποθετήθηκαν στο αρχείο DStave.java (και είναι µάλιστα στατικές µέθοδοι) µένει το κοµµάτι που όταν µια νότα επιλέγεται να µετατρέπονται τα pixel της από µαύρο χρώµα σε κόκκινο (ή κάποιο άλλο). Στη γραµµή 180 του αρχείου DGrantStave.java εντοπίζεται η εντολή g.drawImage(currImage, totalBeatWidth, pitchTempPos, this); Σε συνεργασία µε τις εντολές // draw notes and rests for (int i = 0; i < phrase.size(); i++) { int notePitchNum = (int) phrase.getNote(i).getPitch(); // choose graphic chooseImage(notePitchNum, phrase.getNote(i).getRhythmValue(), 71, 60, 50); που προϋπάρχουν και για κάθε νότα της µουσικής φράσης Phrase παίρνουν την αντίστοιχη εικόνα µε την εντολή chooseImage και την αποθηκεύουν στη µεταβλητή currImage, καταλαβαίνουµε ότι η εικόνα τύπου java.awt.Image περιέχει την εικόνα της κάθε νότας. Έτσι πριν τη γραµµή 180 στο αρχείο DGrantStave.java τοποθετούµε το δικό µας κώδικα: // Aimilia Grigorakaki – Euthimiou Michalitsa -> draw note/rest if (currImage != null) { if (i==getSelectedNote()) { Note selected_note = phrase.getNote(i);//.getPitch(); BufferedImage bi = DStave.toBufferedImage(currImage); int w = bi.getWidth(); int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, bi.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); if (pixel == (Color.BLACK).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } currImage = DStave.toImage(biOut); } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 81 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Ουσιαστικά αυτό που κάνει το παραπάνω κοµµάτι κώδικα είναι να χρησιµοποιεί τις δύο µεθόδους που δηµιουργήσαµε νωρίτερα. Αν η εικόνα δεν είναι null και είναι επιλεγµένη από το χρήστη, τότε δηµιουργούµε ένα νέο BufferedImage αντικείµενο «ζωγραφίζοντας» πάνω του το περιεχόµενο της αρχικής εικόνας όπως έχουµε περιγράψει παραπάνω. Κατόπιν ανακαλύπτουµε το width και το height της νέας εικόνας και σαρώνοντας τα pixel της εικόνας αυτής ένα-ένα, συγκρίνουµε το RGB (Red-Green-Blue) µε το RGB του µαύρου χρώµατος (Color.BLACK) και αντικαθιστούµε τα pixels που είναι µαύρου χρώµατος µε κόκκινα pixels. Όταν η διαδικασία ολοκληρωθεί, τοποθετούµε ξανά την εικόνα από την BufferedImage στη µεταβλητή currImage. Τώρα η εντολή: g.drawImage(currImage, totalBeatWidth, pitchTempPos, this); θα εµφανίσει στην οθόνη την κόκκινη εκδοχή της αντίστοιχης εικόνας για την επιλεγµένη µόνο νότα της παρτιτούρας, όπως φαίνεται στο Σχήµα 6-25. Σχήµα 6-25: Απεικόνιση επιλεγµένης νότας (αλλαγή χρώµατος). Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 82 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.7 Διάδραση ανάμεσα στην παρτιτούρα και την εικονική κιθάρα Στο αρχείο DGrandStave στην προηγούµενη ενότητα έχουµε υλοποιήσει όταν κάποια νότα επιλέγεται στην παρτιτούρα να χρωµατίζεται µε κόκκινο χρώµα. Σε αυτό ακριβώς το σηµείο του κώδικα γνωρίζουµε ποια νότα έχει επιλεγεί. Προσθέτουµε λοιπόν µια γραµµή κώδικα : // Aimilia Grigorakaki – Euthimiou Michalitsa -> draw note/rest if (currImage != null) { if (i==getSelectedNote()) { Note selected_note = phrase.getNote(i);//.getPitch(); GuitarNeck.getInstance().setNote(selected_note.getPitch()); Η τελευταία γραµµή θα εξυπηρετήσει ώστε στην εικονική κιθάρα να εµφανιστεί αµέσως η αντίστοιχη νότα στο αντίστοιχο τάστο της κιθάρας. Για να γίνει όµως αυτό, πρέπει πρώτα να εξηγήσουµε τις απαραίτητες αλλαγές στον κώδικα του κεφαλαίου 7.5 GuitarNeck.java. Η πρώτη αλλαγή είναι η µετατροπή του αρχείου αυτού σε Singleton. Singleton [27] είναι ένα Design Pattern [28] όπου αναγκάζουµε την εφαρµογή να διατηρεί ΜΟΝΟ ΕΝΑ instance του αντικειµένου αυτού. Ενώ όλα τα αντικείµενα της Java µπορούν να αρχικοποιηθούν πολλές φορές π.χ. JButton button1 = new JButton(); JButton button2 = new JButton(); Τα Singleton αντικείµενα αρχικοποιούνται µόνο µια φορά, την πρώτη φορά που τα αρχικοποιούµε. Πως το πετυχαίνουµε αυτό? Κάνουµε τον constructor του αντικειµένου private, δηλαδή ένα αντικείµενο τέτοιου τύπου µπορεί να αρχικοποιηθεί µόνο από τον ίδιο τον εαυτό του. ∆ηλαδή προγραµµατιστικά δεν µπορούµε να κάνουµε το JButton button1 = new JButton(); επειδή το ‘new JButton()’ µέρος της παραπάνω εντολής κάνει χρήση του constructor του αντικειµένου, που πλέον το µετατρέψαµε σε private. Πώς θα αρχικοποιήσουµε όµως το αντικείµενο? ∆ηµιουργούµε µια µέθοδο µε το όνοµα getInstance() η οποία αρχικοποιεί το αντικείµενο GuitarNeck. Παρακάτω φαίνεται ο απαραίτητος κώδικας: Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 83 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private static GuitarNeck reference; public static GuitarNeck getInstance() { if (reference==null) { reference = new GuitarNeck(); } return reference; } Έχουµε λοιπόν µια µεταβλητή µε το όνοµα reference. Όταν καλούµε τη µέθοδο getInstance() την πρώτη φορά το reference είναι null οπότε θα αρχικοποιηθεί µέσω του constructor το αντικείµενο. Αυτό µπορεί να γίνει και ας είναι ο constructor private επειδή ο παραπάνω κώδικας βρίσκεται µέσα στο αρχείο GuitarNeck.java To private δηλαδή «κρύβει» κάτι από όλα τα αντικείµενα εκτός από τον εαυτό του. Για να συνεχίσουµε, την δεύτερη, τρίτη κ.ο.κ. φορά που ο χρήστης καλεί τη µέθοδο getInstance, το reference δεν είναι πλέον null. Υπάρχει instance του αντικειµένου και το επιστρέφουµε. To αντικείµενο GuitarNeck µε αυτόν τον τρόπο υπάρχει καµία, ή µια φορά µόνο στη µνήµη. Αυτή η µέθοδος ήταν µια από τις πολλές που θα µπορούσαµε να χρησιµοποιήσουµε για να καταφέρουµε να υλοποιήσουµε µια σύνδεση ανάµεσα στην εικονική κιθάρα και στην παρτιτούρα. Ένας άλλος τρόπος θα ήταν να χρησιµοποιούσαµε events. Όταν δηλαδή επιλεγόταν µια νότα θα κάναµε fire ένα νέο event. Το GuitarNeck από την άλλη, θα έκανε register σαν listener για events και «ακούγοντας» το νέο αυτό event θα αντιδρούσε κατάλληλα. Επειδή όµως οι παρτιτούρες είναι πολλές ενώ η εικονική κιθάρα µια και µοναδική η λύση που επιλέξαµε µε τη µετατροπή του αντικειµένου σε Singleton καλύπτει εύκολα τις ανάγκες της εφαρµογής. Ο κώδικας του αντικειµένου GuitarNeck χρειάζεται µια νέα µέθοδο η οποία θα δέχεται σαν είσοδο το pitch της επιλεγµένης νότας της παρτιτούρας και θα εµφανίζει τις αντίστοιχες νότες στην εικονική κιθάρα. Η µέθοδος αυτή θα ονοµάζεται setNote και έχει τον παρακάτω κώδικα: public void setNote(int pitch) clearAll(); int fret_1 = (pitch-77); int fret_2 = (pitch-72); int fret_3 = (pitch-68); int fret_4 = (pitch-63); int fret_5 = (pitch-58); int fret_6 = (pitch-53); { //5 //4 //5 //5 //5 if (fret_1>=0 & fret_1<=21) { fret_buttons[0][fret_1].setSelected(true); } if (fret_2>=0 & fret_2<=21) { fret_buttons[1][fret_2].setSelected(true); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 84 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (fret_3>=0 & fret_3<=21) { fret_buttons[2][fret_3].setSelected(true); } if (fret_4>=0 & fret_4<=21) { fret_buttons[3][fret_4].setSelected(true); } if (fret_5>=0 & fret_5<=21) { fret_buttons[4][fret_5].setSelected(true); } if (fret_6>=0 & fret_6<=21) { fret_buttons[5][fret_6].setSelected(true); } } Ο παραπάνω κώδικα πρώτα από όλα καλεί τη νέα µέθοδο clearAll(); public void clearAll() { for (int string=0; string <= 5; string++) { for (int i=0; i<fret_buttons[0].length; i++) { fret_buttons[string][i].setSelected(false); } } } Η οποία αναλαµβάνει να «καθαρίσει» την εικονική κιθάρα από τις ενεργές νότες. Αφού τρέξει η µέθοδος αυτή η εικονική κιθάρα είναι έτοιµη να απεικονίσει τη νότα της παρτιτούρας. Όταν ο χρήστης επιλέγει µια νότα στην παρτιτούρα τρέχει η εντολή GuitarNeck.getInstance().setNote(selected_note.getPitch()); Έχουµε σαν παράµετρο λοιπόν το pitch της νότας και θέλουµε να ανακαλύψουµε τη θέση της κιθάρας που αντιστοιχεί στη νότα αυτή. Στην κιθάρα όµως οι νότες της παρτιτούρας αντιστοιχούν σε αρκετά πιθανά σηµεία, για παράδειγµα χρησιµοποιώντας ένα on-line εργαλείο [29] βλέπουµε ότι στη νότα Ε στην πρώτη γραµµή του πενταγράµµου στο µουσικό κλειδί ΣΟΛ αντιστοιχεί το δεύτερο τάστο της 4ης χορδής της κιθάρας καθώς και το 7ο της 5ης χορδής και το 12ο της 6ης χορδής (Σχήµα 6-26). Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 85 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-26: Αντιστοιχία νότας E της 1η γραµµής στην ταστιέρα της κιθάρας. Το ίδιο θέλουµε να πετύχουµε από την εφαρµογή µας. Έτσι όταν στην παρτιτούρα επιλέγεται η νότα Ε όπως φαίνεται στο Σχήµα 6-27, θέλουµε να εµφανίζεται η αντίστοιχη νότα στο µπράτσο της κιθάρας. Σχήµα 6-27: Αντιστοιχία νότας E της 1ης γραµµής στην εφαρµογή. Το παραπάνω επιτεύχθηκε µε τη χρήση της γνώσης του pitch της νότας. Όταν µια νότα έχει για pitch την τιµή 76 τότε αντιστοιχεί στην 1η χορδή της κιθάρας. Με τιµή 77 αντιστοιχεί στο πρώτο τάστο της 1ης χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 86 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Ακολουθώντας τους µαθηµατικούς κανόνες που συνδέουν τις νότες µε τις αντίστοιχες χορδές και τα τάστα καταλήξαµε στον απλό αλγόριθµο: int int int int int int fret_1 fret_2 fret_3 fret_4 fret_5 fret_6 = = = = = = (pitch-77); (pitch-72); (pitch-68); (pitch-63); (pitch-58); (pitch-53); //5 //4 //5 //5 //5 H θέση του τάστου (frets) που πρέπει να χρησιµοποιήσει ο καλλιτέχνης µουσικός για να παίξει µια νότα µπορεί να βρεθεί µε µια απλή αφαίρεση. Η αφαίρεση ανακαλύπτει τη θέση αυτή. Αν η αφαίρεση επιστρέψει την τιµή 0 τότε µιλάµε για την ανοιχτή χορδή. Αν η τιµή είναι µικρότερη ή ίση µε το 22 τότε υπάρχει θέση στη χορδή και η νότα εµφανίζεται στη θέση αυτή. Υπάρχουν νότες που µπορούν να παίχτουν σε περισσότερες από µια χορδές της κιθάρας. Στην περίπτωση αυτή µια από τις θέσεις αυτές είναι η σύνηθες ενώ οι άλλες θεωρούνται «alternatives» ή εναλλακτικές. Στην εφαρµογή αυτή φαίνονται όλες οι πιθανές θέσεις στην κιθάρα όταν επιλέγονται στην παρτιτούρα. Ένα θέµα που δεν έχει αντιµετωπιστεί µέχρι τώρα είναι οι ανοιχτές χορδές, και η υλοποίηση που επιλέχθηκε θα παρουσιαστεί στο επόµενο κεφάλαιο. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 87 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.8 Ανοιχτές χορδές Για τις «ανοιχτές» χορδές, όταν δηλαδή η χορδή πρέπει να παίξει χωρίς τη χρήση τάστου ένας πιο έξυπνος τρόπος υλοποίησης χρειαζόταν. Η υλοποίηση που επιλέχθηκε είναι η παρακάτω: Με χρήση του PhotoShop επεξεργαστήκαµε την εικόνα του µπράτσου της κιθάρας και επιλέξαµε συγκεκριµένα RGB χρωµάτων για κάθε χορδή ξεχωριστά. Σχήµα 6-28: Το RGB της πρώτης χορδής. Σχήµα 6-29: Το RGB της δεύτερης χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 88 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-30: Το RGB της τρίτης χορδής. Σχήµα 6-31: Το RGB της τέταρτης χορδής. Σχήµα 6-32: Το RGB της πέµπτης χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 89 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-33: Το RGB της έκτης χορδής. Έτσι η κάθε χορδή είναι ένα συγκεκριµένο RGB: 1η χορδή 241,255,255 2η χορδή 255,255,240 3η χορδή 255,255,230 4η χορδή 255,255,225 5η χορδή 255,255,220 6η χορδή 255,255,210 Χρησιµοποιώντας τις µεθόδους για color replacement που υλοποιήσαµε στο κεφάλαιο 7.6 µπορούµε πλέον να χρωµατίσουµε τις χορδές επειδή απεικονίζονται µε διακριτό RGB στην εικόνα µε ένα έντονο κόκκινο χρώµα για να καταλάβει ο χρήστης ότι πρέπει να παίξει απλά τη χορδή. Σχήµα 6-34: Απεικόνιση 1ης χορδής ανοιχτής. Σχήµα 6-35: Απεικόνιση 2ης χορδής ανοιχτής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 90 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-36: Απεικόνιση 3ης χορδής ανοιχτής. Σχήµα 6-37: Απεικόνιση 4ης χορδής ανοιχτής. Σχήµα 6-38: Απεικόνιση 5ης χορδής ανοιχτής. Σχήµα 6-39: Απεικόνιση 6ης χορδής ανοιχτής. Εφόσον η δοκιµή έγινε µε επιτυχία µε πρόχειρο κώδικα ήρθε η στιγµή να υλοποιηθεί µε δηµιουργία µεθόδων ώστε να µπορούµε εύκολα να εµφανίσουµε όποια χορδή θέλουµε. Στην αρχή του αντικειµένου τοποθετούµε τα χρώµατα που τοποθετήσαµε στις χορδές. Color Color Color Color Color Color color_1st color_2nd color_3rd color_4th color_5th color_6th = = = = = = new new new new new new Color(241,255,255); Color(255,255,240); Color(255,255,230); Color(255,255,225); Color(255,255,220); Color(255,255,210); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 91 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Κατόπιν δηµιουργούµε µέθοδο η οποία κάνει την απαραίτητη χρωµατική αλλαγή. Παίρνει σαν παράµετρο τον αριθµό της χορδής (από 1 ως 6), εντοπίζει το χρώµα της χορδής, κάνει τη χρωµατική αλλαγή και καλεί την repaint εντολή για να ανανεωθεί το γραφικό περιβάλλον της εφαρµογής. /** * Switch the color of the string * @param string values 1 to 6 */ private void switchColor(int string) { Color new_color; switch (string) { case 1: new_color = color_1st; break; case 2: new_color = color_2nd; break; case 3: new_color = color_3rd; break; case 4: new_color = color_4th; break; case 5: new_color = color_5th; break; case 6: new_color = color_6th; break; default: new_color = Color.black; } BufferedImage bi = DStave.toBufferedImage(ii.getImage()); int w = bi.getWidth(); int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, bi.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); if (pixel == (new_color).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } ii = new ImageIcon(DStave.toImage(biOut)); repaint(); } Στη συνέχεια πρέπει να προσέξουµε τη µέθοδο clearAll. Μέχρι τώρα «καθάριζε» την εικονική κιθάρα από τις νότες. Τώρα η µέθοδος αυτή θα πρέπει να την «καθαρίζει» και από τις επιλεγµένες-κοκκινισµένες χορδές. Έτσι η µέθοδος µετατρέπεται: public void clearAll() { for (int string=0; string <= 5; string++) { for (int i=0; i<fret_buttons[0].length; i++) { fret_buttons[string][i].setSelected(false); } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 92 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» String imgLocation2 = "images/GuitarNeck.png"; URL imageURL = GuitarNeck.class.getResource(imgLocation2); ii = new ImageIcon(imageURL, "altText"); repaint(); } Σε αυτό το σηµείο ολοκληρώνεται η φάση της υλοποίησης. Μάλιστα χρειάστηκαν µόλις 300 γραµµές κώδικα για την υλοποίηση τόσο της εικονικής κιθάρας όσο και της διεπαφής της µε την παρτιτούρα. Παρακάτω παρουσιάζονται παραδείγµατα χρήσης της εφαρµογής, όπως αυτή υλοποιήθηκε. Σχήµα 6-40: Αντιστοιχία νότας Ε 4ου διαστήµατος στην εφαρµογή. Στο Σχήµα 6-40 φαίνεται η αντιστοιχία του προγράµµατος που αναπτύξαµε µε τη θεωρία. Η νότα Ε µπορεί να παιχτεί σαν «ανοιχτή πρώτη χορδή» ή στην 5η θέση της δεύτερης χορδής ή στην 9η θέση της 3ης χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 93 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-41: Αντιστοιχία νότας D στην εφαρµογή. Στο Σχήµα 6-41 απεικονίζεται η αντιστοιχία της νότας D όπου η σωστή θέση της στην κιθάρα είναι η ανοιχτή 3η χορδή ή η 5η θέση της 5ης χορδής ή η 10η θέση της 6ης χορδής. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 94 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-42: Αντιστοιχία νότας C - sharp στην εφαρµογή. Επίσης εµφανίζονται σωστά και οι νότες µε σηµεία αλλοίωσης όπως π.χ. η δίεση (#) που ανεβάζει το φθόγγο ένα ηµιτόνιο. Στο σχήµα 6-42 εµφανίζονται οι θέσεις που αντιστοιχούν στην C-Sharp. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 95 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.9 Δημιουργία Ant Script Για να µπορεί η εφαρµογή να πακεταριστεί όµορφα θα πρέπει όλα τα .class αρχεία µετά το compilation να µπουν σε ένα αρχείο .JAR Για να γίνει αυτό πρέπει πρώτα από όλα να δηµιουργηθεί ένα MANIFEST αρχείο [25]. Κατόπιν µελέτης το τελικό manifest.txt ακολουθεί. Manifest-Version: 2.0 Main-Class: diamouses.ui.scoreEditor.Main Class-Path: jmusic.jar Created-By: 1.5 (Sun Microsystems Inc.) Name: Specification-Title: @Name@ Specification-Version: @version@ Specification-Vendor: @vendor@ Implementation-Vendor: Aimilia Implementation-Version: @version@ Implementation-Title: ScoreEditor Το παραπάνω αρχείο περιγράφει πού θα βρεθεί η κύρια κλάση της εφαρµογής (δηλαδή στο diamouses.ui.scoreEditor.Main) και ποιες βοηθητικές βιβλιοθήκες χρησιµοποιούνται και απαιτούνται για να τρέξει η εφαρµογή (στην εφαρµογή της πτυχιακής αυτής εργασίας, απαραίτητη είναι η ύπαρξη και χρήση της βιβλιοθήκης jmusic.jar). Εφόσον δηµιουργηθεί το MANIFEST αρχείο, ένα ακόµα θέµα είναι ότι η εφαρµογή ακολουθείται από ορισµένες εικόνες που πρέπει και αυτές να τοποθετηθούν στο .JAR Και ενώ η εφαρµογή φόρτωνε αρχικά τις εικόνες κανονικά, όταν τοποθετούνταν µέσα σε .JAR αρχείο δεν µπορούσε να τις εντοπίσει. Η λύση δόθηκε µε τη χρήση του getClass().getClassLoader().getResource(): Όταν δηλαδή η εφαρµογή αποτελείται από .class αρχεία τότε µπορούµε να χρησιµοποιήσουµε το filesystem ευθέως π.χ. URL url = “images/image.gif”. Όταν όµως πακεταριστούν τα δεδοµένα µέσα σε ένα JAR αρχείο, που στην πράξη δεν είναι τίποτα παραπάνω από ένα συµπιεσµένο αρχείο τύπου RAR, πρέπει να καλέσουµε την getResource µέθοδο για να αποκτήσουµε πρόσβαση στα αρχεία που περικλείονται στο αρχείο JAR. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 96 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Εφόσον οι παραπάνω προϋποθέσεις εκπληρώθηκαν (δηλαδή η δηµιουργία του MANIFEST αρχείου, και οι απαραίτητες µετατροπές στον κώδικα της εφαρµογής) µε την χρήση µιας σειράς µακροσκελών εντολών µπορούµε να κάνουµε compile τα αρχεία της εργασίας και να τα τοποθετήσουµε µαζί µε τα υπόλοιπα αρχεία (εικόνες κτλ) σε ένα .JAR. Για να αυτοµατοποιηθεί όµως η παραπάνω διαδικασία κατασκευής του αρχείου .JAR αποφασίστηκε να χρησιµοποιηθεί ένα ant script file [26]. Ονοµάζεται build.xml και βρίσκεται στο root κατάλογο του project. Το ANT αποτελεί ένα open source project του Apache Foundation και είναι ένα εργαλείο που βοηθάει στην αυτοµατοποίηση της διαδικασίας BUILD και packaging εφαρµογών java. Για να χρησιµοποιήσει κανείς το ANT αφού το εγκαταστήσει και το τοποθετήσει στο PATH του λειτουργικού συστήµατος πρέπει να δηµιουργήσει ένα αρχείο XML, το build.xml. Το XML αυτό αρχείο αποτελείται από tasks, κάθε ένα από τα οποία κάνει µια συγκεκριµένη εργασία. ∆ηµιουργήθηκε λοιπόν ένα build.xml αρχείο µε τα tasks που παρουσιάζονται στον Πίνακα 6-1. TASK NAME Περιγραφή - Επεξήγηση variables Αρχικοποιεί µεταβλητές που θα χρησιµοποιηθούν στη συνέχεια της διαδικασίας του build, όπως βοηθητικές βιβλιοθήκες, τοποθεσία φακέλων και εικόνων. init ∆ηµιουργεί το φάκελο build/classes όπου τα .class (τα compiled αρχεία), το φάκελο θα τοποθετηθούν τα .JAR αρχεία και φωτογραφίες (images) από το source destination directory compile Κάνει compile τα αρχεία .java της εφαρµογής jar Πακετάρει τα αρχεία .class το Manifest.txt εικόνες σε ένα αρχείο ScoreEditor.jar clean Σβήνει τους φακέλους /build/classes και /build/jars «καθαρίζοντας» το περιβάλλον για δηµιουργία νέας έκδοσης της εφαρµογής run θα τοποθετηθούν build/jars όπου αντιγράφει τις directory στο και τις Εκτελεί την εφαρµογή Πίνακας 6-1: Λίστα των tasks που υλοποιούνται στο αρχείο build.xml. Τέλος πρέπει να ειπωθεί ότι ορισµένα tasks εξαρτούνται (depend) από άλλα tasks. ∆ηλαδή για να τρέξει το jar-task το οποίο εξαρτάται από το init-task και το compile-task, τρέχουν πρώτα τα απαραίτητα task και κατόπιν το jar-task. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 97 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» <?xml version="1.0"?> <!-- ============================================================== --> <!-- A Buildfile --> <!-- ============================================================== --> <project name="ScoreEditor" default="jar" basedir="."> <target name="variables"> <tstamp/> <property name="Name" value="ScoreEditor"/> <property name="src.dir" value="${basedir}${file.separator}src"/> <property name="lib.dir" value="${basedir}${file.separator}libs"/> <property name="build.dir" value="${basedir}${file.separator}build"/> <property name="build.classes"value="${build.dir}${file.separator}classes"/> <property name="build.jar.dir" value="${build.dir}${file.separator}jars"/> <property name="build.jar" value="${build.jar.dir}${file.separator}${Name}.jar"/> <property name="build.classpath" value=".:${lib.dir}${file.separator}jmusic.jar"/> <property name="images.src" value="${src.dir}${file.separator}diamouses${file.separator}ui${file.sepa rator}scoreEditor${file.separator}images"/> <property name="images.dest" value="${build.classes}${file.separator}diamouses${file.separator}ui${fil e.separator}scoreEditor${file.separator}images"/> <property name="packages" value="*"/> </target> <!-- ============================================================== --> <!-- Create required directories --> <!-- ============================================================== --> <target name="init" depends="variables"> <!-- Prepare necessary directories --> <mkdir dir="${build.dir}"/> <mkdir dir="${build.classes}"/> <mkdir dir="${build.jar.dir}"/> <mkdir dir="${images.dest}"/> <!-- Copy all the images to the build directory --> <copy todir="${images.dest}"> <fileset dir="${images.src}" casesensitive="yes"> <include name="**/*.*"/> </fileset> </copy> </target> Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 98 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» <!-- ============================================================== --> <!-- Compiles the source code --> <!-- ============================================================== --> <target name="compile" depends="init" description="Compiles source code"> <javac encoding="UTF-8" srcdir="${src.dir}" destdir="${build.classes}" classpath="${build.classpath}" optimize="on" /> </target> <!-- ===============================================================--> <!-- Creates a jar archive --> <!-- ============================================================== --> <target name="jar" depends="init,compile" description="Generates ScoreEditor.jar" > <!-- Put everything from ${build.dir} into the ScoreEditor.jar file --> <jar manifest="${src.dir}/myManifest.txt" jarfile="${build.jar.dir}${file.separator}${Name}.jar" basedir="${build.classes}" includes="**" /> <copy todir="${build.jar.dir}"> <fileset dir="${src.dir}" casesensitive="yes"> <include name="**/*.html"/> </fileset> </copy> <copy todir="${build.jar.dir}"> <fileset dir="${lib.dir}" casesensitive="yes"> <include name="**/*.jar"/> </fileset> </copy> </target> <!-- ============================================================== --> <!-- Cleans up all --> <!-- ============================================================== --> <target name="clean" depends="init" description="Removes previous build (classes and jar files)"> <delete dir="${build.classes}"/> <delete dir="${build.jar.dir}"/> </target> <!-- ============================================================== --> <!-- Run Score Editor --> <!-- ============================================================== --> <target name="run" depends="init,compile,jar" description="Initiates ScoreEditor.java"> <echo message = "Running ScoreEditor"/> <java jar="${build.jar.dir}${file.separator}${Name}.jar" fork="true" > </java> </target> </project> Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 99 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Φαίνεται λοιπόν η jar εντολή που παίρνει σαν παράµετρο το myManifest.txt βρίσκει τα .class αρχεία και τις εικόνες στο φάκελο που ενηµερώσαµε νωρίτερα και τα πακετάρει όλα στο φάκελο build/jars στο αρχείο ScoreEditor.jar. Στον φάκελο αυτό τοποθετεί και το jmusic.jar µιας και είναι απαραίτητο για την εφαρµογή της πτυχιακής αυτής εργασίας. Με ‘ant –p’ βλέπουµε τα tasks που υποστηρίζονται από το script (βλέπε Σχήµα 6-43). Σχήµα 6-43: Λίστα µε tasks που υποστηρίζει το script. Με ‘ant clean’ διαγράφεται το προηγούµενο compilation της εφαρµογής (βλέπε Σχήµα 6-44). Σχήµα 6-44: Απεικόνιση εκτέλεσης εντολής ‘ant clean’. Και τέλος µε ‘ant jar’ γίνεται compile η εφαρµογή, αντιγράφονται οι εικόνες, και τοποθετούνται όλα τα απαραίτητα αρχεία µαζί µε το myManifest.txt σε αρχείο µε όνοµα ScoreEditor.jar όπως φαίνεται στο Σχήµα 6-45. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 100 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-45: Απεικόνιση εκτέλεσης εντολής ‘ant jar’. Εφόσον τρέξει το task του ANT η εφαρµογή βρίσκεται πακεταρισµένη στον φάκελο build/jars και µπορεί να τρέξει µε την εντολή ‘java -jar ScoreEditor.jar’ ή µε double-click από το λειτουργικό σύστηµα. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 101 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.10 Μετατροπή εφαρμογής σε Applet Για να τρέξει η εφαρµογή µέσα από µια ιστοσελίδα πρέπει να µετατραπεί σε Java Applet. Εφόσον έχουµε ήδη εξηγήσει τη µέθοδο τοποθέτησης των interpreted αρχείων µε την κατάληξη .class και των εικόνων σε αρχείο .JAR (κάτι που κρίνεται απαραίτητο ώστε η εφαρµογή να µπορεί να τρέξει σαν Applet) προχωράµε στη συγγραφή του απαραίτητου κώδικα HTML που δίνει τις κατάλληλες οδηγίες στον φυλλοµετρητή όπως µέγεθος του Applet καθώς και την starting class: <HTML> <HEAD> <TITLE>Εκµάθηση κιθάρας</TITLE> <META content="MSHTML 5.50.4616.200" name=GENERATOR> <link rel="stylesheet" href="css/styles.css" type="text/css"> </HEAD> <BODY> <applet code="diamouses.ui.scoreEditor.Main.class" codebase="." archive="ScoreEditor.jar" width="1160" height="600"> Your browser is completely ignoring the <APPLET> tag! </applet> </center> </BODY></HTML> ∆οκιµάζοντας από τοπικό υπολογιστή να ανοίξουµε το παραπάνω αρχείο το οποίο το τοποθετούµε στον φάκελο µε τα δύο JAR αρχεία, το ένα της εφαρµογής µας και το άλλο της βιβλιοθήκης JMusic, το Applet αρνείται να φορτώσει και εµφανίζεται ένα µήνυµα: java.security.AccessControlException: access denied (java.lang.RuntimePermission) Κοιτάζοντας πιο προσεκτικά εντοπίζουµε στην έβδοµη (7) γραµµή του exception ότι το µήνυµα λάθους οφείλεται στη χρήση του JFrame.setDefaultCloseOperation. Βρίσκοντας το σηµείο του κώδικα στο οποίο οφείλεται το λάθος καταλαβαίνουµε την αιτία: Στα Applets η Java δεν επιτρέπει το κλείσιµο της Java Virtual Machine. O λόγος είναι ότι επίδοξοι hackers θα χρησιµοποιούσαν αυτή τη δυνατότητα για να κάνουν DOS attacks (Denial Of Service επιθέσεις σε περιηγητές του Ιστού). Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 102 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Το άνοιγµα και το κλείσιµο της Java Virtual Machine είναι µια σχετικά χρονοβόρα διαδικασία η οποία καταναλώνει resources (τόσο µνήµη όσο και επεξεργαστή). Ένας κακόβουλος χρήστης θα µπορούσε να γράψει απλά Applets που θα ανοιγόκλειναν την Java Virtual Machine εκατοντάδες φορές το δευτερόλεπτο. Αυτή η διαδικασία θα «κράσαρε» ακόµα και τα πιο σύγχρονα υπολογιστικά µηχανήµατα των χρηστών, που επισκεπτόµενοι τη σελίδα αυτή δεν θα µπορούσαν να αποφύγουν την παγίδα του κακόβουλου χρήστη. Για το σκοπό αυτό απαγορεύτηκε το κλείσιµο της JVM. Στο Σχήµα 6-46 φαίνεται το µήνυµα λάθους. Σχήµα 6-46: Μήνυµα λάθους της Java Console. Εναλλακτικά µπορούµε να αντικαταστήσουµε τη γραµµή setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); µε τη γραµµή setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); Κατόπιν ξαναδηµιουργούµε το νέο JAR αρχείο που περιέχει τον ανανεωµένο κώδικα. Αυτή τη φορά η εφαρµογή µας τρέχει σαν Application και στη θέση του Applet βρίσκουµε µήνυµα λάθους που µας λέει: Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 103 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» java.lang.ClassCastException: diamouses.ui.scoreEditor.Main java.applet.Applet (βλέπε Σχήµα 6-47) . cannot be cast to Σχήµα 6-47: Exception: diamouses.ui.scoreEditor.Main cannot be cast to java.applet.Applet. Η εφαρµογή χρειάζεται και άλλες αλλαγές για τη µετατροπή της σε Applet. Μέχρι τώρα η Main έκανε extend το JFrame. Αυτό αναγκάζει την εφαρµογή να τρέξει σε νέο παράθυρο. public class Main extends javax.swing.JFrame έτσι η Main θα χρειαστεί πλέον να κάνει extend το JApplet. Ο λόγος είναι ότι επιθυµούµε η εφαρµογή να τρέχει τόσο σαν Application όσο και σαν Applet την ίδια στιγµή. O τρόπος που µπορεί να επιτευχθεί αυτό είναι να τοποθετήσουµε τον initialization κώδικα σε µέθοδο µε την ονοµασία init και να καταργήσουµε τη χρήση του JFrame. Βέβαια αυτό απαιτεί αλλαγές σε πολλά σηµεία του κώδικα. Αφού όµως κάναµε τις κατάλληλες τροποποιήσεις τα οφέλη ήταν πολλαπλά. Ένα ακόµα πρόβληµα που αντιµετωπίσαµε κατά τη µετατροπή του ScoreEditor σε Java Applet ήταν η παράκαµψη των µηχανισµών ασφαλείας του sandbox της Java Virtual Machine που απαγορεύει τη χρήση του JFileChooser για εγγραφή και ανάγνωση αρχείων από τοπικό filesystem του χρήστη στον φυλλοµετρητή του οποίου τρέχει το Java Applet. Όπως είπαµε η JVM για να προστατέψει τους χρήστες περιέχει έναν πολύ αυστηρό µηχανισµό ασφαλείας ειδικά στα Applets για να αποτρέψει την κακόβουλη χρήση τους. Παρέχει όµως µηχανισµούς για τροποποίηση των policy settings παρέχοντας µέσα από πιστοποιητικά ασφαλείας και κατόπιν συναίνεσης του χρήστη priviledge escalation, δηλαδή παροχή αυξηµένων δικαιωµάτων σε ένα applet. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 104 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Συγκεκριµένα 3 εργαλεία παρέχονται µαζί µε το JDK, τα: • Keytool: ∆ηµιουργεί και κάνει manage κλειδιά ασφαλείας • Jarsigner: Υπογράφει αρχεία JAR µε κάποιο κλειδί ασφαλείας που έχουµε ήδη δηµιουργήσει. • Policytool: Γραφικό περιβάλλον για διαχείριση πολιτικής ασφαλείας (policy management). Το πρώτο βήµα λοιπόν είναι να χρησιµοποιήσουµε το keytool και να δηµιουργήσουµε ένα κλειδί ασφαλείας. Το εργαλείο αυτό δηµιουργεί κλειδιά βασισµένα στο πρωτόκολλο X.509. Το Χ.509 είναι ένα πρότυπο κρυπτογράφησης το οποίο σχεδιάστηκε για να παρέχει υποδοµή πιστοποίησης. Η πρώτη έκδοση του X.509 δηµοσιεύθηκε το 1988, καθιστώντας το την παλαιότερη πρόταση για µια παγκόσµια Υποδοµή ∆ηµόσιου Κλειδιού. Σε συνδυασµό µε την υποστήριξη του προτύπου από τον ISO και από άλλους οργανισµούς όπως τη ∆ιεθνή Ένωση Τηλεπικοινωνιών (International Telecommunications Union ITU), το X.509 έχει διεθνώς αναγνωριστεί. Αρκετά χρηµατοπιστωτικά ιδρύµατα χρησιµοποιούν το X.509 για το πρότυπο ασφαλών συναλλαγών SET (Secure Electronic Transactions). Με τα παραπάνω φαίνεται ότι αποτελεί ένα σοβαρό πρότυπο ασφαλείας και για το λόγο αυτό χρησιµοποιείται από την Sun για πιστοποίηση στα Applets. Για να δηµιουργήσουµε λοιπόν ένα κλειδί ασφαλείας, πληκτρολογούµε την παρακάτω εντολή: keytool -genkey -alias scoreeditor -keyalg rsa -validity 750 Ζητάµε δηλαδή τη δηµιουργία κλειδιού µε το alias scoreeditor µε χρήση του αλγορίθµου RSA και µε διάρκεια ζωής 750 µέρες. Στο Σχήµα 6-48 που ακολουθεί µπορείτε να παρατηρήσετε τα στοιχεία που συµπληρώνουµε και ακολουθούν το κλειδί ασφαλείας. Σχήµα 6-48: Στοιχεία που ακολουθούν το κλειδί ασφαλείας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 105 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Εφόσον προετοιµάσαµε το κλειδί, αυτό τοποθετείται στο µηχανισµό που συγκρατεί τα κλειδιά στον τοπικό υπολογιστή. ∆ηµιουργεί µάλιστα ένα αρχείο µε το όνοµα .keystore (βλέπε Σχήµα 6-49) στο φάκελο user.home. ∆ηλαδή στο φάκελο: C:\Documents and Settings\uName στα Windows XP C:\Users\uName στα Windows Vista Σχήµα 6-49: Αρχείο .keystore. Για να τοποθετήσουµε το certificate στο φάκελο στον οποίο πατάµε την εντολή, πρέπει να χρησιµοποιήσουµε την παράµετρο -keystore ορίζοντας το όνοµα του αρχείου που επιθυµούµε να χρησιµοποιήσουµε. Έτσι χρησιµοποιώντας την εντολή keytool -genkey -alias score_editor -keyalg rsa -validity 750 keystore scoreeditor.cer Θα δηµιουργηθεί το αρχείο scoreeditor.cer στον τρέχον φάκελο όπως φαίνεται στο Σχήµα 6-50. Σχήµα 6-50: Αρχείο scoreeditor.cer. Το επόµενο βήµα είναι να κάνουµε sign το JAR αρχείο µας µε το κλειδί που µόλις δηµιουργήσαµε. Αφού το κάνουµε και αυτό προσπαθούµε να τρέξουµε το applet και να σώσουµε ένα MIDI αρχείο στον τοπικό δίσκο του χρήστη. Παρατηρούµε όµως το εξής µήνυµα λάθους. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 106 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Going to save score into file : C:\Users\Desktop\test.midi -------------------- Writing MIDI File -----------------------------Converting to SMF data structure... Part 0 'Untitled Part' to SMF Track on Ch. 0: Phrase 0:.. Exception in thread "AWT-EventQueue-2" java.security.AccessControlException: access denied (java.io.FilePermission C:\Users\Desktop\test.midi write) at java.security.AccessControlContext.checkPermission(Unknown Source) at java.security.AccessController.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPermission(Unknown Source) Ο Security Manager της Java Virtual Machine αρνείται να σώσει το αρχείο test.midi παρόλο που το Applet µας είναι signed και ο χρήστης δέχεται την παροχή αυξηµένων δικαιωµάτων. Μετά από αρκετό trial & error καταλάβαµε ότι πέρα από το Score_Editor.jar πρέπει να κάνουµε sign και τη βοηθητική βιβλιοθήκη jmusic.jar Έτσι συνολικά οι εντολές που πρέπει να πληκτρολογήσουµε για να πετύχουµε τον αρχικό στόχο, την παροχή δικαιωµάτων εγγραφής στον τοπικό δίσκο είναι: 1. 2. 3. 4. keytool -genkey -alias score_editor keytool -export -alias score_edit -rfc -file score_editor_sig.x509 jarsigner ScoreEditor.jar score_editor jarsigner jmusic.jar score_editor Κατόπιν και χρησιµοποιώντας τα electronically signed applets, όταν ο χρήστης επισκέπτεται τη σελίδα του εµφανίζεται το µήνυµα που παρουσιάζεται στο Σχήµα 6-51, και πρέπει να επιλέξει ‘Run’, διαφορετικά δεν θα δώσει τα απαραίτητα δικαιώµατα στο Applet. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 107 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-51: Μήνυµα στο χρήστη που αφορά τα δικαιώµατα του Applet. Στο κάτω δεξιά µέρος της παραπάνω εικόνας υπάρχει ένα link µε την ονοµασία More Information (βλέπε Σχήµα 6-52). Ο χρήστης επιλέγοντας το link αυτό µπορεί να δει πληροφορίες σχετικές µε το πιστοποιητικό ασφαλείας. Μπορεί να δει δηλαδή τα στοιχεία που συµπληρώσαµε όταν δηµιουργούσαµε το πιστοποιητικό αυτό. Πρώτα βέβαια εµφανίζεται το παρακάτω µήνυµα που προειδοποιεί τον χρήστη για 2 πράγµατα: α) ότι αν δεχθεί το πιστοποιητικό θα δώσει αυξηµένα δικαιώµατα σε remote applet και β) ότι το συγκεκριµένο πιστοποιητικό είναι untrusted. Θα µπορούσαµε να δηµιουργούσαµε ένα trusted πιστοποιητικό, αλλά αυτό προϋποθέτει την πληρωµή µιας συνδροµής σε έναν από τους διεθνής αναγνωρισµένους οργανισµούς που έχουν αναλάβει την παροχή trusted πιστοποιητικών µε ένα σχετικά µικρό κόστος. Στην περίπτωση αυτή θα στέλναµε το public key στον οργανισµό αυτό (π.χ. VeriSign) και θα µας το πιστοποιούσε αφού εξακρίβωνε τα πραγµατικά µας στοιχεία. To private key όµως θα το κρατούσαµε εµείς κάνοντάς µας τους µοναδικούς που θα µπορούσαν να κάνουν sign applets µε το συγκεκριµένο κλειδί - ηλεκτρονική υπογραφή µας. Κάτι τέτοιο όµως δεν είναι απαραίτητο και δεν έγινε στα πλαίσια της πτυχιακής αυτής εργασίας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 108 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-52: Παράθυρο ‘More Information’. Τέλος πατώντας ‘Certificate Details…’ µπορούµε να δούµε τα στοιχεία που συνοδεύουν το πιστοποιητικό ασφαλείας, όωπς φαίνεται στο Σχήµα 6-53. Σχήµα 6-53: Στοιχεία που συνοδεύουν το πιστοποιητικό ασφαλείας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 109 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Εν τέλει ο χρήστης µπορεί να αποθηκεύσει αρχεία στον τοπικό υπολογιστή του χρήστη και να κάνει χρήση του JFileChooser component που φαίνεται στο Σχήµα 6-54. Σχήµα 6-54: Απεικόνιση Save Dialog για αποθήκευση παρτιτούρας. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 110 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 6.11 Δημιουργία Ιστοσελίδας Το Joomla [33] είναι ένα πλήρες σύστηµα διαχείρισης περιεχοµένου ανοιχτού κώδικα. Είναι εφαρµογή η οποία µπορεί να καλύψει τις ανάγκες µιας προσωπικής ιστοσελίδας, αλλά και ενός ολόκληρου δικτυακού τόπου. Είναι προσαρµόσιµο σε περιβάλλοντα επιχειρηµατικής κλίµακας όπως τα intranets µεγάλων επιχειρήσεων ή οργανισµών, και οι δυνατότητες επέκτασής του είναι πρακτικά απεριόριστες. Το Joomla εγκαθίσταται σε έναν κεντρικό υπολογιστή, τον web server. Ο χρήστης διαχειριστής , έχει πρόσβαση στο περιβάλλον διαχείρισης του Joomla (βλέπε Σχήµα 6-55) µέσω ενός browser, όπως είναι ο Internet Explorer ή ο Firefox, και µπορεί να προσθέσει οποιοδήποτε κείµενο ή γραφικό ώστε να δηµιουργήσει την ιστοσελίδα του. Σχήµα 6-55: Περιβάλλον διαχείρισης Joomla. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 111 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Το Joomla χρησιµοποιεί µια ισχυρή templating engine που µας δίνει την δυνατότητα να χρησιµοποιήσουµε το template που επιθυµούµε. Έτσι, ψάχνοντας στον παγκόσµιο ιστό βρήκαµε το template [34] που πληρούσε τις δικές µας προϋποθέσεις από αισθητικής άποψης, και το εγκαταστήσαµε στο Joomla. Στη συνέχεια εργαστήκαµε στο υπάρχον template αλλάζοντας από το αρχείο css [35] παραµέτρους που αφορούν την εµφάνιση. Η τελική µορφή που πήρε το template της σελίδας µας απεικονίζεται στο Σχήµα 6-56. Σχήµα 6-56: Template ιστοσελίδας. Αφού ολοκληρώσαµε την επεξεργασία του template και γενικά της εµφάνισης που θα έχει η ιστοσελίδα µας, προχωρήσαµε στο επόµενο βήµα που είναι η προσθήκη των υποσελίδων που θα αποτελούν την δοµή της σελίδας. Αρχικά µέσα από το control panel του Joomla ορίσαµε την εµφάνιση του κεντρικού µενού και έπειτα προσθέσαµε τα menu items που θα απαρτίζουν το κεντρικό µενού της σελίδας µας, όπως φαίνεται στο Σχήµα 6-57. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 112 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-57: Κεντρικό µενού της σελίδας. Αφού δηµιουργήσαµε το κεντρικό µενού της σελίδας ξεκινήσαµε να προσθέτουµε περιεχόµενο σε κάθε υποσελίδα. Κεντρική Σελίδα Πηγαίνοντας στο µενού Content => Article Manager του Joomla µπορούµε να διαχειριστούµε το περιεχόµενο κάθε υποσελίδας. Έτσι, η κεντρική σελίδα διαµορφώθηκε όπως φαίνεται στο Σχήµα 6-58. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 113 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-58: Κεντρική σελίδα. Είσοδος Στην Τάξη Σε αυτή την σελίδα εργαστήκαµε διαφοτερικά διότι έπρεπε να ενσωµατώσουµε το applet µέσα στο οποίο θα εµφανίζεται η εφαρµογή µας. Αφού γράψαµε τον κώδικα που ήταν απαραίτητος για την εµφάνιση του applet σε µια html σελίδα προσθέσαµε ένα εισαγωγικό κείµενο πριν την εφαρµογή, και ορίσαµε στο Joomla το Menu Item «Είσοδος στην τάξη» ως Wrapper. Με αυτό τον τρόπο το Joomla δηµιουργεί ένα εσωτερικό πλαίσιο (iframe) όπου µπορούµε να ενσωµατώσουµε µία εξωτερική σελίδα στη σελίδα µας. Έτσι εισάγουµε το url της σελίδας (http://localhost/mysite/myapplet/application.php) στην οποία εµφανίζουµε το applet και παίρνουµε το αποτέλεσµα που απεικονίζεται στο Σχήµα 6-59. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 114 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-59: Σελίδα ‘Είσοδος στην τάξη’. Φόρουµ Για το φόρουµ εργαστήκαµε ως εξής: Αρχικά κατεβάσαµε και εγκαταστήσαµε στην ίδια βάση µε το Joomla, το phpBB3 ("Olympus") forum [36] που είναι ένα forum ανοιχτού κώδικα. Μετά την εγκατάσταση του phpBB3 στον υπολογιστή βλέπουµε τον πίνακα ελέγχου του διαχειριστή (ACP) του φόρουµ (βλέπε Σχήµα 6-60). Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 115 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-60: Πίνακας ελέγχου διαχειριστή φόρουµ. Έπειτα ψάξαµε στον παγκόσµιο ιστό το στυλ του φόρουµ που ταιριάζει στη σελίδα µας και αφού το εγκαταστήσαµε το ορίσαµε ως default για το φόρουµ. Η ενσωµάτωση του φόρουµ στη σελίδα έγινε µε παρόµοιο τρόπο όπως και στην σελίδα «Είσοδος Στην Τάξη». ∆ηλαδή ορίσαµε το Menu Item «Φόρουµ» ως Wrapper και βάλαµε το url του φόρουµ phpBB3 που στήσαµε. Έτσι η σελίδα «Φόρουµ» διαµορφώθηκε όπως φαίνεται στο Σχήµα 6-61. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 116 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-61: Σελίδα ‘Φόρουµ’. Τέλος, αποφασίσαµε η σελίδα να υποστηρίζει login χρηστών. Κάνοντας enabled το login module του Joomla, εµφανίστηκε στην σελίδα η φόρµα εγγραφής που φαίνεται στο Σχήµα 6-62. Αυτό είχε ως αποτέλεσµα κάθε φορά που ο χρήστης έκανε login στη σελίδα να αναγκάζεται να κάνει login και στο φόρουµ. Το πρόβληµα λύθηκε εγκαθιστώντας µια γέφυρα [37] µεταξύ του Joomla και του phpBB3 forum. Με αυτό τον τρόπο οι χρήστες κάνουν µια φορά login στη σελίδα και φαίνονται συνδεδεµένοι και στο φόρουµ. Σχήµα 6-62: Φόρµα εγγραφής χρηστών. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 117 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Το phpBB3 forum µας δίνει τη δυνατότητα µέσω του πίνακα ελέγχου να δηµιουργήσουµε νέες δηµόσιες συζητήσεις, να ορίσουµε τις προσβάσεις που θα έχουν οι οµάδες σε αυτές, και τους ρόλους των οµάδων µελών. Επίσης µπορούµε να δηµιουργήσουµε νέες οµάδες µελών και να ορίσουµε επιπλέον δυνατότητες σε αυτές. Συγκεκριµένα δηµιουργήσαµε δύο νέες οµάδες, τις Καθηγητές και Μαθητές, και τις εξής δηµόσιες συζητήσεις: - Καλώς Ήρθατε Θεωρία Ασκήσεις Απορίες για τη θεωρία και τις ασκήσεις Γενικά Σε κάθε µία από αυτές τις δηµόσιες συζητήσεις ορίσαµε τα δικαιώµατα πρόσβασης συνοψίζονται στον Πίνακα 6-2. ∆ηµόσια Συζήτηση Θεωρία Ασκήσεις Απορίες για τη θεωρία και τις ασκήσεις Γενικά ∆ικαιώµατα Πρόσβασης µόνο οι καθηγητές έχουν τη δυνατότητα να επισυνάπτουν αρχεία και µόνο οι µαθητές µπορούν να µεταφορτώσουν τα αρχεία αυτά. Επίσης µόνο οι καθηγητές µπορούν να δηµιουργήσουν νέα θέµατα σε αυτή τη δηµόσια συζήτηση οι µαθητές µπορούν να συζητήσουν τις απορίες που έχουν σχετικά µε τη θεωρία και τις ασκήσεις που έχουν επισυνάψει οι καθηγητές για αυτούς. Επίσης οι µαθητές µπορούν να επισυνάψουν αρχεία ώστε να διευκολύνουν τους καθηγητές και να λυθούν ευκολότερα οι απορίες τους µπορούν όλα τα µέλη να συζητήσουν γενικά για ότι αφορά την κιθάρα. Οι επισκέπτες του φόρουµ µπορούν µόνο να διαβάσουν τα θέµατα που έχουν δηµοσιευτεί σε αυτή τη δηµόσια συζήτηση. ∆εν έχουν όµως δικαίωµα συµµετοχής στη συζήτηση µε την δηµιουργία νέου θέµατος Πίνακας 6-2: ∆ικαιώµατα πρόσβασης στις ∆ηµόσιες Συζητήσεις. Βοήθεια Στη «Βοήθεια» εργαστήκαµε όπως και στην «Κεντρική Σελίδα» και έτσι η σελίδα διοµορφώθηκε όπως φαίνεται στο Σχήµα 6-63. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 118 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Σχήµα 6-63: Σελίδα ‘Βοήθεια’. Επίπεδο πρόσβασης στις σελίδες Το επίπεδο πρόσβασης των χρηστών στις σελίδες παρουσιάζεται στον Πίνακα 6-3. Σελίδα Κεντρική Σελίδα Είσοδος στην Τάξη Φόρουµ Βοήθεια Επίπεδο Πρόσβασης Χρηστών ∆ηµόσιο / Public Μέλη / Registered ∆ηµόσιο / Public Μέλη / Registered Πίνακας 6-3: Επίπεδο πρόσβασης χρηστών στις σελίδες. Όπως προκύπτει από τον πίνακα όλοι οι επισκέπτες της σελίδας έχουν πρόσβαση στην κεντρική σελίδα και στο φόρουµ, ενώ οι εγγεγραµένοι χρήστες έχουν επιπλέον πρόσβαση στην τάξη και στην βοήθεια. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 119 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 7. Σύνοψη και Συμπεράσματα Με την εκπόνηση της πτυχιακής αυτής εργασίας, αποκτήσαµε γνώσεις σχετικά µε τον αντικειµενοστραφή προγραµµατισµό χρησιµοποιώντας την Java. Ανακαλύψαµε τις τεράστιες δυνατότητες που παρέχει ο αντικειµενοστραφής προγραµµατισµός, υλοποιώντας απλές µεθόδους και κλάσεις, και δηµιουργώντας αντικείµενα. Επίσης, µε την χρήση του εργαλείου Joomla διαπιστώσαµε την αξία ενός συστήµατος διαχείρισης περιεχοµένου ιστοσελίδων. Χρησιµοποιώντας modules και components του Joomla δηµιουργήσαµε την ιστοσελίδα που πληρούσε τις δικές µας απαιτήσεις και προδιαγραφές, µε απλές διαδικασίες. Η πτυχιακή αυτή εργασία µας εφοδίασε µε γνώσεις τόσο στον αντικειµενοστραφή προγραµµατισµό, όσο και στον προγραµµατισµό ιστοσελίδων, τις οποίες θα αξιοποιήσουµε στην µελλοντική εργασία µας. Οι δυνατότητες επέκτασης και βελτίωσης της πτυχιακής εργασίας είναι πολλές. Μπορεί να αποτελέσει τη βάση ώστε να προστεθούν και άλλες εικονικές αναπαραστάσεις κιθάρας (π.χ. ηλεκτρική κιθάρα). Έχουµε υλοποιήσει έτσι την εφαρµογή ώστε να µπορεί να παραµετροποιηθεί εύκολα το αντικείµενο GuitarNeck και να καθιστά απλή µια τέτοια βελτίωση. Επίσης, θα µπορούσαν να προστεθούν και άλλα µουσικά όργανα ώστε ο χρήστης όταν επιλέγει ένα µουσικό όργανο να βλέπει και την αντίστοιχη απεικόνισή του. Ακόµα, θα µπορούσε να βελτιωθεί το κουµπί play score, ώστε όταν ο χρήστης το πατάει, για κάθε νότα που θα ακούγεται να εµφανίζεται η εκάστοτε θέση της στην εικονική αναπαράσταση του µουσικού οργάνου. Γενικά οι επεκτάσεις και οι βελτιώσεις της πτυχιακής εργασίας περιορίζονται µόνο από την φαντασία του εκάστοτε προγραµµατιστή! Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 120 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» 8. Βιβλιογραφία [1] UML ∆ιαγράµµατα, http://el.wikipedia.org/wiki/Γλώσσες_µοντελοποίησης_λογισµικού [2] Βιβλιοθήκη ABC4J, http://code.google.com/p/abc4j/ [3] ABC notation, http://en.wikipedia.org/wiki/Abc_notation [4] Using abc notation abc4j, http://code.google.com/p/abc4j/wiki/JScore_Using_abc_notation [5] Αρχεία MIDI, http://el.wikipedia.org/wiki/MIDI [6] ImproVisor, http://www.cs.hmc.edu/~keller/jazz/improvisor/ [7] JFugue, http://www.jfugue.org/ [8] Java MIDI Kit, http://www.mcnabb.com/software/fantasia/index.html [9] jMusic, http://jmusic.ci.qut.edu.au/ [10] jFrets, https://jfrets.dev.java.net/ [11] ABC BNF, http://www.norbeck.nu/abc/abcbnf.htm [12] The Complete Guide to JFugue, http://www.jfugue.org/book.html [13] Λίστα µε βιβλιοθήκες και πακέτα λογισµικού στη µουσική τεχνολογία, http://www.softsynth.com/links/java_music.html [14] Java Sound API, http://java.sun.com/products/java-media/sound/ Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 121 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» [15] Apple QuickTime, http://www.apple.com/quicktime/ [16] MidiShare, http://midishare.sourceforge.net/ [17] CodeSounding, http://www.codesounding.org/ [18] Red Wine Music, http://www.redwinemusic.com/ [19] JM-Etude, http://jmetude.dihardja.de/ [20] CVS, http://en.wikipedia.org/wiki/Concurrent_Versions_System [21] Subversion, http://subversion.tigris.org/ [22] NetBeans, http://www.netbeans.org/ [23] Rational Rose, http://www-01.ibm.com/software/rational/ [24] Κληρονοµικότητα, http://www.cs.teilar.gr/gkakaron/java/Day2/8.html [25] JAR File Specifications, http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html [26] Ant tool, http://ant.apache.org/ [27] Singleton, http://en.wikipedia.org/wiki/Singleton_pattern [28] Design Patterns, http://en.wikipedia.org/wiki/Design_pattern_(computer_science) [29]Guitar note positioning, http://www.ocmusic.com/trebleclef.htm Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 122 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» [30] Saving MIDI files in JMusic, http://jmusic.ci.qut.edu.au/jmtutorial/jmDOSTute.html [31] FileChoosers in Java, http://java.sun.com/docs/books/tutorial/uiswing/components/filechooser.html [32] Θεωρία µουσικής, http://homepages.pathfinder.gr/papakrasas/diafora.htm [33] Joomla, http://joomla.org [34] Template, http://www.themza.com/joomla1.5/computer-society-template.html [35] Cascading Style Sheets, http://www.w3schools.com/css [36] PhpBB3, http://www.phpbb.com [37] Γέφυρα Joomla – phpBB3, http://www.rocketwerx.com/forum/viewtopic.php?f=22&t=368 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 123 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Παράρτημα 1 package diamouses.ui.scoreEditor; import javax.swing.ImageIcon; public final class { Constants public ImageIcon a; public ImageIcon asharp; public ImageIcon b ; public ImageIcon c; public ImageIcon csharp; public ImageIcon d; public ImageIcon dsharp; public ImageIcon e; public ImageIcon f; public ImageIcon fsharp; public ImageIcon g; public ImageIcon gsharp; public Constants() { a = new ImageIcon(getClass().getResource("images/note_images/a.png")); asharp = new ImageIcon(getClass().getResource("images/note_images/asharp.png")); b = new ImageIcon(getClass().getResource("images/note_images/b.png")); c = new ImageIcon(getClass().getResource("images/note_images/c.png")); csharp = new ImageIcon(getClass().getResource("images/note_images/csharp.png")); d = new ImageIcon(getClass().getResource("images/note_images/d.png")); dsharp = new ImageIcon(getClass().getResource("images/note_images/dsharp.png")); e = new ImageIcon(getClass().getResource("images/note_images/e.png")); f = new ImageIcon(getClass().getResource("images/note_images/f.png")); fsharp = new ImageIcon(getClass().getResource("images/note_images/fsharp.png")); g = new ImageIcon(getClass().getResource("images/note_images/g.png")); gsharp = new ImageIcon(getClass().getResource("images/note_images/gsharp.png")); e.setDescription("e"); f.setDescription("f"); fsharp.setDescription("fsharp"); g.setDescription("g"); gsharp.setDescription("gsharp"); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 124 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» a.setDescription("a"); asharp.setDescription("asharp"); b.setDescription("b"); c.setDescription("c"); csharp.setDescription("csharp"); d.setDescription("d"); dsharp.setDescription("dsharp"); } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor; import import import import import import import java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.GridLayout; java.awt.Toolkit; java.awt.image.BufferedImage; java.net.URL; import import import import javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JToggleButton; import diamouses.ui.scoreEditor.staves.DStave; public class GuitarNeck extends JPanel { /** * Global variables for the application */ private int height = 28; private int screen_width; private JToggleButton [][] fret_buttons = new JToggleButton [6][22]; private JPanel [] string_panels; private JPanel fret_panel; ImageIcon ii; Color color_1st = new Color(241,255,255); Color color_2nd = new Color(255,255,240); Color color_3rd = new Color(255,255,230); Color color_4th = new Color(255,255,225); Color color_5th = new Color(255,255,220); Color color_6th = new Color(255,255,210); private static GuitarNeck reference; /** * Constructor. * Initializes 6 JPanels, one for each guitar string */ private GuitarNeck(int screen_width) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 125 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» this.screen_width = screen_width; // 0. Load background image String imgLocation2 = "images/GuitarNeck.png"; URL imageURL = GuitarNeck.class.getResource(imgLocation2); ii = new ImageIcon(imageURL, "altText"); // 1. Create six JPanels fret_panel = getStringPanel(); // 2. Make them Not-Opaque (Make them TRANSPARENT) fret_panel.setOpaque(false); add(fret_panel); setSizes(); } public static GuitarNeck getInstance(int mm) { if (reference==null) { reference = new GuitarNeck(mm); // mm = 1160px } return reference; } public static GuitarNeck getInstance() { return reference; } /** * Method returns a JPanel with JButtons for each note of the Guitar * @param string First..Sixth string of the guitar * @return a JPanel with appropriate JButtons */ private JPanel getStringPanel() { JPanel main_panel = new JPanel(); ImageIcon [] string_images; // Add each of the JPanel in a stack from top to bottom main_panel .setLayout(new GridLayout(0,1,0,0)); string_panels = new JPanel [6]; for (int string = 0; string <= 5; string++) { string_panels[string] = new JPanel(); string_panels[string].setOpaque(false); string_images = getIconsForString(string); for (int i = 0; i <= 21; i++) { fret_buttons[string][i] = new JToggleButton(); fret_buttons[string][i].setContentAreaFilled(false); fret_buttons[string][i].setBorderPainted(false); fret_buttons[string][i].setOpaque(false); fret_buttons[string][i].setIcon(new ImageIcon()); fret_buttons[string][i].setSelectedIcon(string_images [i]); string_panels[string].add(fret_buttons[string][i]); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 126 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» main_panel.add(string_panels[string]); } return main_panel; } /** * This method sets the sizes of the JToggleButtons */ private void setSizes () { int width; for (int i = 0; i <= 21; i++) { switch (i) { case 0: width = 83; break; case 1: width = 89; break; case 2: width = 82; break; case 3: width = 77; break; case 4: width = 75; break; case 5: width = 66; break; case 6: width = 64; break; case 7: width = 59; break; case 8: width = 55; break; case 9: width = 54; break; case 10: width = 49; break; case 11: width = 46; break; case 12: width = 43; break; case 13: width = 40; break; case 14: width = 38; break; case 15: width = 35; break; case 16: width = 32; break; case 17: width = 30; break; case 18: width = 28; break; case 19: width = 26; break; case 20: width = 22; break; default: width = 12; break; } width+=2; width = screen_width*width/1280; //string_1.setPreferredSize(new Dimension(width,height)); fret_buttons[0][i].setPreferredSize(new height)); fret_buttons[1][i].setPreferredSize(new height)); fret_buttons[2][i].setPreferredSize(new height)); fret_buttons[3][i].setPreferredSize(new height)); fret_buttons[4][i].setPreferredSize(new height)); fret_buttons[5][i].setPreferredSize(new height)); } Dimension(width, Dimension(width, Dimension(width, Dimension(width, Dimension(width, Dimension(width, } /** Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 127 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» * Method Paint the Guitar Neck on the Background of this JPanel. */ public void paintComponent(Graphics g) { g.drawImage(ii.getImage(),0,0,this.getSize().width,this.getSize() .height, this); } /** * Switch the color of the string * @param string values 1 to 6 */ private void switchColor(int string) { Color new_color; switch (string) { case 1: new_color = color_1st; break; case 2: new_color = color_2nd; break; case 3: new_color = color_3rd; break; case 4: new_color = color_4th; break; case 5: new_color = color_5th; break; case 6: new_color = color_6th; break; default: new_color = Color.black; } BufferedImage bi = DStave.toBufferedImage(ii.getImage()); int w = bi.getWidth(); int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); if (pixel == (new_color).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } ii = new ImageIcon(DStave.toImage(biOut)); repaint(); } /** * Method getIconsForString returns an Array [] of ImageIcons * with the images of the appropriate Notes for each string of * the Guitar. * * @param string An integer from 1 to 6 representing the First..Sixth string * of a Guitar. * @return an ImageIcon [] array. */ private ImageIcon [] getIconsForString( int string ) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 128 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Constants c = new Constants(); string+=1; switch (string) { //c.e, case 1: ImageIcon [] string_1_notes = { c.f, c.fsharp, c.g, c.gsharp,c.a,c.asharp,c.b, c.c,c.csharp,c.d,c.dsharp,c.e,c.f, null, null, null, null,null,null,null,null,null}; return string_1_notes; // c.b case 2: ImageIcon [] string_2_notes = {c.c,c.csharp,c.d, c.dsharp,c.e,c.f, c.fsharp, c.g, c.gsharp,c.a,c.asharp,c.b, c.c,null,null, null, null,null,null,null,null,null}; return string_2_notes; // c.g case 3: ImageIcon [] string_3_notes = {c.gsharp,c.a,c.asharp,c.b, c.c,c.csharp,c.d, c.dsharp, c.e,c.f,c.fsharp, c.g, c.gsharp,null,null,null, null, null,null,null,null,null}; return string_3_notes; // c.d case 4: ImageIcon [] string_4_notes = {c.dsharp, c.e,c.f,c.fsharp, c.g, c.gsharp,c.a,c.asharp,c.b, c.c, c.csharp,c.d, c.dsharp,null,null,null, null, null,null,null,null,null}; return string_4_notes; // c.a case 5: ImageIcon [] string_5_notes = {c.asharp,c.b, c.c,c.csharp,c.d,c.dsharp,c.e,c.f,c.fsharp, c.g,c.gsharp,c.a, c.asharp,null,null,null, null, null,null,null,null,null}; return string_5_notes; // c.e case 6: ImageIcon [] string_6_notes = {c.f,c.fsharp, c.g, c.gsharp,c.a,c.asharp,c.b,c.c,c.csharp,c.d,c.dsharp,c.e,c.f, null,null, null, null,null,null,null,null,null}; return string_6_notes; default: System.out.println("Errornous string " + string + " in method GuitarFrame.getIconsForString"); return null; } } /** * Sets the note with a specific pitch on the virtual guitar * * @param pitch the pitch value. */ public void setNote(int pitch) clearAll(); int fret_1 = (pitch-77); int fret_2 = (pitch-72); int fret_3 = (pitch-68); int fret_4 = (pitch-63); int fret_5 = (pitch-58); int fret_6 = (pitch-53); { //5 //4 //5 //5 //5 Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 129 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (fret_1>=0 & fret_1<=21) { fret_buttons[0][fret_1].setSelected(true); } if (fret_2>=0 & fret_2<=21) { fret_buttons[1][fret_2].setSelected(true); } if (fret_3>=0 & fret_3<=21) { fret_buttons[2][fret_3].setSelected(true); } if (fret_4>=0 & fret_4<=21) { fret_buttons[3][fret_4].setSelected(true); } if (fret_5>=0 & fret_5<=21) { fret_buttons[4][fret_5].setSelected(true); } if (fret_6>=0 & fret_6<=21) { fret_buttons[5][fret_6].setSelected(true); } if (fret_1==-1) switchColor(1); if (fret_2==-1) switchColor(2); if (fret_3==-1) switchColor(3); if (fret_4==-1) switchColor(4); if (fret_5==-1) switchColor(5); if (fret_6==-1) switchColor(6); } public void clearAll() { for (int string=0; string <= 5; string++) { for (int i=0; i<fret_buttons[0].length; i++) { fret_buttons[string][i].setSelected(false); } } String imgLocation2 = "images/GuitarNeck.png"; URL imageURL = GuitarNeck.class.getResource(imgLocation2); ii = new ImageIcon(imageURL, "altText"); repaint(); } /** *For testing */ /*public static void main(String[] args) { JFrame f = new JFrame(); f.setVisible(true); f.setExtendedState(JFrame.MAXIMIZED_HORIZ); Toolkit kit = f.getToolkit(); Dimension screenSize = kit.getScreenSize(); int screenWidth = screenSize.width; System.out.println("screenwidth" + screenWidth); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 130 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» GuitarNeck ff = GuitarNeck.getInstance(screenWidth); f.getContentPane().add(ff); f.pack(); }*/ } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor; java.awt.BorderLayout; java.awt.Dimension; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyEvent; java.io.File; import import import import import import import import javax.swing.*; import javax.swing.filechooser.FileFilter; import jm.music.data.Phrase; import jm.music.data.Score; import jm.util.Write; import diamouses.ui.Metronome.Metronome; /** * */ public class Main extends JApplet implements ActionListener { // Global Variables private static ScoreEditor score; /** * Constructor */ public Main() { //setJMenuBar(getJMenuBar()); //-System.out.println("screenwidth" + screenWidth); } public void init (){ score = new ScoreEditor(); //JFrame new_frame = new JFrame("Score Editor"); //setDefaultCloseOperation(javax.swing.WindowConstants.DISPOS E_ON_CLOSE); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 131 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» setJMenuBar(getJMenuBar()); Toolkit kit = getToolkit(); Dimension screenSize = kit.getScreenSize(); int screenWidth = screenSize.width; GuitarNeck ff = GuitarNeck.getInstance(screenWidth); getContentPane().add(score, BorderLayout.CENTER); getContentPane().add(score.getJToolBar(), BorderLayout.PAGE_START); getContentPane().add(ff, BorderLayout.SOUTH); setVisible(true); //setExtendedState(JFrame.MAXIMIZED_HORIZ); //pack(); } /** * Creates a JMenuBar (File/Tools/Help) with appropriate * menu items */ public JMenuBar getJMenuBar() { // Local Variables JMenuBar menuBar; JMenu menu; JMenuItem menuItem; // Create the menu bar. menuBar = new JMenuBar(); // 1. Build the 'File' menu. menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menuBar.add(menu); // 'New Stave' MenuItem menuItem = new JMenuItem("Clear Stave", KeyEvent.VK_N); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("Clear_Stave"); menuItem.addActionListener(this); menu.add(menuItem); // 'Save Stave' MenuItem menuItem = new JMenuItem("Save Stave"); //, new ImageIcon("images/middle.gif")); menuItem.setMnemonic(KeyEvent.VK_S); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("Save_Stave"); menuItem.addActionListener(this); menu.add(menuItem); // Separator menu.addSeparator(); // 'Play Stave' MenuItem Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 132 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» menuItem = new JMenuItem("Play Stave"); //, new ImageIcon("images/middle.gif")); menuItem.setMnemonic(KeyEvent.VK_P); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("Play_Stave"); menuItem.addActionListener(this); menu.add(menuItem); // Separator menu.addSeparator(); /* 'Exit' MenuItem menuItem = new JMenuItem("Exit"); //, new ImageIcon("images/middle.gif")); menuItem.setMnemonic(KeyEvent.VK_X); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("Exit"); menuItem.addActionListener(this); menu.add(menuItem); */ // 2. Build 'Tools' menu menu = new JMenu("Tools"); menu.setMnemonic(KeyEvent.VK_T); menuBar.add(menu); // 'Metronome' MenuItem menuItem = new JMenuItem("Metronome", KeyEvent.VK_M); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("Metronome"); menuItem.addActionListener(this); menu.add(menuItem); // 3. Build 'Help' menu menu = new JMenu("Help"); menu.setMnemonic(KeyEvent.VK_H); menuBar.add(menu); // 'About' MenuItem menuItem = new JMenuItem("About", KeyEvent.VK_A); menuItem.setActionCommand("About"); menuItem.addActionListener(this); menu.add(menuItem); return menuBar; } /** * Starting place of the application * @param args */ public static void main(String args[]) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 133 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» JFrame f = new JFrame("Πτυχιακή ΤΕΙ Κρήτης - Ευθυµίου Μιχαλίτσα, Γρηγορακάκη Αιµιλία"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JApplet ap = new Main(); ap.init(); ap.start(); f.add("Center", ap); f.pack(); f.setVisible(true); /*java.awt.EventQueue.invokeLater(new Runnable() { public void run() { setDefault(); new Main().setVisible(true); } }); }*/ } /** * setDefault() , Sets the default Look and Feel. On Microsoft Windows * platforms, this specifies the Windows Look & Feel. On Mac OS platforms, * this specifies the Mac OS Look & Feel. On Sun platforms, it specifies the * CDE/Motif Look & Feel. * * With JDK1.4.2 upwards it sets the 'Luna' look and feel in Windows XP and * 'Bluecurve' in GNOME. */ public static void setDefault() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) ; } catch (java.lang.ClassNotFoundException classE) { // Handle exception } catch (java.lang.InstantiationException insE) { // Handle exception } catch (java.lang.IllegalAccessException illE) { // Handle exception } catch (javax.swing.UnsupportedLookAndFeelException unE) { // Handle exception } } public void actionPerformed(ActionEvent event) { String action_command = event.getActionCommand(); if (action_command.equals("Metronome")) { Metronome met = new Metronome(new JFrame(), null, true); met.setVisible(true); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 134 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» else if (action_command.equals("Clear_Stave")) { score.stave.setPhrase(new Phrase()); } else if (action_command.equals("Save_Stave")) { String filename = File.separator+".midi"; JFileChooser fc = new JFileChooser(new File(filename)); fc.setFileFilter(new MIDIFileFilter()); // Show save dialog; this method does not return until the dialog is closed fc.showSaveDialog(this); File selected_file = fc.getSelectedFile(); String file_path = selected_file.toString(); if (!file_path.endsWith(".midi")) { file_path += ".midi"; } System.out.println("Goint to save score into file : " + file_path); Score s = new Score(score.getPart()); Write.midi(s, file_path); } else if (action_command.equals("Play_Stave")) { score.playTune(); } else if (action_command.equals("About")) { JOptionPane.showMessageDialog(new JFrame(), "Αυτή η εφαρµογή αναπτύχθηκε από τις Γρηγορακάκη Αιµιλία και Ευθυµίου Μιχαλίτσα,\n"+ "για την εκµάθηση κιθάρας\n"+ "Ctrl-N --> Νέα παρτιτούρα\n"+ "Ctrl-P --> Play tune\n" + "Ctrl-M --> Show Metronome\n"+ //"Ctrl-X --> Exit", "About the score-editor", JOptionPane.INFORMATION_MESSAGE); } /*else if (action_command.equals("Exit")) { this.dispose(); System.exit(0); }*/ } public static void setEnabledRemove(boolean b) { score.setEnabledRemove(b); // TODO Auto-generated method stub } class MIDIFileFilter extends FileFilter { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 135 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public String getDescription() { return "MIDI files (*.midi)"; } public boolean accept(File f) { return f.isDirectory() || f.getName().endsWith(".midi"); } } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor; import import import import import import import import diamouses.ui.scoreEditor.staves.*; java.awt.*; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.net.URL; java.util.Vector; javax.swing.*; javax.swing.border.*; import import import import jm.music.data.Note; jm.music.data.Part; jm.music.data.Phrase; jm.util.Play; ScoreEditor extends JPanel implements public class ActionListener { /** Private Variables **/ private JComboBox stave_selection_combobox; protected DStave stave; private JPanel scorePanel; private JScrollPane scroll; private String selectedInstrument = "Guitar (Clean) private JButton remove_last_note_button,play_button ; /** * Constructor Creates new form ScoreEditor **/ public ScoreEditor() { 027"; Phrase ph = new Phrase(); /*Vector<Note> notes = new Vector<Note>(); for (int i = 0; i < 40; i++) { double min = 41; double max = 78; double pitch = (max - min) * Math.random() + min; Note note1 = new Note((int) pitch, 2 * Math.random()); notes.add(note1); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 136 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» ph.addNoteList(notes, true); DGrandStave dg_stave = new DGrandStave(ph); */ DGrandStave dg_stave = new DGrandStave(); stave = dg_stave; stave.setTitle(selectedInstrument); stave.setDisplayTitle(true); scorePanel = new JPanel(); scorePanel.setBackground(Color.getHSBColor((float) 0.14, (float) 0.09,(float) 1.0)); // .17, .1, 1 scorePanel.add(stave, BorderLayout.CENTER); scroll = new JScrollPane(stave); scroll.setViewportView(scorePanel); setLayout(new BorderLayout()); add(scroll, BorderLayout.CENTER); stave_selection_combobox = new JComboBox(); stave_selection_combobox.setModel(new DefaultComboBoxModel(new String[] {"Grand Stave", "Treble Stave", "Bass Stave" })); stave_selection_combobox.setActionCommand("Select_Stave"); stave_selection_combobox.addActionListener(this); stave_selection_combobox.setSelectedItem(1); selectStave(); } /** * Method creates a "smart" JToggleButton * @param imageName The location of the image of this toggle-button * @param selected_imageName The location of the image of this toggle-button * when the toggle button is selected * @param actionCommand The action-command to set for this components * @param toolTipText the tooltip of the component * @param altText Alternative text in case the image is missing * @return A JToggleButton component */ protected JToggleButton makeToggleButton(String imageName, String selected_imageName,String actionCommand, String toolTipText, String altText) { // Look for the image. String imgLocation = "images/menu_images/" + imageName + ".gif"; String imgLocation2 = "images/menu_images/" + selected_imageName + ".gif"; URL imageURL = Main.class.getResource(imgLocation); URL imageURL2 = Main.class.getResource(imgLocation2); // Create and initialize the button. JToggleButton button = new JToggleButton(); button.setActionCommand(actionCommand); button.setToolTipText(toolTipText); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 137 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» button.addActionListener(this); if ( (imageURL != null) & (imageURL2 != null) ){ // image found button.setIcon(new ImageIcon(imageURL, altText)); button.setSelectedIcon(new ImageIcon(imageURL2, altText)); } else { // no image found button.setText(altText); System.err.println("Resource not found: " + imgLocation + "" + (imageURL==null) + " " + (imageURL2==null)); } return button; } /** * Method creates a "smart" JButton * @param imageName The location of the image of this button * @param actionCommand The action-command to set for this components * @param toolTipText the tooltip of the component * @param altText Alternative text in case the image is missing * @return A JButton component */ protected JButton makeButton(String imageName, String actionCommand, String toolTipText, String altText) { // Look for the image. String imgLocation = "images/menu_images/" + imageName + ".gif"; URL imageURL = Main.class.getResource(imgLocation); // Create and initialize the button. JButton button = new JButton(); button.setActionCommand(actionCommand); button.setToolTipText(toolTipText); button.setText(toolTipText); button.addActionListener(this); (imageURL != null) { // image found button.setIcon(new ImageIcon(imageURL, altText)); } else { // no image found button.setText(altText); System.err.println("Resource not found 2: " + imgLocation); } return button; if } /** * Method creates and returns the top part of the Score-Editor with the buttons * @return the JPanel created with control buttons */ public JPanel getJToolBar() { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 138 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // 1. Create the control panel JButton new_stave, metronome_button; new_stave = makeButton("clearscore", "Clear_Stave", "Clear Stave","altText"); play_button = makeButton("play", "Play", "Play", "altText"); remove_last_note_button = makeButton("removelastnote", "Remove_Last_Note","Remove Note", "altText"); remove_last_note_button.setEnabled(false); play_button.setEnabled(false); JPanel control_panel = new JPanel(); control_panel.setBorder(BorderFactory.createTitledBorder(null , "Control Buttons", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Tahoma", 0, 10))); control_panel.add(new_stave); control_panel.add(play_button); control_panel.add(remove_last_note_button); // 2. Create the NOTE selection panel JPanel note_panel = new JPanel(); note_panel.setBorder(BorderFactory.createTitledBorder(null, "Select Note", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Tahoma", 0, 10))); JToggleButton note16th_button, note_8th_button, note_4th_button,note_Half_button, note_Full_button; note16th_button = makeToggleButton("semiquaverUp","semiquaverUp_selected", "Note_16th","Semi quaver Up", "altText"); note_8th_button = makeToggleButton("quaverUp","quaverUp_selected", "Note_8th", "Quaver Up", "altText"); note_4th_button = makeToggleButton("crotchetUp", "crotchetUp_selected", "Note_4th","Crotchet Up", "altText"); note_Half_button = makeToggleButton("minimUp", "minimUp_selected", "Note_Half","Minim Up", "altText"); note_Full_button = makeToggleButton("semibreve", "semibreve_selected","Whole","Semi Breve", "altText"); note_panel.add(note16th_button); note_panel.add(note_8th_button); note_panel.add(note_4th_button); note_panel.add(note_Half_button); note_panel.add(note_Full_button); // 3. Create the REST selection panel JPanel rest_panel = new JPanel(); rest_panel.setBorder(BorderFactory.createTitledBorder(null, "Add Rest", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Tahoma", 0, 10))); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 139 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» JToggleButton semi_quaver_rest_button, quaver_rest_button, crotchet_rest_button,minim_rest_button, semibreve_rest_button; semi_quaver_rest_button = makeToggleButton("semiquaverRest","semiquaverRest", "Rest_16th","Semi quarter Rest", "altText"); quaver_rest_button = makeToggleButton("quaverRest", "quaverRest","Rest_8th","Quarter Rest", "altText"); crotchet_rest_button = makeToggleButton("crotchetRest", "crotchetRest","Rest_4th","Crotchet Rest", "altText"); minim_rest_button = makeToggleButton("minimRest", "minimRest","Rest_Half", "Minim Rest","altText"); semibreve_rest_button = makeToggleButton("semibreveRest", "semibreveRest", "Rest_Whole","Semi breve Rest", "altText"); rest_panel.add(semi_quaver_rest_button); rest_panel.add(quaver_rest_button); rest_panel.add(crotchet_rest_button); rest_panel.add(minim_rest_button); rest_panel.add(semibreve_rest_button); // 4. Only one Note or one Rest can be selected at a time, // So add all toggle buttons in a button group ButtonGroup group = new ButtonGroup(); group.add(note16th_button); group.add(note_8th_button); group.add(note_4th_button); group.add(note_Half_button); group.add(note_Full_button); group.add(semi_quaver_rest_button); group.add(quaver_rest_button); group.add(crotchet_rest_button); group.add(minim_rest_button); group.add(semibreve_rest_button); // 5. Add Instrument selection panel JPanel instrument_selection = new JPanel(); instrument_selection.setBorder(BorderFactory.createTitledBord er(null, "Options", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Tahoma", 0, 10))); JButton select_instrumnet_button = makeButton("selectinstrument", "Show_Instrument_Dialog", "Select Instrument", "altText"); instrument_selection.add(stave_selection_combobox); instrument_selection.add(select_instrumnet_button); JPanel main_panel = new JPanel(); main_panel.setLayout(new BoxLayout(main_panel,BoxLayout.X_AXIS)); main_panel.add(control_panel); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 140 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» main_panel.add(note_panel); main_panel.add(rest_panel); main_panel.add(instrument_selection); return main_panel; } /** * Method to listen and react to actions */ public void actionPerformed(ActionEvent e) { // Get the action-command associated with the event String action_command = e.getActionCommand(); if (action_command.equals("Note_16th")) stave.setNoteDuration(0.25); else if (action_command.equals("Note_8th")) stave.setNoteDuration(0.5); else if (action_command.equals("Note_4th")) stave.setNoteDuration(1.0); else if (action_command.equals("Note_Half")) stave.setNoteDuration(2.0); else if (action_command.equals("Note_Whole")) stave.setNoteDuration(4.0); else if (action_command.equals("Rest_16th")) setRest(0.25); else if (action_command.equals("Rest_8th")) setRest(0.5); else if (action_command.equals("Rest_4th")) setRest(1.0); else if (action_command.equals("Rest_Half")) setRest(2.0); else if (action_command.equals("Rest_Whole")) setRest(4.0); else if (action_command.equals("Select_Instrument")) selectStave(); else if (action_command.equals("Clear_Stave")) { remove_last_note_button.setEnabled(false); play_button .setEnabled(false); stave.setPhrase(new Phrase()); } else if (action_command.equals("Remove_Last_Note")) removeLastNote(); else if (action_command.equals("Show_Instrument_Dialog")) showInstrumentDialog(); else if (action_command.equals("Play")) playTune(); } public Part getPart() { Phrase phr = stave.getPhrase(); Part part = new Part(phr); String s = selectedInstrument; s = s.substring(21, 24); s.trim(); part.setInstrument(Integer.valueOf(s)); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 141 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» return part; } /** * Method that plays the tune in the stave */ protected void playTune() { Play.midi(getPart(), false); } /** * Method that shows an Instrument Selection Dialog */ private void showInstrumentDialog() { Object result = (Object) JOptionPane.showInputDialog(this, "","Select instrument", JOptionPane.PLAIN_MESSAGE, null, initializeInstrumentList(), selectedInstrument); String s = null; if (result instanceof String) s = (String) result; if (s != null) { selectedInstrument = s; stave.setTitle(s); stave.repaint(); } } /** * Method that removes last note in the stave */ private void removeLastNote() { Phrase phrase = stave.getPhrase(); if (phrase.getSize()>0) { phrase.removeLastNote(); stave.setPhrase(phrase); if (phrase.getSize()==0) { remove_last_note_button.setEnabled(false); play_button.setEnabled(false); } } } /** * Method that selects the stave */ private void selectStave() { Phrase phr = stave.getPhrase(); String item = (String) stave_selection_combobox.getSelectedItem(); scorePanel.remove(stave); if (item.equalsIgnoreCase("Grand Stave")) { stave = new DGrandStave(phr); scorePanel.add(stave, BorderLayout.CENTER); } else if (item.equalsIgnoreCase("Treble Stave")) { stave = new DTrebleStave(phr); scorePanel.add(stave, BorderLayout.CENTER); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 142 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } else if (item.equalsIgnoreCase("Bass Stave")) { stave = new DTrebleStave(phr); scorePanel.add(stave, BorderLayout.CENTER); } stave.setTitle(selectedInstrument); stave.setDisplayTitle(true); scorePanel.repaint(); } /** * This method adds a new Rest if no Note is selected, * or changes the selected note to a rest. * * @param rest_value Rest value is the value of the rest, in the range: * 4.0 (whole-rest) 2.0 (half-rest) 1.0 (quarter-rest) * 0.5 (eight-rest) 0.25 (sixteenth-rest) */ private void setRest(double rest_value) { Phrase phr = stave.getPhrase(); Note n; if (stave.getSelectedNote() == -1) { n = new Note(-2147483648, rest_value); phr.add(n); } else { n = phr.getNote(stave.getSelectedNote()); n.setPitch(-2147483648); n.setRhythmValue(rest_value); } remove_last_note_button.setEnabled(true); stave.repaint(); } /** * Method returns an array of valid Guitar instruments */ private String [] initializeInstrumentList() { String [] instrumentList = { "Guitar (Clean) 027", "Guitar (Distorted) 030", "Guitar Harmonics 031", "Guitar (Jazz) 026", "Guitar (Muted) 028", "Guitar (Nylon) 024", "Guitar (Overdrive) 029", "Guitar (Steel) 025" }; return instrumentList; } public void setEnabledRemove(boolean b) { remove_last_note_button.setEnabled(b); play_button.setEnabled(b); } } ========================================================================= ========================================================================= Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 143 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» package diamouses.ui.scoreEditor.staves; import java.awt.*; import java.awt.image.BufferedImage; import diamouses.ui.scoreEditor.GuitarNeck; import jm.JMC; import jm.music.data.*; public class DBassStave extends DStave implements JMC { public DBassStave() { super(); staveDelta = staveSpaceHeight*11/2; } public DBassStave(Phrase phrase) { super(phrase); staveDelta = staveSpaceHeight*11/2; } public void paintComponent(Graphics graphics) { // set up for double buffering if (image == null) { image = this.createImage(this.getSize().width, this.getSize().height); g = image.getGraphics(); } g.setFont(font); // keep track of the rhythmic values for bar lines double beatCounter = 0.0; // reset the chromatic vector previouslyChromatic.removeAllElements(); // reste note position locations notePositions.removeAllElements(); int keyAccidentals = 0; // add a title if set to be visible if (getDisplayTitle()) { g.drawString(title, rightMargin, bPos - 10); // insert key signature if required } int keyOffset = 0; // is the key signature using sharps or flats? if (keySignature > 0 && keySignature < 8) { // sharp for (int ks = 0; ks < keySignature; ks++) { // claulate position Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 144 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» int keyAccidentalPosition = notePosOffset[sharps[ks] % 12] + bPos - 4 + ((5 - sharps[ks] / 12) * 24) + ((6 sharps[ks] / 12) * 4); // draw sharp g.drawImage(sharp, rightMargin + clefWidth + keyOffset, keyAccidentalPosition + staveSpaceHeight , this); // indent position keyOffset += 10; //add note to accidental vector int theModValue = sharps[ks] % 12; for (int pc = 0; pc < 128; pc++) { if ((pc % 12) == theModValue) { previouslyChromatic.addElement(new Integer(pc)); keyAccidentals++; } } keySigWidth = keyOffset; } } else { if (keySignature < 0 && keySignature > -8) { // flat for (int ks = 0; ks < Math.abs(keySignature); ks++) { // claulate position int keyAccidentalPosition = notePosOffset[flats[ks] % 12] + bPos - 4 + ((5 - flats[ks] / 12) * 24) + ((6 flats[ks] / 12) * 4); // draw flat g.drawImage(flat, rightMargin + clefWidth + keyOffset, keyAccidentalPosition + staveSpaceHeight, this); // indent position keyOffset += 10; //add note to accidental vector int theModValue = sharps[ks] % 12; for (int pc = 0; pc < 128; pc++) { if ((pc % 12) == theModValue) { previouslyChromatic.addElement(new Integer(pc)); keyAccidentals++; } } keySigWidth = keyOffset; } } else { if (keySignature < 0 && keySignature > -8) { // flat for (int ks = 0; ks < Math.abs(keySignature); ks++) { // claulate position int keyAccidentalPosition = notePosOffset[flats[ks] % 12] + bPos - 4 + ((5 - flats[ks] / 12) * 24) + ((6 flats[ks] / 12) * 4); // draw flat g.drawImage(flat, rightMargin + clefWidth + keyOffset, keyAccidentalPosition + staveSpaceHeight, this); // indent position keyOffset += 10; //add note to accidental vector Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 145 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» int theModValue = flats[ks] % 12; for (int pc = 0; pc < 128; pc++) { if ((pc % 12) == theModValue) { previouslyChromatic.addElement(new Integer(pc)); keyAccidentals++; } } } } } keySigWidth = keyOffset + 3; // insert time signature if required if (metre != 0.0) { Image[] numbers = {one, two, three, four, five, six, seven, eight, nine}; // top number g.drawImage(numbers[(int) metre - 1], rightMargin + clefWidth + keySigWidth, bPos + 13, this); //bottom number g.drawImage(four, rightMargin + clefWidth + keySigWidth, bPos + 29, this); timeSigWidth = 30; } else timeSigWidth = 5; // set indent position for first note totalBeatWidth = rightMargin + clefWidth + keySigWidth + timeSigWidth; // draw notes and rests for (int i = 0; i < phrase.size(); i++) { int notePitchNum = (int) phrase.getNote(i).getPitch(); // choose graphic chooseImage(notePitchNum, phrase.getNote(i).getRhythmValue(), 50, 0, 50); // reset pitch for rests // position? int pitchTempPos; if (notePitchNum == REST || phrase.getNote(i).getRhythmValue() == 0.0) { // rest or delete pitchTempPos = notePosOffset[71 % 12] + bPos - 4 + ((5 - 71 / 12) * 24) + ((6 - 71 / 12) * 4); } else { pitchTempPos = notePosOffset[notePitchNum % 12] + bPos 4 + ((5 - notePitchNum / 12) * 24) + ((6 - notePitchNum / 12) * 4 – staveSpaceHeight * 6); } // accidental? if (((notePitchNum % 12) == 1 || (notePitchNum % 12) == 3 || (notePitchNum % 12) == 6 || (notePitchNum % 12) == 8 || (notePitchNum % 12) == 10) && notePitchNum != REST && phrase.getNote(i).getRhythmValue() != 0.0) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 146 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (keySignature > -1) { g.drawImage(sharp, totalBeatWidth - 9, pitchTempPos, this); previouslyChromatic.addElement(new Integer(notePitchNum - 1)); // enter the note made sharp i.e, F for an F# } else { // flat pitchTempPos -= 4; // to show the note a semitone higher for flats g.drawImage(flat, totalBeatWidth - 9, pitchTempPos, this); previouslyChromatic.addElement(new Integer(notePitchNum + 1)); notePitchNum++; // assume it is a semitone higher for legerlines etc... } } else { // check for a natural // check vector int size = previouslyChromatic.size(); for (int j = 0; j < size; j++) { Integer temp = (Integer) previouslyChromatic.elementAt(j); if (temp.intValue() == notePitchNum && notePitchNum != REST && phrase.getNote(i).getRhythmValue() != 0.0) { // add natural g.drawImage(natural, totalBeatWidth - 7, pitchTempPos, this); // remove element if not in key signature if (j > keyAccidentals - 1) { previouslyChromatic.removeElementAt(j); } j = size; } } } // draw note/rest if (currImage != null) { if (i==getSelectedNote()) { Note selected_note = phrase.getNote(i);//.getPitch(); GuitarNeck.getInstance().setNote(selected_note.getPitch()); //-System.out.println(selected_note.toString()); BufferedImage bi = DStave.toBufferedImage(currImage); int w = bi.getWidth(); int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, bi.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 147 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (pixel == Color.BLACK).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } currImage = DStave.toImage(biOut); } } g.drawImage(currImage, totalBeatWidth, pitchTempPos, this); // store position in a vector notePositions.addElement(new Integer(totalBeatWidth)); notePositions.addElement(new Integer(pitchTempPos + staveDelta)); // stave delta required for bass clef offset from treble //System.out.println("Position "+i+" "+totalBeatWidth + " "+ pitchTempPos); if (dottedNote) { boolean dotFlag = true; for (int l = 0; l < lineNotes.length; l++) { if (lineNotes[l] + 12 == notePitchNum || lineNotes[l] + 36 == notePitchNum || lineNotes[l] + 60 == notePitchNum || lineNotes[l] + 84 == notePitchNum || lineNotes[l] + 108 == notePitchNum || notePitchNum == REST) { g.drawImage(dot, totalBeatWidth + 1, pitchTempPos - 4, this); dotFlag = false; l = lineNotes.length; } } if (dotFlag) { g.drawImage(dot, totalBeatWidth + 1, pitchTempPos, this); } } // leger lines down if (notePitchNum <= 40 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 52, totalBeatWidth + 12, bPos + 52); } if (notePitchNum <= 37 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 60, totalBeatWidth + 12, bPos + 60); } if (notePitchNum <= 34 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 68, totalBeatWidth + 12, bPos + 68); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 148 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (notePitchNum <= 30 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 76, totalBeatWidth + 12, bPos + 76); } if (notePitchNum <= 26 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 84, totalBeatWidth + 12, bPos + 84); } // leger lines up if (notePitchNum >= 60 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 4, totalBeatWidth + 12, bPos + 4); } if (notePitchNum >= 64 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 4, totalBeatWidth + 12, bPos - 4); } if (notePitchNum >= 67 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 12, totalBeatWidth + 12, bPos - 12); } if (notePitchNum >= 71 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 20, totalBeatWidth + 12, bPos - 20); } if (notePitchNum >= 74 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 28, totalBeatWidth + 12, bPos - 28); // increment everything totalBeatWidth += currBeatWidth; dottedNote = false; // quantised to semiquvers! // (int)((phrase.getNote(i).getRhythmValue()/0.25) * 0.25); beatCounter += (int) (phrase.getNote(i).getRhythmValue() / 0.25) * 0.25; // add bar line if required if (metre != 0.0) { if ((beatCounter % metre) == 0.0) { g.drawLine(totalBeatWidth, bPos + 12, totalBeatWidth, bPos + 44); // add bar numbers? if (barNumbers) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 149 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» g.drawString("" + (int) (beatCounter / metre + 1 + phrase.getStartTime()), totalBeatWidth - 4, bPos); } totalBeatWidth += 12; } } } // draw stave for (int i = 0; i < 5; i++) { g.drawLine(rightMargin, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)); } // draw next note stave area // draw stave g.setColor(Color.lightGray); for (int i = 0; i < 5; i++) { g.drawLine(totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight),totalBeatWidth + 50,(bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } g.setColor(Color.black); // add Clef g.drawImage(bassClef, rightMargin + 7, bPos, this); /* Draw completed buffer to g */ graphics.drawImage(image, 0, 0, null); // clear image // clear g.setColor(this.getBackground()); g.fillRect(0, 0, getSize().width, getSize().height); g.setColor(this.getForeground()); //repaint(); //g.dispose(); } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor.staves; import java.awt.*; import java.awt.image.BufferedImage; import diamouses.ui.scoreEditor.GuitarNeck; import jm.JMC; import jm.music.data.*; public class DGrandStave extends DStave implements JMC { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 150 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public DGrandStave() { super(); bPos = 110; panelHeight = 310; this.setSize((int) (beatWidth * spacingValue), panelHeight); } public DGrandStave(Phrase phrase) { super(phrase); bPos = 110; panelHeight = 310; this.setSize((int) (beatWidth * spacingValue), panelHeight); } public void paintComponent(Graphics graphics) { // set up for double buffering if (image == null) { image = this.createImage(this.getSize().width, this.getSize().height); g = image.getGraphics(); } g.setFont(font); // keep track of the rhythmic values for bar lines double beatCounter = 0.0; // reset the chromatic vector previouslyChromatic.removeAllElements(); // reste note position locations notePositions.removeAllElements(); int keyAccidentals = 0; // add a title if set to be visible if (getDisplayTitle()) { g.drawString(title, rightMargin, 60); //bPos - 10); // insert key signature if required } int keyOffset = 0; // is the key signature using sharps or flats? if (keySignature > 0 && keySignature < 8) { // sharp for (int ks = 0; ks < keySignature; ks++) { // claulate position int keyAccidentalPosition = notePosOffset[sharps[ks] % 12] + bPos - 4 + ((5 - sharps[ks] / 12) * 24) + ((6 sharps[ks] / 12) * 4); // draw sharp on treble g.drawImage(sharp, rightMargin + clefWidth + keyOffset, keyAccidentalPosition, this); // draw sharp on bass g.drawImage(sharp, rightMargin + clefWidth + keyOffset, keyAccidentalPosition + staveSpaceHeight * 7, this); // indent position keyOffset += 10; //add note to accidental vector int theModValue = sharps[ks] % 12; for (int pc = 0; pc < 128; pc++) { if ((pc % 12) == theModValue) { previouslyChromatic.addElement(new Integer(pc)); keyAccidentals++; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 151 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } } keySigWidth = keyOffset; } } else { if (keySignature < 0 && keySignature > -8) { // flat for (int ks = 0; ks < Math.abs(keySignature); ks++) { // claulate position int keyAccidentalPosition = notePosOffset[flats[ks] % 12] + bPos - 4 + ((5 - flats[ks] / 12) * 24) + ((6 flats[ks] / 12) * 4); // draw flat g.drawImage(flat, rightMargin + clefWidth + keyOffset, keyAccidentalPosition, this); // draw flat on bass stave g.drawImage(flat, rightMargin + clefWidth + keyOffset, keyAccidentalPosition + staveSpaceHeight * 7, this); // indent position keyOffset += 10; //add note to accidental vector int theModValue = flats[ks] % 12; for (int pc = 0; pc < 128; pc++) { if ((pc % 12) == theModValue) { previouslyChromatic.addElement(new Integer(pc)); keyAccidentals++; } } } } } keySigWidth = keyOffset + 3; // insert time signature if required if (metre != 0.0) { Image[] numbers = {one, two, three, four, five, six, seven, eight, nine}; // top number g.drawImage(numbers[(int) metre - 1], rightMargin + clefWidth + keySigWidth, bPos + 13, this); g.drawImage(numbers[(int) metre - 1], rightMargin + clefWidth + keySigWidth, bPos + 13 + staveSpaceHeight * 6, this); //bottom number g.drawImage(four, rightMargin + clefWidth + keySigWidth, bPos + 29, this); g.drawImage(four, rightMargin + clefWidth + keySigWidth, bPos + 29 + staveSpaceHeight * 6, this); timeSigWidth = 30; } else { timeSigWidth = 5; // set indent position for first note } totalBeatWidth = rightMargin + clefWidth + keySigWidth + timeSigWidth; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 152 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // draw notes and rests for (int i = 0; i < phrase.size(); i++) { int notePitchNum = (int) phrase.getNote(i).getPitch(); // choose graphic chooseImage(notePitchNum, phrase.getNote(i).getRhythmValue(), 71, 60, 50); // reset pitch for rests // position? int pitchTempPos; if (notePitchNum == REST || phrase.getNote(i).getRhythmValue() == 0.0) { // rest or delete pitchTempPos = notePosOffset[71 % 12] + bPos - 4 + ((5 - 71 / 12) * 24) + ((6 - 71 / 12) * 4); } else { pitchTempPos = notePosOffset[notePitchNum % 12] + bPos 4 + ((5 - notePitchNum / 12) * 24) + ((6 - notePitchNum / 12) * 4); } // accidental? if (((notePitchNum % 12) == 1 || (notePitchNum % 12) == 3 || (notePitchNum % 12) == 6 || (notePitchNum % 12) == 8 || (notePitchNum % 12) == 10) && notePitchNum != REST && phrase.getNote(i).getRhythmValue() != 0.0) { if (keySignature > -1) { g.drawImage(sharp, totalBeatWidth - 9, pitchTempPos, this); previouslyChromatic.addElement(new Integer(notePitchNum - 1)); // enter the note made sharp i.e, F for an F# } else { // flat pitchTempPos -= 4; // to show the note a semitone higher for flats g.drawImage(flat, totalBeatWidth - 9, pitchTempPos, this); previouslyChromatic.addElement(new Integer(notePitchNum + 1)); notePitchNum++; // assume it is a semitone higher for legerlines etc... } } else { // check for a natural // check vector int size = previouslyChromatic.size(); for (int j = 0; j < size; j++) { Integer temp = (Integer) previouslyChromatic.elementAt(j); if (temp.intValue() == notePitchNum && notePitchNum != REST && phrase.getNote(i).getRhythmValue() != 0.0) { // add natural g.drawImage(natural, totalBeatWidth - 7, pitchTempPos, this); // remove element if not in key signature if (j > keyAccidentals - 1) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 153 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» previouslyChromatic.removeElementAt(j); } j = size; } } } /* draw the selection line if ((i >= getSelectedNote()) && (i <= getSelectedNote() + getSelectedArea())) { g.setColor(new Color(255, 0, 0)); g.drawLine(totalBeatWidth, 10, totalBeatWidth + currImage.getWidth(this), 10); g.drawLine(totalBeatWidth, 10, totalBeatWidth, 12); g.drawLine(totalBeatWidth + currImage.getWidth(this), 10, totalBeatWidth + currImage.getWidth(this), 12); g.setColor(new Color(0, 0, 0)); } */ // draw note/rest if (currImage != null) { if (i==getSelectedNote()) { Note selected_note = phrase.getNote(i);//.getPitch(); GuitarNeck.getInstance().setNote(selected_note.getPitch()); //-System.out.println(selected_note.toString()); BufferedImage bi = DStave.toBufferedImage(currImage); int w = bi.getWidth(); int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, bi.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); if (pixel == (Color.BLACK).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } currImage = DStave.toImage(biOut); } } g.drawImage(currImage, totalBeatWidth, pitchTempPos, this); // store position in a vector notePositions.addElement(new Integer(totalBeatWidth)); notePositions.addElement(new Integer(pitchTempPos)); if (dottedNote) { boolean dotFlag = true; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 154 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» for (int l = 0; l < lineNotes.length; l++) { if (lineNotes[l] + 12 == notePitchNum || lineNotes[l] + 36 == notePitchNum || lineNotes[l] + 60 == notePitchNum || lineNotes[l] + 84 == notePitchNum || lineNotes[l] + 108 == notePitchNum || notePitchNum == REST) { g.drawImage(dot, totalBeatWidth + 1, pitchTempPos - 4, this); dotFlag = false; l = lineNotes.length; } } if (dotFlag) { g.drawImage(dot, totalBeatWidth + 1, pitchTempPos, this); } } // leger lines middle C if (notePitchNum == 60 || notePitchNum == 61 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 52, totalBeatWidth + 12, bPos + 52); } // leger lines down if (notePitchNum <= 40 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 100, totalBeatWidth 12, bPos + 100); } if (notePitchNum <= 37 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 108, totalBeatWidth 12, bPos + 108); } // leger lines down low if (notePitchNum <= 16 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 156, totalBeatWidth 12, bPos + 156); } if (notePitchNum <= 13 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 164, totalBeatWidth 12, bPos + 164); } if (notePitchNum <= 10 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 172, totalBeatWidth 12, bPos + 172); } if (notePitchNum <= 6 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 180, totalBeatWidth 12, bPos + 180); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων + + + + + + 155 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if (notePitchNum <= 3 && notePitchNum > -1 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 188, totalBeatWidth + 12, bPos + 188); } // leger lines up if (notePitchNum >= 81 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos + 4, totalBeatWidth + 12, bPos + 4); } if (notePitchNum >= 84 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 4, totalBeatWidth + 12, bPos - 4); } // leger lines up high if (notePitchNum >= 105 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 52, totalBeatWidth + 12, bPos - 52); } if (notePitchNum >= 108 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 60, totalBeatWidth + 12, bPos - 60); } if (notePitchNum >= 112 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 68, totalBeatWidth + 12, bPos - 68); } if (notePitchNum >= 115 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 76, totalBeatWidth + 12, bPos - 76); } if (notePitchNum >= 119 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 84, totalBeatWidth + 12, bPos - 84); } if (notePitchNum >= 122 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 92, totalBeatWidth + 12, bPos - 92); } if (notePitchNum >= 125 && notePitchNum < 128 && phrase.getNote(i).getRhythmValue() != 0.0) { g.drawLine(totalBeatWidth - 3, bPos - 100, totalBeatWidth + 12, bPos - 100); } // increment everything totalBeatWidth += currBeatWidth; dottedNote = false; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 156 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // quantised to semiquvers! beatCounter += (int) (phrase.getNote(i).getRhythmValue() / 0.25) * 0.25; // add bar line if required if (metre != 0.0) { if ((beatCounter % metre) == 0.0) { g.drawLine(totalBeatWidth, bPos + 12 staveSpaceHeight * 7, totalBeatWidth, bPos + 44 + staveSpaceHeight * 13); // add bar numbers? if (barNumbers) { g.drawString("" + (int) (beatCounter / metre + 1 + phrase.getStartTime()), totalBeatWidth - 4, bPos - 50); } totalBeatWidth += 12; } } } // draw treble stave for (int i = 0; i < 5; i++) { g.drawLine(rightMargin, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } // draw bass stave for (int i = 6; i < 11; i++) { g.drawLine(rightMargin, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } g.setColor(Color.darkGray); // draw upper treble stave for (int i = -7; i < -2; i++) { g.drawLine(rightMargin, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } // draw lower bass stave for (int i = 13; i < 18; i++) { g.drawLine(rightMargin, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 157 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } // draw next note stave area // draw stave g.setColor(Color.lightGray); for (int i = 0; i < 5; i++) { g.drawLine(totalBeatWidth, (bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight),totalBeatWidth + 50,(bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } for (int i = 6; i < 11; i++) { g.drawLine(totalBeatWidth,(bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight), totalBeatWidth + 50,(bPos + imageHeightOffset - (2 * staveSpaceHeight)) + (i * staveSpaceHeight)); } g.setColor(Color.black); // add Clefs g.drawImage(trebleClef, rightMargin + 7, bPos - 4, this); g.drawImage(bassClef, rightMargin + 7, bPos + staveSpaceHeight * 6, this); /* Draw completed buffer to g */ graphics.drawImage(image, 0, 0, null); // clear image // clear g.setColor(this.getBackground()); g.fillRect(0, 0, getSize().width, getSize().height); g.setColor(this.getForeground()); //repaint(); //g.dispose(); } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor.staves; import import import import java.awt.event.*; java.awt.image.BufferedImage; java.awt.*; java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JPanel; import import import import jm.JMC; jm.gui.cpn.Images; jm.gui.cpn.ToolkitImages; jm.music.data.*; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 158 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** * An AWT Component for displaying a Common Practice Notation stave. * * @author Andrew Brown, Adam Kirby * @version 8th July 2001 */ public abstract class KeyListener { DStave extends JPanel implements JMC, private int selectedNote = -1; private int selectedArea = 0; protected boolean requiresMoreThanOneImage = false; protected double excessRhythmValue = 0.0; protected boolean isUp = true; protected boolean isNote = false; // for double buffering public Image image; protected Graphics g; // attributes protected Image trebleClef, bassClef, crotchetUp, crotchetDown, quaverDown, quaverUp,semiquaverDown, semiquaverUp, minimDown, minimUp, semibreve, dot,semiquaverRest, quaverRest, crotchetRest, minimRest, semibreveRest,sharp, flat, natural, one, two, three, four, five, six, seven, eight, nine, delete, tieOver, tieUnder; public int staveSpaceHeight = 8, rightMargin = 20, beatWidth = 43, staveWidth = beatWidth*15,imageHeightOffset = 28, clefWidth = 38, timeSigWidth = 5, keySigWidth = 5; public int bPos = 28; protected Phrase phrase; protected Image currImage; protected int currBeatWidth, totalBeatWidth; protected boolean dottedNote = false; protected int[] notePosOffset = {24,24,20,20,16,12,12,8,8,4,4,0}; // chromatic scale protected double metre = 4.0; protected int keySignature = 0; // postive numbers = sharps, negative numbers = flats protected int[] sharps = {77, 72, 79, 74, 69, 76, 71}; protected int[] flats = {71, 76, 69, 74, 67, 72, 65}; protected Vector previouslyChromatic = new Vector(); protected int[] lineNotes = {0, 1, 4, 7, 8, 11, 14, 15, 17, 18, 21, 22}; public Vector notePositions = new Vector(); protected int maxPitch = 127, minPitch = 0; protected String title; protected boolean barNumbers = false, editable = true, qtOn = false; protected int panelHeight = 110, staveDelta = 0; protected boolean displayTitle = false; protected Font font = new Font("Helvetica", Font.PLAIN, 10); protected int spacingValue = 70; private double noteDuration=1.0; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 159 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // constructor /** * Constructs a new stave to display a blank Phrase using the default stave * images. */ public DStave() { this(new Phrase(), new ToolkitImages()); } /** * Constructs a new stave to display the specified <code>phrase</code> using * the default stave images. * * @param phrase Phrase to be displayed in stave */ public DStave(Phrase phrase) { this(phrase, new ToolkitImages()); } public int getSelectedNote(){ return selectedNote; } public void setSelectedNote(int note){ System.out.println("Selected note = " + note); this.selectedNote=note; } public int getSelectedArea(){ return selectedArea; } public void setSelectedArea(int area){ this.selectedArea=area; } /** * Constructs a new stave to display a blank Phrase using the specified * stave <code>images</code>. * * @param images Images representing notes, rest and other stave elements to use within the compenent */ public DStave(Images images) { this(new Phrase(), images); } /** * Constructs a new stave to display the specified <code>phrase</code> using * the specified stave <code>images</code>. * * @param phrase Phrase to be displayed in stave * @param images Images representing notes, rest and other stave elements to use within the compenent */ Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 160 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public DStave(Phrase phr, Images images) { super(); title = phr.getTitle(); this.phrase = addRequiredRests(phr); // change 'paper' colour this.setBackground(Color.getHSBColor((float)0.14,(float)0.09,(flo at)1.0)); // .17, .1, 1 // set the appropriate size (at least 8 bars of 4/4) for the stave this.setSize((int)(beatWidth*spacingValue), panelHeight); if (this.getSize().width < (int)(phrase.getEndTime()* beatWidth * 1.5) ) this.setSize( (int)(phrase.getEndTime()* beatWidth * 1.5), panelHeight); // compensate for overly large images - pain!! //if (this.getSize().width > 5000) { // this.setSize(5000, panelHeight); // System.out.println("Not all the phrase can be shown due to overly large image requirements - sorry"); //} //System.out.println("Max size is "+this.getMaximumSize().width +" "+ this.getMaximumSize().height); // register the listerners DStaveActionHandler handleActions = new DStaveActionHandler(this); this.addMouseListener(handleActions); this.addMouseMotionListener(handleActions); // this.addKeyListener(handleActions); trebleClef = images.getTrebleClef(); bassClef = images.getBassClef(); crotchetDown = images.getCrotchetDown(); crotchetUp = images.getCrotchetUp(); quaverDown = images.getQuaverDown(); quaverUp = images.getQuaverUp(); semiquaverDown = images.getSemiquaverDown(); semiquaverUp = images.getSemiquaverUp(); minimDown = images.getMinimDown(); minimUp = images.getMinimUp(); semibreve = images.getSemibreve(); dot = images.getDot(); semiquaverRest = images.getSemiquaverRest(); quaverRest = images.getQuaverRest(); crotchetRest = images.getCrotchetRest(); minimRest = images.getMinimRest(); semibreveRest = images.getSemibreveRest(); sharp = images.getSharp(); flat = images.getFlat(); natural = images.getNatural(); one = images.getOne(); two = images.getTwo(); three = images.getThree(); four = images.getFour(); five = images.getFive(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 161 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» six = images.getSix(); seven = images.getSeven(); eight = images.getEight(); nine = images.getNine(); delete = images.getDelete(); tieOver = images.getTieOver(); tieUnder = images.getTieUnder(); } /* * Puts rests at the start of an phrase that does not * start at time 0.0. */ public Phrase addRequiredRests(Phrase phrase) { // add rests if required at the start if (phrase.getStartTime() > 0.0) { Phrase tempPhrase = new Phrase(0.0); double remTime = phrase.getStartTime(); while(remTime >= 4.0) { tempPhrase.addNote(REST, 4.0); remTime -= 4.0; } while(remTime >= 1.0) { tempPhrase.addNote(REST, 1.0); remTime -= 1.0; } tempPhrase.addNote(REST, remTime); jm.music.tools.Mod.append(tempPhrase, phrase); phrase = tempPhrase; } return phrase; } /** * Sets the current Phrase for this Stave instance * @param Phrase */ public void setPhrase(Phrase phr) { this.phrase = addRequiredRests(phr); previouslyChromatic.removeAllElements(); //setTitle(phr.getTitle()); repaint(); } /** * Returns the current Phrase of this Stave instance */ public Phrase getPhrase() { return this.phrase; } /** * Sets the name for this Stave instance * @param String Specify the title of the score */ public void setTitle(String title) { this.title = title; if(this.phrase != null) this.phrase.setTitle(title); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 162 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** * Returns the name for this Stave instance * @return String The title of the score */ public String getTitle() { return title; } /** * Emptys the name of this Stave instance */ public void removeTitle() { this.title = null; } /** * Show the title or not. * @param value True or false */ public void setDisplayTitle(boolean value) { this.displayTitle = value; this.repaint(); } /** * Is the title displayed or not. * @param value True or false */ public boolean getDisplayTitle() { return this.displayTitle; } /** * Return the recommended height for this stave. */ public int getPanelHeight() { return panelHeight; } /** * Sets the current metre for this Stave instance * This effects the displayed time signature. 4.0 = 4/4 etc. * @param double */ public void setMetre(double timeSig) { /* System.out.print("Time Sig ="); System.out.println(timeSig); System.out.print("Numerator ="); System.out.println(phrase.getNumerator()); System.out.print("Denominator ="); System.out.println(phrase.getDenominator()); */ this.metre = timeSig; } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 163 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** * returns the current metre for this Stave instance as a double */ public double getMetre() { return this.metre; } /** * returns the current major key for this Stave instance as a integer * 0 is C, 1 is C#/Db major, 2 is D major, etc */ public int getMajorKey() { int[] keys = {11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1}; return keys[keySignature + 7]; } /** * Sets the current key signature for this Stave instance * This effects the displayed key signature. 1 = F# etc. * 0 is no key signature, + numbers for sharps, - numbers for flats * @param int */ public void setKeySignature(int key) { this.keySignature = key; } /** * returns the current key signature for this Stave instance as a double */ public int getKeySignature() { return this.keySignature; } /** * Decide to show bar numbers or not * @param boolean */ public void setBarNumbers(boolean show) { this.barNumbers = show; } /** * Decide to allow stave to be editable or not * @param boolean */ public void setEditable(boolean state) { this.editable = state; } /** * returns the current minimum MIDI pitch number */ public int getMinPitch() { return this.minPitch; } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 164 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** * Decide the minimum MIDI pitch number for this stave * @param int */ public void setMinPitch(int min) { this.minPitch = min; } /** * returns the current maximum MIDI pitch number */ public int getMaxPitch() { return this.maxPitch; } /** * Decide the maxinum MIDI pitch number for this stave * @param int */ public void setMaxPitch(int max) { this.maxPitch = max; } /** * Returns the current next note position in pixels */ public int getTotalBeatWidth() { return this.totalBeatWidth; } /** * Sets the current width of the stave in pixels * @param int */ public void setTotalBeatWidth(int width) { this.totalBeatWidth = width; } /** * Returns the current state of barNumber showing */ public boolean getBarNumbers() { return barNumbers; } /** * Called by outer containers */ public Dimension getPreferredSize() { return new Dimension( this.getSize().width, this.getSize().height); } /** * Returns the current state of QuickTime Playback */ public boolean getQtOn() { return qtOn; } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 165 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** * Sets the current state of QuickTime * @param boolean */ public void setQtOn(boolean state) { this.qtOn = state; } /** * Called by stave action on mouseUp * Can be overridden by extending classes * to add functionality */ public void updateChange() {} // override update for double buffering public void update(Graphics g) { paintComponent(g); }; public void paintComponent(Graphics graphics) { // overridden by each class which extends Stave } /** * Remove the last note from the phrase */ public void deleteLastNote() { if(phrase.size() > 0) { phrase.removeNote(phrase.size() -1); repaint(); updateChange(); } } protected void chooseImage(int pitch, double rhythmValue, int upPitch1, int downPitch, int upPitch2) { if (pitch == Note.REST) { isNote = false; if (rhythmValue <= 0.0) { currImage = delete; currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.2501) { currImage = semiquaverRest; currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.501) { currImage = quaverRest; currBeatWidth = (int) (beatWidth * (2.0 / 3.0)); } else if (rhythmValue <= 0.7501) { currImage = quaverRest; currBeatWidth = (int) (beatWidth * (2.0 / 3.0)); dottedNote = true; } else if (rhythmValue <= 1.001) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 166 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } } } } } } } } } currImage = crotchetRest; currBeatWidth = beatWidth; else if (rhythmValue <= 1.2501) { currImage = crotchetRest; currBeatWidth = beatWidth; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.0; else if (rhythmValue <= 1.501) { currImage = crotchetRest; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; else if (rhythmValue <= 1.7501) { currImage = crotchetRest; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.5; else if (rhythmValue <= 2.001) { currImage = minimRest; currBeatWidth = (int) (beatWidth * 1.7); else if (rhythmValue <= 2.7501) { currImage = minimRest; currBeatWidth = (int) (beatWidth * 1.7); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 2.0; else if (rhythmValue <= 3.001) { currImage = minimRest; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; else if (rhythmValue <= 3.7501) { currImage = minimRest; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 3.0; else if (rhythmValue <= 4.001) { currImage = semibreveRest; currBeatWidth = (int) (beatWidth * 0.5); else { currImage = semibreveRest; currBeatWidth = (int) (beatWidth * 0.5); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 4.0; } } else { // a note rather than a rest isNote = true; if ((pitch < upPitch1 && pitch >= downPitch) || pitch < upPitch2 ) { // stems down isUp = true; if (rhythmValue <= 0.001) { currImage = delete; currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.2501) { currImage = semiquaverUp; currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.501) { currImage = quaverUp; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 167 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» currBeatWidth = (int) (beatWidth * (2.0 / 3.0)); } else if (rhythmValue <= 0.7501) { currImage = quaverUp; currBeatWidth = (int) (beatWidth * 0.67); dottedNote = true; } else if (rhythmValue <= 1.001) { currImage = crotchetUp; currBeatWidth = beatWidth; } else if (rhythmValue <= 1.2501) { currImage = crotchetUp; currBeatWidth = beatWidth; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.0; } else if (rhythmValue <= 1.501) { currImage = crotchetUp; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; } else if (rhythmValue <= 1.7501) { currImage = crotchetUp; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.5; } else if (rhythmValue <= 2.001) { currImage = minimUp; currBeatWidth = (int) (beatWidth * 1.7); } else if (rhythmValue <= 2.7501) { currImage = minimUp; currBeatWidth = (int) (beatWidth * 1.7); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 2.0; } else if (rhythmValue <= 3.001) { currImage = minimUp; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; } else if (rhythmValue <= 3.7501) { currImage = minimUp; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 3.0; } else if (rhythmValue <= 4.001) { currImage = semibreve; currBeatWidth = (int) (beatWidth * 2.25); } else { currImage = semibreve; currBeatWidth = (int) (beatWidth * 2.25); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 4.0; } } else { // stem down isUp = false; if (rhythmValue <= 0.001) { currImage = delete; currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.2501) { currImage = semiquaverDown; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 168 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» currBeatWidth = (int) (beatWidth * 0.5); } else if (rhythmValue <= 0.501) { currImage = quaverDown; currBeatWidth = (int) (beatWidth * (2.0 / 3.0)); } else if (rhythmValue <= 0.7501) { currImage = quaverDown; currBeatWidth = (int) (beatWidth * (2.0 / 3.0)); dottedNote = true; } else if (rhythmValue <= 1.001) { currImage = crotchetDown; currBeatWidth = beatWidth; } else if (rhythmValue <= 1.2501) { currImage = crotchetDown; currBeatWidth = beatWidth; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.0; } else if (rhythmValue <= 1.501) { currImage = crotchetDown; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; } else if (rhythmValue <= 1.7501) { currImage = crotchetDown; currBeatWidth = (int) (beatWidth * 1.5); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 1.5; } else if (rhythmValue <= 2.001) { currImage = minimDown; currBeatWidth = (int) (beatWidth * 1.7); } else if (rhythmValue <= 2.7501) { currImage = minimDown; currBeatWidth = (int) (beatWidth * 1.7); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 2.0; } else if (rhythmValue <= 3.001) { currImage = minimDown; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; } else if (rhythmValue <= 3.7501) { currImage = minimDown; currBeatWidth = (int) (beatWidth * 1.9); dottedNote = true; requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 3.0; } else if (rhythmValue <= 4.001) { currImage = semibreve; currBeatWidth = (int) (beatWidth * 2.25); } else { currImage = semibreve; currBeatWidth = (int) (beatWidth * 2.25); requiresMoreThanOneImage = true; excessRhythmValue = rhythmValue - 4.0; } } } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 169 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { System.out.println(e.getKeyChar()); } public double getNoteDuration(){ return this.noteDuration; } public void setNoteDuration(double noteDuration){ this.noteDuration=noteDuration; } // This method returns a buffered image with the contents of an image public static BufferedImage toBufferedImage(Image image) { if (image instanceof BufferedImage) { return (BufferedImage)image; } // This code ensures that all the pixels in the image are loaded image = new ImageIcon(image).getImage(); // Determine if the image has transparent pixels; for this method's // implementation, see e661 Determining If an Image Has Transparent Pixels // Create a buffered image with a format that's compatible with the screen BufferedImage bimage = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); try { // Determine the type of transparency of the new buffered image // int transparency = Transparency.OPAQUE; //boolean hasAlpha = hasAlpha(image); //if (hasAlpha) { int transparency = Transparency.BITMASK; //} // Create the buffered image GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); bimage = gc.createCompatibleImage( image.getWidth(null), image.getHeight(null), transparency); } catch (HeadlessException e) { // The system does not have a screen } if (bimage == null) { // Create a buffered image using the default color model //int type = BufferedImage.TYPE_INT_RGB; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 170 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» //if (hasAlpha) { int type = BufferedImage.TYPE_INT_ARGB; //} bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); } // Copy image to buffered image Graphics g = bimage.createGraphics(); // Paint the image onto the buffered image g.drawImage(image, 0, 0, null); g.dispose(); return bimage; } // This method returns an Image object from a buffered image public static Image toImage(BufferedImage bufferedImage) { return Toolkit.getDefaultToolkit().createImage(bufferedImage.getSource()); } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor.staves; import import import import import import import import import import import java.awt.Cursor; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.InputEvent; java.awt.event.KeyEvent; java.awt.event.KeyListener; java.awt.event.MouseEvent; java.awt.event.MouseListener; java.awt.event.MouseMotionListener; javax.swing.JMenuItem; javax.swing.JPopupMenu; import import import import diamouses.ui.scoreEditor.Main; jm.JMC; jm.music.data.Note; jm.music.data.Phrase; DStaveActionHandler public class implements JMC, MouseListener, MouseMotionListener, ActionListener, KeyListener { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 171 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private DStave theApp; private int selectedNote = -1; private boolean topTimeSelected = false, keySelected = false; private int clickedPosY, clickedPosX, storedPitch = 72; private double[] rhythmValues = {104.0, 103.0, 102.0, 101.5, 101.0, 100.75,100.5, 100.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0}; private boolean button1Down = false; private JPopupMenu noteContextMenu; private JMenuItem editNote, repeatNote, makeRest, deleteNote, // constructor DStaveActionHandler(DStave stave) { theApp = stave; noteContextMenu = new JPopupMenu(); repeatNote = new JMenuItem("Repeat Note"); repeatNote.addActionListener(this); noteContextMenu.add(repeatNote ); makeRest = new JMenuItem("Change to Rest"); makeRest.addActionListener(this); noteContextMenu.add(makeRest); deleteNote = new JMenuItem("Delete Note"); deleteNote.addActionListener(this); noteContextMenu.add(deleteNote ); theApp.add(noteContextMenu); } boolean inNoteArea( MouseEvent e ) { Integer lastX; if (theApp.notePositions.size() < 2) { lastX = new Integer(theApp.getTotalBeatWidth() ); } else { lastX = (Integer)theApp.notePositions.elementAt(theApp.notePositions. size() -2); } return (e.getX() <= lastX.intValue() + 15) && (e.getX() < theApp.getTotalBeatWidth() + 50); } private void searchForSelectedNote(MouseEvent e) { Integer tempX; Integer tempY; for(int i=0;i< theApp.notePositions.size(); i += 2) { tempX = (Integer)theApp.notePositions.elementAt(i); tempY = (Integer)theApp.notePositions.elementAt(i+1); if((e.getX() > tempX.intValue()) && Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 172 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» (e.getX() < tempX.intValue() + 15) && ( e.getY() + theApp.staveDelta > tempY.intValue() + 22) && (e.getY() + theApp.staveDelta < tempY.intValue()+35)){ selectedNote = i/2; clickedPosY = e.getY() + theApp.staveDelta; clickedPosX = e.getX(); // get out of loop ASAP i = theApp.notePositions.size(); System.out.println("LALA"); } } } // Mouse Listener stubs public void mouseClicked(MouseEvent e) { System.out.println("Mouse Clicked"); if ((((e.getModifiers() == InputEvent.BUTTON3_MASK))) && inNoteArea(e)) { searchForSelectedNote(e); if ((selectedNote >= 0) && (selectedNote < theApp.getPhrase().size())) { noteContextMenu.show(theApp, e.getX(), e.getY()); } }else if ((((e.getModifiers() == InputEvent.BUTTON1_MASK))) && inNoteArea(e)) { searchForSelectedNote(e); if ((selectedNote >= 0) && (selectedNote < theApp.getPhrase().size())) { if(selectedNote==theApp.getSelectedNote()){ theApp.setSelectedNote(-1); theApp.setSelectedArea(0); }else{ theApp.setSelectedNote(selectedNote); theApp.setSelectedArea(0); } }else{ theApp.setSelectedNote(-1); theApp.setSelectedArea(0); } } // alg0 System.out.println(theApp.phrase.getSize()); Main.setEnabledRemove(theApp.phrase.getSize() >= 1); } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} //mouseMotionListener stubs public void mouseMoved(MouseEvent e) {} Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 173 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public void mousePressed(MouseEvent e) { if (e.isPopupTrigger() || ((e.getModifiers() & InputEvent.BUTTON2_MASK) != 0) || (!theApp.editable)) return; button1Down = true; // add a new note if necessary // no notes yet? if(!inNoteArea(e)) { // new note close to mouse click pitch int newPitch = 85 - (e.getY() + theApp.staveDelta theApp.bPos)/2; // make sure it is not an accidental int[] blackNotes = {1,3,6,8,10}; boolean white = true; for(int k=0;k<blackNotes.length;k++) { if(newPitch%12 == blackNotes[k]) newPitch--; } Note n = new Note(newPitch,theApp.getNoteDuration()); Phrase phr = theApp.getPhrase(); phr.addNote(n); theApp.repaint(); // set cursor theApp.setCursor(new Cursor(Cursor.HAND_CURSOR)); // get ready to drag it selectedNote = phr.size()-1; //theApp.playCurrentNote(selectedNote); clickedPosY = e.getY() + theApp.staveDelta; clickedPosX = e.getX(); } else { searchForSelectedNote(e); theApp.setCursor(new Cursor(Cursor.MOVE_CURSOR)); // check for a click on a note - head? } if (selectedNote < 0) { // no note clicked on or yet made // check which note to insert for(int j=0;j< theApp.notePositions.size() - 2; j += 2) { Integer tempX = (Integer)theApp.notePositions.elementAt(j); Integer nextTempX = (Integer)theApp.notePositions.elementAt(j+2); if(e.getX() > tempX.intValue() + 15 && e.getX() < nextTempX.intValue()) { // change cursor theApp.setCursor(new Cursor(Cursor.HAND_CURSOR)); // add new note close to mouse clicked pitch int newPitch = 85 - (e.getY() + theApp.staveDelta theApp.bPos)/2; // make sure it is not an accidental int[] blackNotes = {1,3,6,8,10}; boolean white = true; for(int k=0;k<blackNotes.length;k++) { if(newPitch%12 == blackNotes[k]) newPitch--; } Note n = new Note(newPitch ,theApp.getNoteDuration()); Phrase phr = theApp.getPhrase(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 174 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» phr.getNoteList().insertElementAt(n, j/2 + 1); theApp.repaint(); // play and update variables for dragging it selectedNote = j/2 + 1; clickedPosY = e.getY() + theApp.staveDelta; clickedPosX = e.getX(); // get out of the loop j = theApp.notePositions.size(); } } } // check if the time signature is clicked int theSpace = theApp.rightMargin + theApp.clefWidth + theApp.keySigWidth; if(e.getX() > theSpace && e.getX() < theSpace + 10) { theApp.setCursor(new Cursor(Cursor.MOVE_CURSOR)); topTimeSelected = true; clickedPosY = e.getY() + theApp.staveDelta; clickedPosX = e.getX(); } // check if the key signature is clicked int theClefSpace = theApp.rightMargin + theApp.clefWidth; int minKeySpace = 10; if (theApp.keySigWidth > minKeySpace) minKeySpace = theApp.keySigWidth; if(e.getX() > theClefSpace - 10 && e.getX() < theClefSpace + minKeySpace) { theApp.setCursor(new Cursor(Cursor.MOVE_CURSOR)); keySelected = true; clickedPosY = e.getY() + theApp.staveDelta; clickedPosX = e.getX(); } // alg0 System.out.println(theApp.phrase.getSize()); Main.setEnabledRemove(theApp.phrase.getSize() >= 1); } public void mouseDragged(MouseEvent e) { if ((!button1Down) || (!theApp.editable)) return; //theApp.dragNote(e.getX(), e.getY()); if (selectedNote >= 0) { Phrase phr = theApp.getPhrase(); Note n = phr.getNote(selectedNote); // move note down if(e.getY() + theApp.staveDelta > clickedPosY + 2 && theApp.getPhrase().getNote(selectedNote).getPitch() != REST) { n.setPitch(n.getPitch() - 1); if (n.getPitch() < theApp.getMinPitch()) n.setPitch(theApp.getMinPitch()); // update the current mouse location clickedPosY += 2; // update the visual display Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 175 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» theApp.repaint(); // keep pitch to reinstate it after displaying a rest storedPitch = n.getPitch(); } // move note up if(e.getY() + theApp.staveDelta < clickedPosY - 2 && theApp.getPhrase().getNote(selectedNote).getPitch() != REST) { n.setPitch(n.getPitch() + 1); if (n.getPitch() > theApp.getMaxPitch()) n.setPitch(theApp.getMaxPitch()); //theApp.playCurrentNote(selectedNote); clickedPosY -= 2; theApp.repaint(); storedPitch = n.getPitch(); } // move note right - increase RV if(e.getX() > clickedPosX + 6) { double tempRV = n.getRhythmValue(); int tempPitch = n.getPitch(); // use +100 numbers for rests if (tempPitch == REST) tempRV = tempRV + 100; int currRVindex = rhythmValues.length; // find current rhythm value and update RV for(int i=0; i<rhythmValues.length - 1;i++) { if(tempRV == rhythmValues[i]) n.setRhythmValue(rhythmValues[i + 1]); } clickedPosX = e.getX(); // update pitch if (n.getRhythmValue() > 100.0) { n.setPitch(REST); n.setRhythmValue(n.getRhythmValue() - 100); // update duration value n.setDuration(n.getRhythmValue()*0.9); } else { if (tempPitch == REST) n.setPitch(storedPitch); } theApp.repaint(); } // move note left - decrease RV if(e.getX() < clickedPosX - 6) { double tempRV = n.getRhythmValue(); int tempPitch = n.getPitch(); // use +100 numbers for rests if (tempPitch == REST) tempRV = tempRV + 100; // find current rhythm value position in the array int currRVindex = 0; for(int i=0; i<rhythmValues.length;i++) { if(tempRV == rhythmValues[i]) currRVindex = i; } // update rv if (currRVindex > 0) { n.setRhythmValue(rhythmValues[currRVindex - 1]); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 176 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» clickedPosX = e.getX(); // update pitch if (n.getRhythmValue() > 100.0) { n.setPitch(REST); n.setRhythmValue(n.getRhythmValue()-100); // update duration value n.setDuration(n.getRhythmValue()*0.9); } else { if (tempPitch == REST) n.setPitch(storedPitch); } theApp.repaint(); } } } // check for time signature change if (topTimeSelected) { //increase? if(e.getY() + theApp.staveDelta< clickedPosY - 4 ) { theApp.setMetre(theApp.getMetre() + 1.0); if (theApp.getMetre() > 9.0) theApp.setMetre(9.0); if (theApp.getMetre() < 1.0) theApp.setMetre(1.0); theApp.getPhrase().setNumerator((new Double(Math.round(theApp.getMetre()))).intValue()); clickedPosY -= 4; theApp.repaint(); theApp.updateChange(); } //decrease if(e.getY() + theApp.staveDelta > clickedPosY + 4) { theApp.setMetre(theApp.getMetre() - 1.0); if (theApp.getMetre() < 1.0) theApp.setMetre(1.0); if (theApp.getMetre() > 9.0) theApp.setMetre(9.0); theApp.getPhrase().setNumerator((new Double(Math.round(theApp.getMetre()))).intValue()); clickedPosY += 4; theApp.repaint(); theApp.updateChange(); } } // check for key signature change if (keySelected) { //increase? if(e.getY() + theApp.staveDelta < clickedPosY - 4) { theApp.setKeySignature(theApp.getKeySignature() + 1); if (theApp.getKeySignature() > 7) theApp.setKeySignature(7); clickedPosY -= 4; theApp.repaint(); theApp.updateChange(); } //decrease if(e.getY() + theApp.staveDelta > clickedPosY + 4) { theApp.setKeySignature(theApp.getKeySignature() - 1); if (theApp.getKeySignature() < -7) theApp.setKeySignature(-7); clickedPosY += 4; theApp.repaint(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 177 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» theApp.updateChange(); } } } public void mouseReleased(MouseEvent e) { button1Down = false; if(!theApp.editable) return; // delete note if necessary for(int i=0; i< theApp.getPhrase().getNoteList().size(); i++) { if (theApp.getPhrase().getNote(i).getRhythmValue() == 0.0) { theApp.getPhrase().getNoteList().removeElementAt(i); } } // unflag any note being selected theApp.repaint(); theApp.updateChange(); selectedNote = -1; topTimeSelected = false; keySelected = false; theApp.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } public void actionPerformed(ActionEvent e) { Phrase phrase = theApp.getPhrase(); Note note = phrase.getNote(selectedNote); if (e.getSource() == editNote) { JFrame editorFrame = new JFrame( "Edit this Note"); editorFrame.setSize( 400, 400); editorFrame.setResizable(true); DNoteEditor noteEditor = new DNoteEditor(editorFrame); noteEditor.editNote(note, 20, 20); } else if (e.getSource() == repeatNote) { Note newNote = note.copy(); phrase.getNoteList().insertElementAt( newNote, selectedNote ); } else if (e.getSource() == makeRest) { note.setFrequency(Note.REST); } else if (e.getSource() == deleteNote) { phrase.getNoteList().removeElementAt(selectedNote); } selectedNote = -1; theApp.repaint(); } // key listener stubs public void keyPressed(KeyEvent e) {} public void keyReleased(KeyEvent e) {} // public void keyTyped(KeyEvent e) { System.out.println(e.getKeyChar()); if(e.getKeyChar() == '\b') theApp.deleteLastNote(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 178 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } } ========================================================================= ========================================================================= package diamouses.ui.scoreEditor.staves; import java.awt.*; import java.awt.image.BufferedImage; import java.util.Vector; import import import import diamouses.ui.scoreEditor.GuitarNeck; jm.JMC; jm.gui.cpn.Images; jm.music.data.*; /** * Represents a treble clef stave. * * @author Andrew Brown, Adam Kirby * @version 1.0.1, 8th July 2001 */ public class DTrebleStave extends DStave implements JMC{ private static final class Accidental { public static final Accidental NONE = new Accidental("none"); public static final Accidental SHARP = new Accidental("sharp"); public static final Accidental NATURAL = new Accidental("natural"); public static final Accidental FLAT = new Accidental("flat"); private String name; // Due to a 1.1 compiler bug this constructor cannot be private Accidental(String name) { this.name = name; } public String toString() { return name; } } /** * Defines a type representing the rules and logic of when accidentals * should be dispalyed against notes on the stave. * * Note: In jMusic version 1.2 and earlier this inner class was previously * called AccidentalDisplayStyle. */ Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 179 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public static abstract class Style { int[] sharpPitches = {77, 72, 79, 74, 69, 76, 71}; int[] flatPitches = {71, 76, 69, 74, 67, 72, 65}; /** * Defines the standard style of displaying accidentals in a Common * Practice Notation stave. */ public static final Style TRADITIONAL = new Trad(); /** * Defines a style unique to jMusic, which displays an accidental in * all situations where the status (sharp/flat/natural) of a note may * be unclear. * * Note: In jMusic version 1.2 and earlier this field was previously * called SUPERFLUOUS_SHARPS_AND_FLATS. */ public static final Style JMUSIC = new JMusic(); private static final class Trad extends Style { private boolean[] accidentalRequiredByKeySignature = new boolean[12]; private static final int[] SHARP_ACCIDENTAL_PAIRS = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 }; private static final int[] FLAT_ACCIDENTAL_PAIRS = { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6 }; private int[] degreeToAccidentalPair = SHARP_ACCIDENTAL_PAIRS; private boolean[] accidentalInEffect = new boolean[7]; private int keySignature = 0; public Trad() { super("Traditional style"); this.initialise(0); } private void setBooleanArrayToFalse(boolean[] array) { for (int i = 0; i < array.length; i++) { array[i] = false; } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 180 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» void initialise(final int keySignature) { this.keySignature = keySignature; if (keySignature < 0) { degreeToAccidentalPair = FLAT_ACCIDENTAL_PAIRS; } else { degreeToAccidentalPair = SHARP_ACCIDENTAL_PAIRS; } this.setBooleanArrayToFalse( accidentalRequiredByKeySignature); accidentalRequiredByKeySignature[1] = true; accidentalRequiredByKeySignature[3] = true; accidentalRequiredByKeySignature[6] = true; accidentalRequiredByKeySignature[8] = true; accidentalRequiredByKeySignature[10] = true; for (int i = 0; i < Math.abs(keySignature); i++) { if (keySignature < 0) { accidentalRequiredByKeySignature[ flatPitches[i] % 12] = true; accidentalRequiredByKeySignature[ (flatPitches[i] - 1) % 12] = false; } else { accidentalRequiredByKeySignature[ sharpPitches[i] % 12] = true; accidentalRequiredByKeySignature[ (sharpPitches[i] + 1) % 12] = false; } } this.setBooleanArrayToFalse(accidentalInEffect); } Accidental selectAccidental( final int pitch, final double rhythmValue) { if (pitch == Note.REST || rhythmValue == 0.0) { return Accidental.NONE; } int degree = pitch % 12; // relative to C not tonic int accidentalPair = degreeToAccidentalPair[degree]; if (accidentalRequiredByKeySignature[degree] ^ accidentalInEffect[accidentalPair]) { accidentalInEffect[accidentalPair] = ! accidentalInEffect[accidentalPair]; if (degree == 1 || degree == 3 || degree == 6 || degree == 8 || degree == 10) { if (keySignature > -1) { return Accidental.SHARP; } else { return Accidental.FLAT; } } else { return Accidental.NATURAL; } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 181 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } return Accidental.NONE; } void processBarLine() { this.setBooleanArrayToFalse(accidentalInEffect); } }; private static final class JMusic extends Style { private Vector chromaticallyAffectedPitches = new Vector(); /** * Key signature encoded as a signed accidental count. The * following conditions apply: * <ul> * <li />0 represents no sharps of flats. * <li />positive <i>n</i> represents <i>n</i> sharps * <li />negative <i>n</i> represents <i>n</n> flats * </ul> */ private int keySignature; // had difficulty finding a better name because I don't // really understand what this variable is and does. It // seems to be closely related to previouslyChromatic. It // seems to be a count of accidentals, but in all staves of // the range of pitches in the MIDI spec. So a G Major // scale would add 1 to the count for the F# in the treble // stave, plus 1 for each F# in octaves above and below // that. // Odd. private int keyAccidentals; public JMusic() { super("JMusic style (with superfluous sharps and " + "flats)"); this.initialise(0); } void initialise(final int keySignature) { chromaticallyAffectedPitches = new Vector(); this.keySignature = keySignature; keyAccidentals = 0; if (keySignature > 0 && keySignature < 8) { for (int i = 0; i < keySignature; i++) { int degree = sharpPitches[i] % 12; for (int j = (int)Note.MIN_PITCH; j <= (int)Note.MAX_PITCH; j++) { if ((j % 12) == degree) { chromaticallyAffectedPitches.addElement( new Integer(j)); keyAccidentals++; } } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 182 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } } else if (keySignature < 0 && keySignature > -8) { for (int i = 0; i > keySignature; i--) { int degree = flatPitches[-i] % 12; for (int j = (int)Note.MIN_PITCH; j <= (int)Note.MAX_PITCH; j++) { if ((j % 12) == degree) { chromaticallyAffectedPitches.addElement( new Integer(j)); keyAccidentals++; } } } } } Accidental selectAccidental( final int pitch, final double rhythmValue) { if (pitch == Note.REST || rhythmValue == 0.0) { return Accidental.NONE; } if ((pitch % 12) == 1 || (pitch % 12) == 3 || (pitch % 12) == 6 || (pitch % 12) == 8 || (pitch % 12) == 10) { if (keySignature > -1) { chromaticallyAffectedPitches.addElement( new Integer(pitch - 1)); return Accidental.SHARP; } else { chromaticallyAffectedPitches.addElement( new Integer(pitch + 1)); return Accidental.FLAT; } } else { int size = chromaticallyAffectedPitches.size(); int temp; for(int j = 0; j < size; j++) { temp = ((Integer) chromaticallyAffectedPitches.elementAt( j)).intValue(); if (temp == pitch) { if (j > keyAccidentals-1) { chromaticallyAffectedPitches. removeElementAt(j); } return Accidental.NATURAL; } } } return Accidental.NONE; } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 183 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» void processBarLine() { // do nothing } }; private String name; // Due to a 1.1 compiler bug this constructor cannot be private Style(String name) { this.name = name; } public String toString() { return name + " of displaying accidentals"; } abstract void initialise(final int keySignature); abstract Accidental selectAccidental( final int pitch, final double rhythmValue); abstract void processBarLine(); } private Style style = new Style.JMusic(); /** * Sets the display style of accidentals for this stave. * * @param ads Style to be used by this stave. */ public void setAccidentalDisplayStyle(Style ads) { if (ads == Style.TRADITIONAL) { this.style = new Style.Trad(); } else if (ads == Style.JMUSIC) { this.style = new Style.JMusic(); } else { throw new RuntimeException("Unknown Accidental Display Style"); } } private int tonic = 0; protected int[] scale = JMC.MAJOR_SCALE; public static final int MAX_HEIGHT = 500; public static final int MAX_WIDTH = 2000; /** * Constructs a new treble stave to display a blank Phrase using the default * stave images. */ Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 184 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» public DTrebleStave() { super(); } /** * Constructs a new treble stave * <code>phrase</code> using the * * @param phrase Phrase to be */ public DTrebleStave(final Phrase super(phrase); } to display the specified default stave images. displayed in stave phrase) { /** * Constructs a new treble stave to display a blank Phrase using the * specified stave <code>images</code>. * * @param images Images representing notes, rest and other stave elements * to use within the compenent */ public DTrebleStave(final Images images) { super(images); } /** * Constructs a new treble stave to display the specified * <code>phrase</code> using the specified stave <code>images</code>. * * @param phrase Phrase to be displayed in stave * @param images Images representing notes, rest and other stave elements * to use within the compenent */ public DTrebleStave(final Phrase phrase, final Images images) { super(phrase, images); } private double beatCounter; public void paintComponent(Graphics graphics) { // if (phrase == null) { // return; // } // set up for double buffering if(image == null) { image = this.createImage(MAX_WIDTH, MAX_HEIGHT); g = image.getGraphics(); } // set font g.setFont(font); // keep track of the rhythmic values for bar lines beatCounter = 0.0; // reste note position locations notePositions.removeAllElements(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 185 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // add a title if set to be visible if(getDisplayTitle()) g.drawString(title, rightMargin, bPos 10); // insert key signature if required int keyOffset = 0; style.initialise(keySignature); // is the key signature using sharps or flats? if (keySignature > 0 && keySignature < 8) { // sharp for(int ks=0;ks<keySignature; ks++) { // claulate position int keyAccidentalPosition = notePosOffset[ sharps[ks]%12] + bPos - 4 + (( 5- sharps[ks]/12) * 24) + (( 6- sharps[ks]/12) * 4); // draw sharp on treble g.drawImage(sharp, rightMargin + clefWidth + keyOffset, keyAccidentalPosition, this); // indent position keyOffset += 10; keySigWidth = keyOffset; } } else { if (keySignature < 0 && keySignature > -8) { // flat for(int ks=0;ks< Math.abs(keySignature); ks++) { // claulate position int keyAccidentalPosition = notePosOffset[ flats[ks]%12] + bPos - 4 + (( 5- flats[ks]/12) * 24) + (( 6- flats[ks]/12) * 4); // draw flat g.drawImage(flat, rightMargin + clefWidth + keyOffset, keyAccidentalPosition, this); // indent position keyOffset += 10; } } } keySigWidth = keyOffset + 3; // insert time signature if required if ( metre != 0.0) { Image[] numbers = {one, two, three, four, five, six, seven, eight, nine}; // top number g.drawImage(numbers[(int)metre - 1], rightMargin + clefWidth + keySigWidth, bPos + 13, this); //bottom number g.drawImage(four, rightMargin + clefWidth + keySigWidth , bPos + 29, this); timeSigWidth = 30; } else timeSigWidth = 5; // set indent position for first note totalBeatWidth = rightMargin + clefWidth + keySigWidth + timeSigWidth; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 186 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // // // // // // firstChords = ChordAnalysis.getFirstPassChords(phrase, 1.0, tonic, scale); secondChords = ChordAnalysis.getSecondPassChords(phrase, 1.0, tonic, scale); lastChordDisplayed = -1; // draw notes and rests for(int i = 0; i < phrase.size();i++) { int notePitchNum = (int)phrase.getNote(i).getPitch(); // reset pitch for rests // position? int pitchTempPos; if ( notePitchNum == REST || phrase.getNote(i).getRhythmValue() == 0.0) { // rest or delete pitchTempPos = notePosOffset[71%12] + bPos - 4 + (( 5- 71/12) * 24) + (( 6- 71/12) * 4); } else { pitchTempPos = notePosOffset[notePitchNum%12] + bPos - 4 + (( 5- notePitchNum/12) * 24) + (( 6- notePitchNum/12) * 4); } firstAccidentalDisplayed = false; semitoneShiftUp = false; isTied = false; isFirstNoteInTie = true; extraImagesUsed = false; savedBeatWidth = totalBeatWidth; savedBeatWidth2 = 0; double rhythmValue = phrase.getNote(i).getRhythmValue(); double rvToEndOfBar = metre - (beatCounter % metre); while (rvToEndOfBar < rhythmValue) { isTied = true; drawNote(notePitchNum, rvToEndOfBar, pitchTempPos, i); rhythmValue -= rvToEndOfBar; rvToEndOfBar = metre - (beatCounter % metre); } drawNote(notePitchNum, rhythmValue, pitchTempPos, i); } // draw treble stave for(int i = 0; i < 5;i++) { g.drawLine( rightMargin, (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight), totalBeatWidth, (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight)); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 187 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» // draw neext note stave area // draw stave g.setColor(Color.lightGray); for(int i = 0; i < 5;i++) { g.drawLine( totalBeatWidth, (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight),totalBeatWidth + 50, (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight)); } //for(int i = 6; i < 11;i++) { //g.drawLine( totalBeatWidth, // (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight), //totalBeatWidth + 50, // (bPos + imageHeightOffset - (2* staveSpaceHeight)) +(i* staveSpaceHeight)); // } g.setColor(Color.black); // add Clefs g.drawImage(trebleClef, rightMargin + 7, bPos - 4, this); //g.drawImage(bassClef, rightMargin + 7, bPos + staveSpaceHeight * 6, this); /* Draw completed buffer to g */ graphics.drawImage(image, 0, 0, null); // clear image // clear g.setColor(this.getBackground()); g.fillRect(0,0, MAX_WIDTH, MAX_HEIGHT); g.setColor(this.getForeground()); //repaint(); //g.dispose(); } private boolean isFirstNoteInTie = true; // private boolean isNote = false; private boolean firstAccidentalDisplayed = false; private boolean isTied = false; // private boolean isUp = true; private boolean semitoneShiftUp = false; private boolean extraImagesUsed; // private boolean requiresMoreThanOneImage; // private double excessRhythmValue; private int savedBeatWidth; private int savedBeatWidth2; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 188 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private int lastChordDisplayed = -1; private int lastPosition = 0; // private int[] firstChords = new int[0]; // private int[] secondChords = new int[0]; private String[] chordStrings = {"I", "II", "III", "IV", "V", "VI", "VII","."}; private void drawNote(int notePitchNum, final double rhythmValue, int pitchTempPos, int noOfNote) { requiresMoreThanOneImage = false; excessRhythmValue = 0.0; // if ((beatCounter % 1.0) == 0.0) { // int currentBeat = (int) (beatCounter / 1.0); // int total = currentBeat - lastChordDisplayed; // int remaining = total; // while (lastChordDisplayed < currentBeat) { // lastChordDisplayed++; // // remaining--; // g.drawString(chordStrings[firstChords[lastChordDisplayed]], // (int) (totalBeatWidth - ((totalBeatWidth - lastPosition) // * (remaining // / (double) total))), // 20); // int index = secondChords[lastChordDisplayed]; // String string = chordStrings[index]; //// g.drawString(chordStrings[secondChords[lastChordDisplayed]], // g.drawString(string, // (int) (totalBeatWidth - ((totalBeatWidth - lastPosition) // * (remaining // / (double) total))), // 40); // } // lastPosition = totalBeatWidth; // } // choose graphic chooseImage( notePitchNum, rhythmValue, 71, 0, 71); // draw note/rest if (currImage != null) { if (noOfNote==getSelectedNote()) { Note selected_note = phrase.getNote(noOfNote);//.getPitch(); GuitarNeck.getInstance().setNote(selected_note.getPitch()); //-System.out.println(selected_note.toString()); BufferedImage bi = DStave.toBufferedImage(currImage); int w = bi.getWidth(); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 189 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» int h = bi.getHeight(); int pixel; BufferedImage biOut = new BufferedImage(w, h, bi.TYPE_INT_ARGB_PRE); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { pixel = bi.getRGB(x, y); if (pixel == (Color.BLACK).getRGB()) { pixel = (Color.RED).getRGB(); } biOut.setRGB(x, y, pixel); } } currImage = DStave.toImage(biOut); } } g.drawImage(currImage, totalBeatWidth, pitchTempPos, this); // store position in a vector drawNote2(notePitchNum, rhythmValue - excessRhythmValue, pitchTempPos, noOfNote); if (requiresMoreThanOneImage) { drawNote(notePitchNum, excessRhythmValue, pitchTempPos, noOfNote); extraImagesUsed = true; } } private void drawNote2(int pitch, final double rhythmValue, int yCoordinate,int noOfNote) { // draw accidental if (pitch != Note.REST && rhythmValue != 0.0) { Accidental accidental = style.selectAccidental(pitch, rhythmValue); if (accidental == Accidental.SHARP) { if (! firstAccidentalDisplayed) { displayImage(g, sharp, totalBeatWidth - 9, yCoordinate, noOfNote); } // enter the note made sharp i.e, F for an F# } else if (accidental == Accidental.FLAT) { yCoordinate -= 4; // to show the note a semitone higher for flats if (! firstAccidentalDisplayed) { displayImage(g, flat, totalBeatWidth - 9, yCoordinate, noOfNote); } pitch++; // assume it is a semitone higher for legerlines etc... semitoneShiftUp = true; } else if (accidental == Accidental.NATURAL) { if (! firstAccidentalDisplayed) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 190 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» displayImage(g, natural, totalBeatWidth - 7, yCoordinate, noOfNote); } } } firstAccidentalDisplayed = true; // draw note/rest displayImage(g, currImage, totalBeatWidth, yCoordinate, noOfNote); // store position in a vector notePositions.addElement(new Integer(totalBeatWidth)); notePositions.addElement(new Integer(yCoordinate)); if (dottedNote) { boolean dotFlag = true; for(int l = 0; l < lineNotes.length; l++) { if (lineNotes[l] + 12 == pitch || lineNotes[l] + 36 == pitch || lineNotes[l] + 60 == pitch || lineNotes[l] + 84 == pitch || lineNotes[l] + 108 == pitch || pitch == REST) { displayImage(g, dot, totalBeatWidth + 1, yCoordinate - 4, noOfNote); dotFlag = false; l = lineNotes.length; } } if (dotFlag) { displayImage(g, dot, totalBeatWidth + 1, yCoordinate, noOfNote); } } // leger lines down if (pitch <= 61 && pitch > -1 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 52, totalBeatWidth+ 12, bPos + 52);} if ( pitch <= 58 && pitch > -1 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 60, totalBeatWidth+ 12, bPos + 60);} if ( pitch <= 54 && pitch > -1 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 68, totalBeatWidth+ 12, bPos + 68);} if ( pitch <= 51 && pitch > -1 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 76, totalBeatWidth+ 12, bPos + 76);} if ( pitch <= 48 && pitch > -1 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 84, totalBeatWidth+ 12, bPos + 84);} // leger lines up if ( pitch >= 81 && pitch < 128 && rhythmValue != 0.0) {g.drawLine( totalBeatWidth - 3, bPos + 4, totalBeatWidth+ 12, bPos + 4);} Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 191 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» if ( pitch >= 84 && pitch < {g.drawLine( totalBeatWidth bPos - 4);} if ( pitch >= 88 && pitch < {g.drawLine( totalBeatWidth bPos - 12);} if ( pitch >= 91 && pitch < {g.drawLine( totalBeatWidth bPos - 20);} if ( pitch >= 95 && pitch < {g.drawLine( totalBeatWidth bPos - 28);} 128 && rhythmValue != 0.0) - 3, bPos - 4, totalBeatWidth+ 12, 128 && rhythmValue != 0.0) - 3, bPos - 12, totalBeatWidth+ 12, 128 && rhythmValue != 0.0) - 3, bPos - 20, totalBeatWidth+ 12, 128 && rhythmValue != 0.0) - 3, bPos - 28, totalBeatWidth+ 12, // increment everything savedBeatWidth2 = totalBeatWidth; if ((isTied || extraImagesUsed) && isNote && ! isFirstNoteInTie) { int yPosition = yCoordinate + 19 - ((semitoneShiftUp) ? 4 : 0); if (isUp) { g.drawImage(tieUnder, savedBeatWidth - 3 + 9, yPosition + 17, savedBeatWidth2 + 19 - 9, yPosition + 17 + tieUnder.getHeight(this), 0, 0, tieUnder.getWidth(this), tieUnder.getHeight(this),this); } else { g.drawImage(tieOver, savedBeatWidth - 3 + 9, yPosition - 20, savedBeatWidth2 + 19 - 9, yPosition - 20 + tieOver.getHeight(this), 0, 0, tieOver.getWidth(this), tieOver.getHeight(this), this); } } if (isFirstNoteInTie = true) { isFirstNoteInTie = false; } savedBeatWidth = totalBeatWidth; totalBeatWidth += currBeatWidth; dottedNote = false; // quantised to semiquvers! // (int)((rhythmValue/0.25) * 0.25); beatCounter += (int)(rhythmValue/0.25) * 0.25; // draw bar line if (metre != 0.0) { if ( (beatCounter % metre) == 0.0) { Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 192 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» g.drawLine( totalBeatWidth , bPos + 12, totalBeatWidth, bPos + 44); style.processBarLine(); // add bar numbers? if (barNumbers) g.drawString( ""+(int)(beatCounter/metre +1 + phrase.getStartTime()) , totalBeatWidth - 4 , bPos); totalBeatWidth += 12; } } } private void displayImage(final Graphics g, final Image image, int xCoord,int yCoord, int noOfNote ) { g.drawImage(image, xCoord, yCoord, this); } } ========================================================================= ========================================================================= package diamouses.ui.Metronome; /* * PlayNote.java * * Created on June 12, 2007, 3:05 PM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ import java.util.TimerTask; import javax.sound.midi.Instrument; import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiSystem; import javax.sound.midi.Patch; import javax.sound.midi.Soundbank; import javax.sound.midi.Synthesizer; /** Used by MetronomeDialog to play a midi note. * * @author Aaron Bauer */ PlayNote public class extends TimerTask{ private MidiChannel[] channels; private int beatCount; private static int noteCount; private boolean compoundTime; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 193 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» /** Creates a new instance of PlayNote. Requires beats per measure integer * and a compound time boolean * @param beatsPM - The beats per minute * @param compound - whether the compound time is used */ public PlayNote(int beatsPM, boolean compound) { PlayNote.noteCount = beatsPM; this.beatCount = beatsPM - 1; this.compoundTime = compound; try { //initializes midi Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); this.channels = synth.getChannels(); Soundbank bank = synth.getDefaultSoundbank(); synth.loadAllInstruments(bank); Instrument instrs[] = synth.getLoadedInstruments(); Instrument instr = instrs[115]; Patch instrPatch = instr.getPatch(); this.channels[2].programChange(instrPatch.getBank(), instrPatch.getProgram()); } catch (Exception exc){ exc.printStackTrace(); } } /** Plays the note depending on beats per measure and whether or not compound * time is enabled. */ public void run() { this.beatCount = this.beatCount + 1; if(this.beatCount == PlayNote.noteCount){ this.channels[2].noteOn(60, 70); try { Thread.sleep(1); } catch (Exception exc){ exc.printStackTrace(); } this.channels[2].noteOff(60); this.beatCount = 0; } else if(this.compoundTime && (this.beatCount == 3 || this.beatCount == 6 || this.beatCount == 9) ){ this.channels[2].noteOn(45, 70); try { Thread.sleep(1); } catch (Exception exc){ exc.printStackTrace(); } this.channels[2].noteOff(45); } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 194 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» else { this.channels[2].noteOn(50, 70); try { Thread.sleep(1); } catch (Exception exc){ exc.printStackTrace(); } this.channels[2].noteOff(50); } } } ========================================================================= ========================================================================= package diamouses.ui.Metronome; import javax.swing.JFrame; public class TestMetronome { public static void main (String [] args) { Metronome met = new Metronome(new JFrame(), null, true); met.setVisible(true); } } ========================================================================= ========================================================================= package diamouses.ui.Metronome; import java.awt.*; import javax.swing.*; import java.util.Timer; /** Creates a metronome that works by having a timer play a note at a * user defined interval in beats per minute. * * @author Aaron Bauer */ public class { Metronome extends javax.swing.JDialog PlayNote playNote = null; // beats per minute private int bpm = 120; private Timer noteTimer = new Timer(); // beats per measure private double beats = 4; // compound time indicator private boolean compound = false; // used to check text field content Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 195 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private double textTest; /** Creates new form Metronome * @param parent - The parent frame * @param image - The title image * @param modal - The modality of the screen */ public Metronome(java.awt.Frame parent, Image image, boolean modal) { super(parent, modal); this.setIconImage(image); initComponents(); this.rb44.setSelected(true); noteTimer = new Timer(); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { buttonGroup1 = new javax.swing.ButtonGroup(); rb24 = new javax.swing.JRadioButton(); jLabel4 = new javax.swing.JLabel(); rb68 = new javax.swing.JRadioButton(); rb98 = new javax.swing.JRadioButton(); rb128 = new javax.swing.JRadioButton(); rbCustom = new javax.swing.JRadioButton(); jLabel3 = new javax.swing.JLabel(); rb44 = new javax.swing.JRadioButton(); rb54 = new javax.swing.JRadioButton(); rb34 = new javax.swing.JRadioButton(); jTextField2 = new javax.swing.JTextField(); jLabel6 = new javax.swing.JLabel(); jLabel5 = new javax.swing.JLabel(); jLabel1 = new javax.swing.JLabel(); jSlider1 = new javax.swing.JSlider(); btnStart = new javax.swing.JButton(); jLabel2 = new javax.swing.JLabel(); jLabel7 = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Score-Editor Metronome"); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); buttonGroup1.add(rb24); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 196 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» rb24.setText("2/4"); rb24.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb24.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb24.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb24ActionPerformed(evt); } }); jLabel4.setText("Compound Time"); buttonGroup1.add(rb68); rb68.setText("6/8"); rb68.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb68.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb68.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb68ActionPerformed(evt); } }); buttonGroup1.add(rb98); rb98.setText("9/8"); rb98.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb98.setMargin(new java.awt.Insets(0, 0, 0, 0)); buttonGroup1.add(rb128); rb128.setText("12/8"); rb128.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb128.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb128.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb128ActionPerformed(evt); } }); buttonGroup1.add(rbCustom); rbCustom.setText("Custom Time"); rbCustom.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rbCustom.setMargin(new java.awt.Insets(0, 0, 0, 0)); rbCustom.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rbCustomActionPerformed(evt); } }); jLabel3.setText("Simple Time"); buttonGroup1.add(rb44); rb44.setText("4/4"); rb44.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 197 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» rb44.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb44.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb44ActionPerformed(evt); } }); buttonGroup1.add(rb54); rb54.setText("5/4"); rb54.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb54.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb54.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb54ActionPerformed(evt); } }); buttonGroup1.add(rb34); rb34.setText("3/4"); rb34.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); rb34.setMargin(new java.awt.Insets(0, 0, 0, 0)); rb34.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rb34ActionPerformed(evt); } }); jTextField2.setText("4"); jLabel6.setText("Beats Per Measure"); jLabel5.setText("Allegro"); jLabel1.setFont(new java.awt.Font("Tahoma", 0, 12)); jLabel1.setText("BPM: 120"); jSlider1.setMaximum(208); jSlider1.setMinimum(40); jSlider1.setPaintTicks(true); jSlider1.setSnapToTicks(true); jSlider1.setValue(120); jSlider1.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { jSlider1StateChanged(evt); } }); btnStart.setText("Start"); btnStart.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnStartActionPerformed(evt); } }); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 198 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» jLabel2.setFont(new java.awt.Font("Tahoma", 1, 18)); jLabel2.setText("Metronome"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LE ADING) .addComponent(rb98) .addComponent(rb68) .addComponent(jLabel4) .addComponent(rb24) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LE ADING) .addComponent(rbCustom) .addComponent(rb54) .addComponent(rb44) .addComponent(jLabel3) .addComponent(rb34)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LE ADING) .addGroup(layout.createSequentialGroup() .addGap(24, 24, 24) .addComponent(jLabel6)) .addGroup(layout.createSequentialGroup() .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnStart)) .addGroup(layout.createSequentialGroup() .addGap(91, 91, 91) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LE ADING) .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1))) .addGroup(layout.createSequentialGroup() .addGap(70, 70, 70) .addComponent(jLabel2)) .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))) Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 199 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» .addComponent(rb128)) .addContainerGap(12, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap(203, Short.MAX_VALUE) .addComponent(jLabel7) .addGap(214, 214, 214)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LE ADING) .addGroup(layout.createSequentialGroup() .addGap(28, 28, 28) .addComponent(jLabel3) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rb44) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rb54) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rb34) .addGap(6, 6, 6) .addComponent(rb24) .addGap(6, 6, 6) .addComponent(jLabel4) .addGap(6, 6, 6) .addComponent(rb68) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rb98)) .addGroup(layout.createSequentialGroup() .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TR AILING) .addComponent(btnStart) .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1) .addGap(6, 6, 6) .addComponent(jLabel5))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rb128) Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 200 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BA SELINE) .addComponent(rbCustom) .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel6)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE) .addComponent(jLabel7) .addContainerGap()) ); java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); setBounds((screenSize.width-425)/2, (screenSize.height-300)/2, 425, 300); }// </editor-fold>//GEN-END:initComponents private void formWindowClosing(java.awt.event.WindowEvent evt) {//GENFIRST:event_formWindowClosing noteTimer.purge(); noteTimer.cancel(); }//GEN-LAST:event_formWindowClosing private void jSlider1StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSlider1StateChanged JSlider source = (JSlider) evt.getSource(); if(!source.getValueIsAdjusting()) { bpm = source.getValue(); jLabel1.setText("BPM: " + bpm); if(bpm <= 59 && bpm >= 40) { jLabel5.setText("Largo"); } if(bpm <= 65 && bpm >= 60) { jLabel5.setText("Larghetto"); } if(bpm <= 75 && bpm >= 66) { jLabel5.setText("Adagio"); } if(bpm <= 107 && bpm >= 76) { jLabel5.setText("Andante"); } if(bpm <= 119 && bpm >= 108) { jLabel5.setText("Moderato"); Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 201 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» } if(bpm <= 167 && bpm >= 120) { jLabel5.setText("Allegro"); } if(bpm <= 199 && bpm >= 168) { jLabel5.setText("Presto"); } if(bpm <= 208 && bpm >= 200) { jLabel5.setText("Prestissimo"); } if(btnStart.getText().equals("Stop")) { double noteLength = 1 / (((double) this.bpm / 60) / 1000); playNote.cancel(); playNote = new PlayNote((int) this.beats, this.compound); noteTimer.schedule(playNote, 0, (long) noteLength); } } }//GEN-LAST:event_jSlider1StateChanged private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStartActionPerformed if(btnStart.getText().equals("Start")) { btnStart.setText("Stop"); double noteLength = 1 / (((double) bpm / 60) / 1000); playNote = new PlayNote((int) this.beats, this.compound); noteTimer.schedule(playNote, 0, (long) noteLength); } else { noteTimer.purge(); playNote.cancel(); btnStart.setText("Start"); } }//GEN-LAST:event_btnStartActionPerformed private void rb44ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb44ActionPerformed resetTempo(); }//GEN-LAST:event_rb44ActionPerformed private void rb54ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb54ActionPerformed resetTempo(); }//GEN-LAST:event_rb54ActionPerformed private void rb34ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb34ActionPerformed resetTempo(); }//GEN-LAST:event_rb34ActionPerformed private void rb24ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb24ActionPerformed Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 202 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» resetTempo(); }//GEN-LAST:event_rb24ActionPerformed private void rb68ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb68ActionPerformed resetTempo(); }//GEN-LAST:event_rb68ActionPerformed private void rb128ActionPerformed(java.awt.event.ActionEvent evt) {//GENFIRST:event_rb128ActionPerformed resetTempo(); }//GEN-LAST:event_rb128ActionPerformed private void rbCustomActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rbCustomActionPerformed resetTempo(); }//GEN-LAST:event_rbCustomActionPerformed private void resetTempo() { if(btnStart.getText().equals("Stop")) { // btnStart.setText("Stop"); double noteLength = 1 / (((double) bpm / 60) / 1000); if(this.rb44.isSelected()) { beats = 4; compound = false; } if(rbCustom.isSelected()) { // allows program to handle illogical entry into text field try { textTest = Double.valueOf(this.jTextField2.getText()); } catch(NumberFormatException ex) { ex.printStackTrace(); } if(this.textTest >= 1 && this.textTest < 101) { beats = Double.valueOf(this.jTextField2.getText()); compound = false; jLabel7.setText(""); } else { jLabel7.setText("Try a different custom time."); } } if(this.rb54.isSelected()) { beats = 5; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 203 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» compound = false; } if(rb34.isSelected()) { beats = 3; compound = false; } if(rb24.isSelected()) { beats = 2; compound = false; } if(rb68.isSelected()) { beats = 6; compound = true; } if(rb98.isSelected()) { beats = 9; compound = true; } if(rb128.isSelected()) { beats = 12; compound = true; } noteTimer.purge(); if(playNote != null) { playNote.cancel(); playNote = null; } playNote = new PlayNote((int) this.beats, this.compound); noteTimer.schedule(playNote, 0, (long) noteLength); //} } else { noteTimer.purge(); playNote.cancel(); } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnStart; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; private javax.swing.JLabel jLabel6; private javax.swing.JLabel jLabel7; private javax.swing.JSlider jSlider1; private javax.swing.JTextField jTextField2; private javax.swing.JRadioButton rb128; Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 204 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» private javax.swing.JRadioButton rb24; private javax.swing.JRadioButton rb34; private javax.swing.JRadioButton rb44; private javax.swing.JRadioButton rb54; private javax.swing.JRadioButton rb68; private javax.swing.JRadioButton rb98; private javax.swing.JRadioButton rbCustom; // End of variables declaration//GEN-END:variables } Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 205 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» <?xml version="1.0"?> <!-- ============================================================== --> <!-- A Buildfile --> <!-- ============================================================== --> <project name="ScoreEditor" default="jar" basedir="."> <target name="variables"> <tstamp/> <property name="Name" value="ScoreEditor"/> <property name="src.dir" value="${basedir}${file.separator}src"/> <property name="lib.dir" value="${basedir}${file.separator}libs"/> <property name="build.dir" value="${basedir}${file.separator}build"/> <property name="build.classes"value="${build.dir}${file.separator}classes"/> <property name="build.jar.dir" value="${build.dir}${file.separator}jars"/> <property name="build.jar" value="${build.jar.dir}${file.separator}${Name}.jar"/> <property name="build.classpath" value=".:${lib.dir}${file.separator}jmusic.jar"/> <property name="images.src" value="${src.dir}${file.separator}diamouses${file.separator}ui${file.sepa rator}scoreEditor${file.separator}images"/> <property name="images.dest" value="${build.classes}${file.separator}diamouses${file.separator}ui${fil e.separator}scoreEditor${file.separator}images"/> <property name="packages" value="*"/> </target> <!-- ============================================================== --> <!-- Create required directories --> <!-- ============================================================== --> <target name="init" depends="variables"> <!-- Prepare necessary directories --> <mkdir dir="${build.dir}"/> <mkdir dir="${build.classes}"/> <mkdir dir="${build.jar.dir}"/> <mkdir dir="${images.dest}"/> <!-- Copy all the images to the build directory --> <copy todir="${images.dest}"> <fileset dir="${images.src}" casesensitive="yes"> <include name="**/*.*"/> </fileset> </copy> </target> Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 206 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» <!-- ============================================================== --> <!-- Compiles the source code --> <!-- ============================================================== --> <target name="compile" depends="init" description="Compiles source code"> <javac encoding="UTF-8" srcdir="${src.dir}" destdir="${build.classes}" classpath="${build.classpath}" optimize="on" /> </target> <!-- ===============================================================--> <!-- Creates a jar archive --> <!-- ============================================================== --> <target name="jar" depends="init,compile" description="Generates ScoreEditor.jar" > <!-- Put everything from ${build.dir} into the ScoreEditor.jar file --> <jar manifest="${src.dir}/myManifest.txt" jarfile="${build.jar.dir}${file.separator}${Name}.jar" basedir="${build.classes}" includes="**" /> <copy todir="${build.jar.dir}"> <fileset dir="${src.dir}" casesensitive="yes"> <include name="**/*.html"/> </fileset> </copy> <copy todir="${build.jar.dir}"> <fileset dir="${lib.dir}" casesensitive="yes"> <include name="**/*.jar"/> </fileset> </copy> </target> <!-- ============================================================== --> <!-- Cleans up all --> <!-- ============================================================== --> <target name="clean" depends="init" description="Removes previous build (classes and jar files)"> <delete dir="${build.classes}"/> <delete dir="${build.jar.dir}"/> </target> <!-- ============================================================== --> <!-- Run Score Editor --> <!-- ============================================================== --> <target name="run" depends="init,compile,jar" description="Initiates ScoreEditor.java"> <echo message = "Running ScoreEditor"/> <java jar="${build.jar.dir}${file.separator}${Name}.jar" fork="true" > </java> </target> </project> Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 207 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» JavaDoc of GuitarNeck object Overview Package Class Use Tree Deprecated Index Help PREV CLASS NEXT CLASS SUMMARY: NESTED | FIELD | CONSTR | METHOD FRAMES NO FRAMES All Classes All Classes DETAIL: FIELD | CONSTR | METHOD diamouses.ui.scoreEditor Class GuitarNeck java.lang.Object java.awt.Component java.awt.Container javax.swing.JComponent javax.swing.JPanel diamouses.ui.scoreEditor.GuitarNeck All Implemented Interfaces: java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable, javax.accessibility.Accessible public class GuitarNeck extends javax.swing.JPanel See Also: Serialized Form Nested Class Summary Nested classes/interfaces inherited from class javax.swing.JPanel javax.swing.JPanel.AccessibleJPanel Nested classes/interfaces inherited from class javax.swing.JComponent javax.swing.JComponent.AccessibleJComponent Nested classes/interfaces inherited from class java.awt.Container java.awt.Container.AccessibleAWTContainer Nested classes/interfaces inherited from class java.awt.Component java.awt.Component.AccessibleAWTComponent, java.awt.Component.BaselineResizeBehavior, java.awt.Component.BltBufferStrategy, java.awt.Component.FlipBufferStrategy Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 208 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Field Summary (package private) color_1st java.awt.Color (package private) color_2nd java.awt.Color (package private) color_3rd java.awt.Color (package private) color_4th java.awt.Color (package private) color_5th java.awt.Color (package private) color_6th java.awt.Color private fret_buttons javax.swing.JToggleButton[][] private javax.swing.JPanel fret_panel private int height Global variables for the application (package private) ii javax.swing.ImageIcon private static GuitarNeck reference private private int screen_width javax.swing.JPanel[] string_panels Fields inherited from class javax.swing.JComponent accessibleContext, listenerList, TOOL_TIP_TEXT_KEY, ui, UNDEFINED_CONDITION, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, WHEN_FOCUSED, WHEN_IN_FOCUSED_WINDOW Fields inherited from class java.awt.Component BOTTOM_ALIGNMENT, TOP_ALIGNMENT CENTER_ALIGNMENT, LEFT_ALIGNMENT, RIGHT_ALIGNMENT, Fields inherited from interface java.awt.image.ImageObserver ABORT, ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, WIDTH Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 209 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» Constructor Summary private GuitarNeck(int screen_width) Constructor. Method Summary void clearAll() private getIconsForString(int string) javax.swing.ImageIcon[] Method getIconsForString returns an Array [] of ImageIcons with the images of the appropriate Notes for each string of the Guitar. static GuitarNeck getInstance() static GuitarNeck getInstance(int mm) private getStringPanel() javax.swing.JPanel Method returns a JPanel with JButtons for each note of the Guitar static void main(java.lang.String[] args) Main -> For testing purposes void paintComponent(java.awt.Graphics g) Method Paint the Guitar Neck on the Background of this JPanel. void setNote(int pitch) Sets the note with a specific pitch on the virtual guitar private void setSizes() This method sets the sizes of the JToggleButtons private void switchColor(int string) Switch the color of the string Methods inherited from class javax.swing.JPanel getAccessibleContext, getUI, getUIClassID, paramString, setUI, updateUI Methods inherited from class javax.swing.JComponent addAncestorListener, addNotify, addVetoableChangeListener, computeVisibleRect, contains, createToolTip, disable, enable, firePropertyChange, firePropertyChange, firePropertyChange, fireVetoableChange, getActionForKeyStroke, getActionMap, getAlignmentX, getAlignmentY, getAncestorListeners, getAutoscrolls, getBaseline, getBaselineResizeBehavior, getBorder, getBounds, getClientProperty, getComponentGraphics, getComponentPopupMenu, getConditionForKeyStroke, Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 210 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» getDebugGraphicsOptions, getDefaultLocale, getFontMetrics, getGraphics, getHeight, getInheritsPopupMenu, getInputMap, getInputMap, getInputVerifier, getInsets, getInsets, getListeners, getLocation, getMaximumSize, getMinimumSize, getNextFocusableComponent, getPopupLocation, getPreferredSize, getRegisteredKeyStrokes, getRootPane, getSize, getToolTipLocation, getToolTipText, getToolTipText, getTopLevelAncestor, getTransferHandler, getVerifyInputWhenFocusTarget, getVetoableChangeListeners, getVisibleRect, getWidth, getX, getY, grabFocus, isDoubleBuffered, isLightweightComponent, isManagingFocus, isOpaque, isOptimizedDrawingEnabled, isPaintingForPrint, isPaintingTile, isRequestFocusEnabled, isValidateRoot, paint, paintBorder, paintChildren, paintImmediately, paintImmediately, print, printAll, printBorder, printChildren, printComponent, processComponentKeyEvent, processKeyBinding, processKeyEvent, processMouseEvent, processMouseMotionEvent, putClientProperty, registerKeyboardAction, registerKeyboardAction, removeAncestorListener, removeNotify, removeVetoableChangeListener, repaint, repaint, requestDefaultFocus, requestFocus, requestFocus, requestFocusInWindow, requestFocusInWindow, resetKeyboardActions, reshape, revalidate, scrollRectToVisible, setActionMap, setAlignmentX, setAlignmentY, setAutoscrolls, setBackground, setBorder, setComponentPopupMenu, setDebugGraphicsOptions, setDefaultLocale, setDoubleBuffered, setEnabled, setFocusTraversalKeys, setFont, setForeground, setInheritsPopupMenu, setInputMap, setInputVerifier, setMaximumSize, setMinimumSize, setNextFocusableComponent, setOpaque, setPreferredSize, setRequestFocusEnabled, setToolTipText, setTransferHandler, setUI, setVerifyInputWhenFocusTarget, setVisible, unregisterKeyboardAction, update Methods inherited from class java.awt.Container add, add, add, add, add, addContainerListener, addImpl, addPropertyChangeListener, addPropertyChangeListener, applyComponentOrientation, areFocusTraversalKeysSet, countComponents, deliverEvent, doLayout, findComponentAt, findComponentAt, getComponent, getComponentAt, getComponentAt, getComponentCount, getComponents, getComponentZOrder, getContainerListeners, getFocusTraversalKeys, getFocusTraversalPolicy, getLayout, getMousePosition, insets, invalidate, isAncestorOf, isFocusCycleRoot, isFocusCycleRoot, isFocusTraversalPolicyProvider, isFocusTraversalPolicySet, layout, list, list, locate, minimumSize, paintComponents, preferredSize, printComponents, processContainerEvent, processEvent, remove, remove, removeAll, removeContainerListener, setComponentZOrder, setFocusCycleRoot, setFocusTraversalPolicy, setFocusTraversalPolicyProvider, setLayout, transferFocusBackward, transferFocusDownCycle, validate, validateTree Methods inherited from class java.awt.Component action, add, addComponentListener, addFocusListener, addHierarchyBoundsListener, addHierarchyListener, addInputMethodListener, addKeyListener, addMouseListener, addMouseMotionListener, addMouseWheelListener, bounds, checkImage, checkImage, coalesceEvents, contains, createImage, createImage, createVolatileImage, createVolatileImage, disableEvents, dispatchEvent, enable, enableEvents, Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 211 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» enableInputMethods, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, getBackground, getBounds, getColorModel, getComponentListeners, getComponentOrientation, getCursor, getDropTarget, getFocusCycleRootAncestor, getFocusListeners, getFocusTraversalKeysEnabled, getFont, getForeground, getGraphicsConfiguration, getHierarchyBoundsListeners, getHierarchyListeners, getIgnoreRepaint, getInputContext, getInputMethodListeners, getInputMethodRequests, getKeyListeners, getLocale, getLocation, getLocationOnScreen, getMouseListeners, getMouseMotionListeners, getMousePosition, getMouseWheelListeners, getName, getParent, getPeer, getPropertyChangeListeners, getPropertyChangeListeners, getSize, getToolkit, getTreeLock, gotFocus, handleEvent, hasFocus, hide, imageUpdate, inside, isBackgroundSet, isCursorSet, isDisplayable, isEnabled, isFocusable, isFocusOwner, isFocusTraversable, isFontSet, isForegroundSet, isLightweight, isMaximumSizeSet, isMinimumSizeSet, isPreferredSizeSet, isShowing, isValid, isVisible, keyDown, keyUp, list, list, list, location, lostFocus, mouseDown, mouseDrag, mouseEnter, mouseExit, mouseMove, mouseUp, move, nextFocus, paintAll, postEvent, prepareImage, prepareImage, processComponentEvent, processFocusEvent, processHierarchyBoundsEvent, processHierarchyEvent, processInputMethodEvent, processMouseWheelEvent, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeMouseWheelListener, removePropertyChangeListener, removePropertyChangeListener, repaint, repaint, repaint, resize, resize, setBounds, setBounds, setComponentOrientation, setCursor, setDropTarget, setFocusable, setFocusTraversalKeysEnabled, setIgnoreRepaint, setLocale, setLocation, setLocation, setName, setSize, setSize, show, show, size, toString, transferFocus, transferFocusUpCycle Methods inherited from class java.lang.Object clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait Field Detail height private int height Global variables for the application screen_width private int screen_width fret_buttons private javax.swing.JToggleButton[][] fret_buttons Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 212 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» string_panels private javax.swing.JPanel[] string_panels fret_panel private javax.swing.JPanel fret_panel ii javax.swing.ImageIcon ii color_1st java.awt.Color color_1st color_2nd java.awt.Color color_2nd color_3rd java.awt.Color color_3rd color_4th java.awt.Color color_4th color_5th java.awt.Color color_5th color_6th java.awt.Color color_6th reference private static GuitarNeck reference Constructor Detail GuitarNeck private GuitarNeck(int screen_width) Constructor. Initializes 6 JPanels, one for each guitar string Method Detail getInstance public static GuitarNeck getInstance(int mm) Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 213 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» getInstance public static GuitarNeck getInstance() getStringPanel private javax.swing.JPanel getStringPanel() Method returns a JPanel with JButtons for each note of the Guitar Parameters: string - First..Sixth string of the guitar Returns: a JPanel with appropriate JButtons setSizes private void setSizes() This method sets the sizes of the JToggleButtons paintComponent public void paintComponent(java.awt.Graphics g) Method Paint the Guitar Neck on the Background of this JPanel. Overrides: paintComponent in class javax.swing.JComponent switchColor private void switchColor(int string) Switch the color of the string Parameters: string - values 1 to 6 getIconsForString private javax.swing.ImageIcon[] getIconsForString(int string) Method getIconsForString returns an Array [] of ImageIcons with the images of the appropriate Notes for each string of the Guitar. Parameters: string - An integer from 1 to 6 representing the First..Sixth string of a Guitar. Returns: an ImageIcon [] array. setNote public void setNote(int pitch) Sets the note with a specific pitch on the virtual guitar Parameters: pitch - the pitch value. Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 214 «Εικονική αναπαράσταση µουσικών κοινοτήτων στο διαδίκτυο» clearAll public void clearAll() main public static void main(java.lang.String[] args) Main -> For testing purposes Parameters: args Overview Package Class Use Tree Deprecated Index Help PREV CLASS NEXT CLASS SUMMARY: NESTED | FIELD | CONSTR | METHOD FRAMES NO FRAMES All Classes All Classes DETAIL: FIELD | CONSTR | METHOD Τµήµα Εφαρµοσµένης Πληροφορικής & Πολυµέσων 215