sviluppo in c++ di una interfaccia utente per l` editing dei
Transcript
sviluppo in c++ di una interfaccia utente per l` editing dei
UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea in Ingegneria Informatica e dell’Automazione Tesi di Laurea SVILUPPO IN C++ DI UNA INTERFACCIA UTENTE PER L’ EDITING DEI PARAMETRI DI UNO STRUMENTO MUSICALE ELETTRONICO ROLAND TRAMITE PROTOCOLLO DI CONNESSIONE MIDI Candidato: Samuele PACIONI matr. 1005693 Relatore: Prof. Ing. Aldo Franco DRAGONI Anno Accademico 2007/2008 RINGRAZIAMENTI Ring raz io i l P ro f. Ald o Fr anc o Dra gon i p er il co sta nt e a iut o pre sta to m i du ran te tut ta la s te su ra de l la t es i e tu tto i l per sona le de l l ’ az i enda Rol and Eur ope S.p .a pe r i co ns ig li , l a fi duc ia e p er l a g rand e re spon sab il it à c onc es sa mi ne ll a rea l izza zi one d ei p rog et t i di scu s s i. Ri vol go un s inc er o rin gr az ia men to a tu tt i i co ll egh i e ag li a m ic i p er l a dis pon ibi l ità d im os tr ata e pe r i l sos te gno o ff er to ne i mo men t i di ff ic i li . In fi ne vo rr ei fa re un r in gra zi am en to sp ec ia le a lla mi a fa mi gl ia, se nza il sos te gno d el la qu al e non a vr ei rag gi unt o ques to i mpo rta nt e t ra gua rdo. SOMMARIO 1. - INTRODUZIONE ……………………………………………………….... 1 2. - INTERFACCE GRAFICHE, VISUAL STUDIO 6.0 ED MFC:…...… …….CARATTERISTICHE SALIENTI …………………………………........ 5 2.1 – INTERFACCE GRAFICHE …………………………………………………………………….......... 5 2.2 – MICROSOFT VISUAL C++ 6.0 …………………………………………………….................. 11 2.3 – MICROSOFT FOUNDATION CLASS: MFC ………………………………………………..… 13 2.3.1 – MFC: DEFINIZIONE E SPECIFICHE ……………………………………………………………… 3. - PANORAMICA SUL PROTOCOLLO MIDI 14 ………………………….. 22 3.1 – INTRODUZIONE …………………………………………………………………………..…………… 22 3.2 – L’ INTERFACCIA E LE PORTE MIDI …………………………………………………………..… 22 3.3 – CAVI DI COLLEGAMENTO MIDI …………………………………………………………..……. 23 3.4 – MESSAGGI MIDI E LORO STRUTTURAZIONE FISICA ………………………………… 24 3.5 – CANALI MIDI …………………………………………………………………………………………... 26 3.6 – STRUTTURA DEI MESSAGGI MIDI ………………………………………………………..…… 27 3.7 – CHANNEL VOICE MESSAGE ………………………………………………………………………. 28 3.8 – SYSTEM EXCLUSIVE MESSAGE ………………………………………………………………….. 29 3.9 – STRUTTURA SYSTEM EXCLUSIVE MESSAGE ………………………………..……………. 30 3.10 – GENERAL MIDI ……………………………………………………………………………….…….. 31 3.11 – SPECIFICHE GENERAL MIDI ………………………………………………………..………….. 32 3.12 – ROLAND GS GENERAL SYSTEM …………………………………………………..………….. 33 4. - SVILUPPO PROGETTO RODGERS i538 …………………………… 34 4.1 – INTRODUZIONE ………………………………………………………………………………..……... 34 4.2 – DESCRIZIONE FILES DI PROGETTO: BAROQUE.CPP E VOICEDATA.H .…….….. 35 4.3 – STANDARDIZZAZIONE MESSAGGIO MIDI ………………………………………….…...… 42 4.4 – PRESENTAZIONE DELL’ INTERFACCIA GRAFICA …………................................ 43 5. - COSTRUZIONE INTERFACCIA GRAFICA …………………………. 47 5.1 – PANORAMICA DAL RESOURCE VIEW ………………………………………………………... 47 5.2 – DIALOG PRINCIPALE : IDD_RODGERS_i385_GUI_DIALOG 5.2.1 – DESCRIZIONE DELLA FUNZIONE: MODIFICATAB( ) …………….……….…. 49 …………………..……………. 52 5.2.2 – ELEMENTI COMBO BOX: RANK E VOICE PALETTE …………………..…………….. 54 5.2.3 – ELEMENTO COMBO BOX: MIDI DEVICE ……………………………………..…………. 55 5.2.4 – ELEMENTI GRAFICI …………………................................................................. 56 5.2.5 – PULSANTI EDIT_PAR_VOCE ED EDIT_PAR_NOTA ………………………..………… 58 5.3 – DIALOG EDIT PARAMETRI VOCE: IDD_DIALOG_PAR …………………………………. 59 5.3.1 – SPECIFICHE: ONINITDIALOG( ) ………………………………………………………………. 60 5.3.2 – MODIFICA DEI PARAMETRI …………………………………………………………………… 62 5.3.3 – INVIO MESSAGGIO MIDI 5.3.4 – ELEMENTI GRAFICI …………………………………………………..………………….. 63 ………………………………………………………………..……………… 70 5.4 – DIALOG EDIT PARAMETRI NOTA: IDD_DIALOG_NOTA………………………….……… 71 5.4.1 – PULSANTE SEND SYSEX …………………………………………………..……………………. 74 5.4.2 – ELEMENTI GRAFICI ……………………………………………………………………………….. 75 5.5 – DIALOG SLIDER: IDD_DIALOG_SLIDER ………………………………………………….…… 78 5.5.1 –SPECIFICHE DI FUNZIONAMENTO …………………………………………..……………… 79 5.6 – DIALOG: IDD_SELEZIONE_VOCI_MULTIPLE …………………………………..…….……. 81 5.6.1 – SPECIFICHE_1: LISTA DI BOTTONI ……………………………………………..………….. 83 5.6.2 – SPECIFICHE_2: MODALITA’ DI FUNZIONAMENTO …………………………………. 86 6. - VERIFICA FUNZIONAMENTO DEL’ INTERFACCIA ……………... 91 6.1 – UTILIZZO DI UN MIDI-MONITOR ……………………………………….…………………….. 91 6.2 – 3 ESEMPI ESSENZIALI ………………………………………………………….…………………... 92 7. - SVILUPPI FUTURI E CONCLUSIONI ………………………………. 98 8. - APPENDICI - APPENDICE A - ……………………………………………………………………………………………….. 92 - APPENDICE B - ……………………………………………………………………………………………….. 106 - APPENDICE C_1 - ……………………………………………………………………………………………. 107 - APPENDICE C_2 - …………………………………………………………………………………………….. 111 - APPENDICE D – ……………………………………………………………………………………………….. 123 9. - BIBLIOGRAFIA 10. - NOTE …………………………………………………………. 130 ……………………………………………………………………. 132 Capitolo 1 27 novembre 2008 1. - INTRODUZIONE 2.1 – INTERFACCE GRAFICHE Il mondo delle tecnologie informatiche risulta attualmente caratterizzato da innumerevoli metodologie e linguaggi di programmazione il cui sviluppo evolutivo ed ottimizzazione vengono pedissequamente spronati dai vari settori della ricerca scientifica, dalla implementazione produttiva nelle molteplici aziende settoriali mondiali e dal progresso scientifico generale, i quali volgono a soddisfare i fabbisogni di una realtà sociale ed ambientale, all‟ insegna di una crescente complessità e globalizzazione; tutto ciò è atto a favorire un miglioramento qualitativo dell‟ esistenza umana e dell‟ ecosistema in cui viviamo. Gli elevati standard qualitativi raggiunti nei campi pertinenti il settore informatico sono frutto di un boom tecnologico avviato negli anni 50‟ con la nascita della prima generazione di calcolatori moderni (Von Neumann realizzò il primo computer in grado di memorizzare interamente un programma) e proseguito poi con l‟ avvento, negli anni 70‟- 80‟, dei primi Micropocessori composti da una CPU (Central Processing Unit su circuito integrato) raggiungenti frequenze di clock di 2 Mhz; l‟ evoluzione è progredita esponenzialmente negli anni successivi integrandosi sempre più nella quotidianità delle società civili maggiormente coinvolte in tale cambiamento epocale. La larga diffusione di tali tecnologie in tutti gli strati sociali è stata resa possibile grazie ad un paradigma di sviluppo che permette a qualsiasi tipo di persona di interagire con apparecchiature elettroniche come computer, dispositivi elettronici portatili (lettori Mp3, Telefoni Cellulari, Videogames), strumentazione domestica (elettrodomestici, domotica) e da ufficio semplicemente manipolando elementi grafici (finestre, icone). Lo strato di un‟ applicazione software che si occupa del dialogo con l‟ utente del sistema utilizzando un ambiente grafico è definito come interfaccia grafica meglio conosciuta sotto il nome di Graphical User Interface (GUI). 1 Capitolo 1 27 novembre 2008 La progettazione e lo sviluppo di un‟ idonea GUI è proprio l‟ aspetto dominante dell‟ attività di tirocinio, oggetto di questa tesi, svolta nel particolare ambito produttivo offerto dall‟ azienda Roland Europe S.p.a., sita ad Acquaviva Picena (AP), una dei leader mondiali nella produzione di strumenti musicali elettronici, con numerose filiali dislocate in tutto il globo, esempio delle più innovative ed interessanti soluzioni tecnologiche del settore. Suddetto distretto industriale, avvalendosi dell‟ impiego di personale altamente specializzato, gestisce tutte le fasi dei processi produttivi garantendo standard qualitativi elevati e all‟ avanguardia, offrendo una vasta gamma di strumenti elettronici che (in tale ripartizione aziendale) risultano classificabili in 4 principali categorie: Tastiere elettroniche Fisarmoniche elettroniche Pianoforti digitali Organi liturgici Uno strumento musicale di tale tipo produce elettronicamente dei suoni emulando timbriche di stumenti reali (chitarra, pianoforte, violino, flauto, batteria etc…) oppure creando nuove sonorità non esistenti in natura, sfruttando svariate modalità di sintesi del suono (additiva, sottrattiva, modulazione di frequenza). Gli aspetti che contraddistinguono tale famiglia di strumenti elettronici sono trattati nell‟ “Appendice A” e risultano qui elencati: Multitimbricità Polifonia Unità effetti integrata (Dsp) Sequencer Connessioni audio e midi Ogni strumento inoltre include un‟ interfaccia utente dove appare possibile modificare vari parametri che caratterizzano ogni singolo timbro (pitch, frequenza, dinamica, volume, panpot, controlli midi, effetti e molto altro) 2 Capitolo 1 27 novembre 2008 editabili per mezzo di un monitor touch screen oppure di appositi tasti e manopole posizionate sul pannello principale. Tali elementi distintivi appaiono presenti anche nell‟ Organo Liturgico RODGERs i538 (Figura 1.1 ) oggetto di studio nelle varie fasi dell‟ attività progettuale di tirocinio, le cui caratteristiche estetiche e specifiche tecniche di composizione vengono ampiamente evidenziate nell‟ “Appendice B”. Figura 1.1 – Pannello principale dell‟ organo liturgico RODGERs Insignia 538-WD.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, L‟ immagine di Figura 1.1 illustra il pannello anteriore dell‟ organo e mette alla luce il fatto che il display lcd al centro (blu) , dove vengono visualizzati i vari parametri modificabili relativamente ad ogni singola voce, sia limitato e quindi abbastanza ostico da utilizzare per le varie funzioni di editing , durante le quali occorre attraversare una lunga serie di sottomenù per raggiungere la funzione desiderata. Si è ritenuto opportuno poter effettuare tali operazioni su un Editor grafico esterno collegato tramite protocollo midi (sistema standard di comunicazione fra strumenti musicali di cui si evidenzieranno le caratteristiche salienti nei capitoli successivi) allo strumento consentendone una visualizzazione ed una gestione molto più comoda ed agevole per l‟ utente finale. Si è deciso quindi di sfruttare le possibilità offerte dalle Microsoft Foundation Class (utilizzando il linguaggio di programmazione ad oggetti C++) per la creazione di una idonea Graphic User Interface avvalendosi dell‟ ambiante di sviluppo di Visual Studio 6 su piattaforma Windows XP. Tali scelte sono giustificabili dal fatto che il C++ è sicuramente uno dei linguaggi maggiormente approfonditi ed appresi durante il percorso formativo universitario; inoltre parte del progetto sviluppato fino ad ora dall‟ azienda medesima utilizza questo stessa sintassi di programmazione ed 3 Capitolo 1 27 novembre 2008 è risultato molto più semplice collegare e migliorare le parti di software già esistenti. Le potenzialità dell‟ applicativo Visual Studio 6 mostrano un importante connubio tra programmazione grafica ed interoperatività a basso livello con le svariate funzionalità del sistema operativo in cui si integra; risulta possibile creare con estrema facilità l‟ insieme di finestre e di elementi dell‟ interfaccia utente gestendo agevolmente anche l‟ interazione con il sistema operativo, impiegando il modello ad oggetti delle Microsoft Foundation Class (particolari librerie di sistema o dll). Questo insieme di Classi C++ operano ad un livello di astrazione più elevato rispetto alle Windows API (insieme di interfacce a basso livello in grado,tra l‟ altro, di gestire la comunicazione con l‟hardware del calcolatore e con le periferiche esterne) garantendo, in tale contesto applicativo, la corretta attivazione ed utilizzazione di tutte le funzioni per l‟ interfacciamento con l‟ hardware esterno. Le WIN32 (Windows API per piattaforme Microsoft a 32Bit) nel tempo si sono arricchite di nuove funzionalità inglobando anche quelle riguardanti la comunicazione con strumenti musicali elettronici nella osservanza degli standard imposti dal protocollo MIDI. Nei capitoli successivi verranno prima illustrate le caratteristiche generali dell‟ evoluzione nel tempo delle interfacce grafiche, dell‟ ambiente di sviluppo Visual Studio 6 in integrazione con le Microsoft Foundation Class e degli aspetti fondamentali riguardanti il protocollo MIDI; successivamente si esamineranno approfonditamente le varie fasi di ideazione e strutturazione della comunicazione con l‟ hardware esterno (Rodgers i538) e della creazione, gestione ed ottimizzazione dell‟ interfaccia utente definitiva. Giungendo infine alle verifiche di corretto funzionamento dell‟ applicativo sviluppato e concludendo con l „esaminazione dellle varie prospettive di sviluppo future valutandone gli aspetti salienti. 4 Capitolo 2 27 novembre 2008 2. - INTERFACCE GRAFICHE, VISUAL STUDIO 6.0 ED MFC: CARATTERISTICHE SALIENTI. 2.1 – INTERFACCE GRAFICHE GUI è l‟ acronimo di Interfaccia Grafica Utente (dall‟ inglese Graphical User Interface), che permette all‟ utente di interagire con un computer 1 manipolando graficamente degli oggetti, senza la necessità di conoscere i comandi da impartire tramite tastiera come invece avviene con le interfacce testuali Command Line Interface (CLI). Il concetto di interfaccia grafica nasce e si evolve con i primi sistemi operativi implementati nei computer di quarta generazione, fino ad assumere l‟ aspetto riscontrabile attualmente, concepito come un piano di lavoro contraddistinto dallo schermo (detto scrivania o desktop), con le icone a rappresentare i file (di cui alcune a forma di cartellina per le directory) e le finestre ad identificare le applicazioni. Tale ambiente di lavoro, in cui si opera attraverso il puntatore comandato con il mouse, è stato concettualizzato dai laboratori Xerox (progetto Alto) ed implementato per la prima volta nel 1981 nel costoso Xerox Star. La prima versione a colori di una GUI venne introdotta da Commodore con il suo Amiga Workbench nel 1985. Figura 2.1a – Interfaccia Xerox Star Figura 2.1b – Interfaccia Amiga Workbench 1 L’ interazione può avvenire con qualsiasi altro strumento elettronico che supporti tali modalità di comunicazione. 5 Capitolo 2 27 novembre 2008 Di pari passo si sono succedute nel tempo le varie release dei più conosciuti S.O. di casa Microsoft ed Apple rispettivamente Windows e Mac OS X fino alle ultime versioni attualmente in commercio con l‟ introduzione di notevoli miglioramenti grafici e prestazionali (figura 2.2). Apple Macintosh Desktop (1984) Mircosoft Windows 1.01 (1985) Apple Mac OS X “Leopard” (2007) Microsoft Windows VISTA (2007) Figura 2.2 – Evoluzione nel tempo delle interfacce grafiche nei sistemi operativi prodotti da Microsoft ed Apple.………….…………….…. In prospettiva il futuro sembra essere il Desktop 3d ed alcune soluzioni Open Source sono già fruibili facilmente dal web (figura 2.3). Figura 2.3 – Desktop 3d di Linux Fedora Core 6 6 Capitolo 2 27 novembre 2008 I miglioramenti che si sono verificati negli anni hanno contribuito ad una diffusione delle interfacce grafiche in svariati campi applicativi e settoriali; basti pensare alla fruizione dei servizi informatici nelle attività commerciali, nelle banche, negli enti pubblici e privati, nelle telecomunicazioni e nell‟ implementazione in svariati strumenti elettronici quali cellulari, lettori MP3, navigatori, video- games, microcomputer, elettrodomestici e strumenti musicali. Figura 2.4 – Esempi di interfacce grafiche dotate di touch screen implementate.rispettivamente su un telefono cellulare ed in una tastiera elettronica. Nella globalità dei vari campi d‟ utilizzazione l‟ interfaccia grafica si pone sempre tra l‟ utente ed un software applicativo dando l‟ accesso alle sue funzionalità in modo facile ed intuitivo. L‟ interazione, quindi, è la modalità con la quale si controlla l‟ esecuzione dell‟ applicazione tramite tastiera, mouse, penna ottica, schermi sensibili al tatto, comandi vocali ed altro. Essa consiste in azioni dell‟ utente che provocano reazioni (feedback) da parte dell‟ interfaccia e possono essere classificate come: posizionare il puntatore su un oggetto, selezionarlo, spostarlo navigare attraverso l'interfaccia (es. menu' a cascata) attivare una funzionalita' trasferire informazioni (es. immettere un parametro) Una generica interfaccia si compone di 2 elementi costitutivi: 7 Capitolo 2 27 novembre 2008 una o piu' finestre vari dispositivi di interfaccia La finestra (in inglese window) identifica un‟ area del desktop (schermo) attraverso cui l'utente effettua il dialogo con l'elaboratore servendosi di una tastiera, di un puntatore tipo mouse, di una penna o di una trackball. Essa è costituita a sua volta da: contorno (window frame) barra del titolo (title bar) zona di contenuto (content area) I primi 2 sono elementi standard di tutte le window esistenti su un sistema, definibili come "decorazioni" del window manager (WM), senza alcun controllo da parte del programma applicativo. La barra del titolo mostra il nome dell'applicazione che è in esecuzione nella finestra primaria nel desktop. Nel contorno e nella barra del titolo si collocano i dispositivi di controllo; essi rappresentano elementi visivi che l'utente puo' manipolare con un apparato di input per compiere determinate operazioni classificabili nel seguente modo: modifica della posizione (MOVE) modifica di altezza e larghezza (RESIZE) chiusura con terminazione dell'applicazione (CLOSE o QUIT) iconificazione (MINIMIZE) espansione a tutto schermo (MAXIMIZE) ritorno a dimensioni precedenti (RESTORE) Nelle zona di controllo avviene l'interazione con l‟ utente (visualizzazione di dati, raccolta di input) effettuando un collegamento diretto con l‟applicazione a cui la finestra appartiene. Esempi: editing di testi 8 Capitolo 2 27 novembre 2008 editing di disegni animazione grafica tridimensionale menu bar (barra del menu'): contiene le funzioni principali dell'applicazione area messaggi: contiene messaggi di help o di spiegazione status bar: mostra lo stato attuale dell'applicazione vari dispositivi di interfaccia I dispositivi di interfaccia rappresentano oggetti con aspetto grafico e sensibilita' a particolari eventi che "popolano" una finestra ed operano la interazione con l'utente. Si classificano in diversi tipi, ciascuno dei quali con certe caratteristiche di aspetto e comportamento: Elementi attivi (sensibili a input). Esempi: o menu' (permettono di immettere una scelta da una lista di opzioni) o bottoni (sensibili al click) o dispositivi per introduzione di testo (campi di input) o dispositivi per introduzione di valori numerici (potenziometri lineari, circolari, digitali) Elementi inerti (datati di solo aspetto grafico). Esempi: o etichette testuali o etichette grafiche (bitmap) o cornici ed altri elementi decorativi Aree dedicate a scopi speciali. Esempi: o aree grafiche o aree di visualizzazione ed editing di testi 9 Capitolo 2 27 novembre 2008 Figura 2.4 – Esempi di dispositivi di Interfaccia con la presenza di elementi attivi ed inerti. 10 Capitolo 2 27 novembre 2008 2.2 – MICROSOFT VISUAL C++ 6.0 Microsoft Visual C++ 6.0 (anche noto come MSVC) è un Integrated Development Environment (IDE2) di Microsoft per la programmazione nei linguaggi C, C++ ed è orientato soprattutto allo sviluppo e al debug di codice C++ basato sulle API 3 di Microsoft Windows. Risulta particolarmente indicato per la costruzione di Graphic User Interface in interazione con le Microsfot Foundation Class o MFC (trattate nel paragrafo successivo). Di seguito è visionabile una schermata di esempio caratterizzante lo ambiente di sviluppo Visual C++ mostrandone le componenti principali. Figura 2.5 – Interfaccia di Microsoft Visual C++. Ambiente di sviluppo particolarmente indicato per la creazioni di applicativi Windows con elementi 2 Un integrated development environment (IDE), in italiano ambiente integrato di sviluppo è un software che aiuta i programmatori nello sviluppo dell’ applicativo. 3 Le Application Programming Interface API (Interfa ccia di Programmazione di un'Applicazione), sono ogni insieme di procedure disponibili al programmatore, di solito raggruppate a formare un set di strumenti specifici per un deter minato compito. È un metodo per ottener e un'astrazione, di solito tra l'hardware e il programmatore, o tra software a basso ed alto livello. 11 Capitolo 2 27 novembre 2008 avanzati d „interfaccia. Risultano qui menzionati i componenti principali. Nella workspace window sono visualizzati i file di progetto (modalità FileView). Lo sviluppo di un‟ applicazione in ambiente Windows si basa su un progetto (project) identificabile come un„ insieme di files di risorse relazionate la cui compilazione e “collegamento” (linking) realizzano un programma eseguibile (file .exe). L „aspetto rilevante in tale ambiente di sviluppo è l‟ implementazione di un potente editor (“compositore”) grafico per finestre di dialogo, icone, immagini e stringhe (sono tutti elementi di interfaccia). Figura 2.6 – Resource View di Visual C++. All‟ interno della workspace window (“finestra di lavoro”) del visualizzatore di risorse sono esaminabili gli elementi grafici: finestre di dialogo, icone, menù e string table. Questo strumento permette al programmatore l‟ inserimento di controlli di Windows e anche controlli di tipo ActiveX. Ogni progetto possiede un file RC (resource script) di tipo testuale che descrive gli elementi d„ interfaccia utilizzati. Nel Visual C++ si delinea anche la presenza di un visualizzatore di classi (strumento coadiuvante nella programmazione C++). 12 Capitolo 2 27 novembre 2008 Figura 2.7 – Class View di Visual C++. Diagramma a grafo della gerarchia delle classi. Compare qui una diagramma a grafo della gerarchia delle classi. Selezionando una classe è possibile visionare le classi derivate o le classi di base con le rispettive funzioni membro. 2.3 – MICROSOFT FOUNDATION CLASS: MFC Lo sviluppo in ambiente Windows, come introdotto dal paragrafo precedente, si e‟ orientato, nel corso degli ultimi anni,verso il linguaggio C++ Object Oriented, utilizzato congiuntamente alla libreria di classi MFC. Le applicazioni Windows operano utilizzando un modello di programmazione chiamato “event driven”; consiste nel gestire la reazione del programma di fronte ad eventi (ad esempio la pressione del pulsante del mouse o la chiusura di una finestra) che vengono segnalati all‟applicazione tramite messaggi inviati dal sistema operativo.In sostanza un‟applicazione Windows non ha un vero e proprio main, ma si limita ad aprire una o piu‟finestre e a posizionarsi in un ciclo di attesa (loop dei messaggi), 13 Capitolo 2 27 novembre 2008 aspettando che il sistema operativo invii messaggi all‟applicazione in risposta ai vari eventi che si possono verificare durante l‟esecuzione del programma. I messaggi in arrivo diretti ad una applicazione vengono accodati in attesa dell‟elaborazione e solitamente si continua l‟esecuzione in questo modo fino all‟arrivo di un messaggio di richiesta di terminazione del programma (WM_QUIT), che generalmente viene inviato quando l‟utente preme il tasto „x‟ su di una finestra, oppure seleziona la voce „Esci‟ dalla barra dei menu. La complessita‟ di sviluppo delle applicazioni Windows deriva pertanto dalla necessita‟ di prevedere in fase di progetto tutti i possibili messaggi che dovranno essere gestiti e che quindi richiederanno la scrittura di una routine di gestione apposita.…………………………….. La libreria MFC semplifica questo processo prendendosi carico di gestire al suo interno gran parte dei messaggi che l‟applicazione riceve e lasciando quindi al programmatore solo la scrittura della logica del programma. 2.3.1 – MFC: DEFINIZIONE E SPECIFICHE Microsoft Foundation Classes, meglio conosciuta con l'acronimo MFC, è una libreria di classi C++ prodotta da Microsoft con lo scopo primario di incapsulare all‟interno di classi (piu‟ di 200 nella versione 6.0) le API di Windows e di fornire inoltre un ambiente di programmazione Windows ad oggetti che aiuti il programmatore nel complesso meccanismo di sviluppo delle applicazioni dotate di interfaccia grafica. Le classi MFC forniscono un architettura di sviluppo che semplifica la gestione dei compiti piu‟ ripetitivi di ogni applicazione Windows come la gestione dei messaggi, ed offre inoltre alcuni potenti strumenti per separare l‟implementazione dei dati di un programma dalla sua rappresentazione grafica raggiungendo un livello di astrazione sensibilmente maggiore rispetto all‟SDK 4 . 4 Software Development Kit (più brevemente SDK) è un termine che in italiano si può tradurre come "pacchetto di sviluppo per applicazioni", e sta a indicare un insieme di strumenti per lo sviluppo e la documentazione di software. 14 Capitolo 2 27 novembre 2008 Possiamo riassumerne gli aspetti salienti in tale schema: Mappa dei Messaggi Device Context Intercettazione di dispositivi di input I controlli di base Dialog Box Mappa dei Messaggi - Il loop dei messaggi e le funzioni di callback che venivano utilizzate con l‟SDK sono state incapsulate all‟interno di MFC e quindi il programmatore deve solo utilizzare le macro BEGIN_MESSAGE_MAP e END_MESSAGE_MAP per specificare quali saranno i messaggi gestiti dalla sua applicazione. Di seguito un esempio implementativo: BEGIN_MESSAGE_ MAP (C MainWindow, CFrameWnd) ON_WM_PAINT () END_MESSAGE_ MAP () E‟ possibile effettuare anche una classificazione dei principali messaggi riguardanti una window: Messaggio WM_CHAR WM_COMMAND WM_CREATE WM_DESTROY WM_LBUTTONDOWN WM_LBUTTONUP WM_MOUSEMOVE WM_PAINT M_QUIT WM_SIZE Descrizione Un carattere è inserito da tastiera L‟ utente seleziona un elemento dal menù o un controllo invia una notifica alla finestra “parente” E‟ creata una finestra E‟ chiusa una finestra Il tasto sinistro del mouse risulta schiaccciato Il tasto sinistro del mouse risulta rilasciato Il puntatore del mouse risulta in movimento Un finestra deve essere ridisegnata (aggiornata) L‟ applicazione viene terminata Un finestra risulta ridimensionata Device Context - Per poter disegnare all‟interno di una finestra e‟ necessario disporre di un oggetto denominato “Device Context” che serve ad astrarre cio‟ che si vuole disegnare dal destinatario fisico del disegno (schermo o stampante indipendentemente dalla risoluzione, numero dei colori, modello della scheda video ecc.). 15 Capitolo 2 27 novembre 2008 Non e‟ possibile effettuare alcun tipo di output diretto su di una finestra senza l‟utilizzo di un Device Context. Una volta istanziato l‟oggetto di tipo Device context e‟ possibile richiamarne il metodo DrawText per inviare del testo ad un area rettangolare all‟interno della finestra definita istanziando un oggetto di classe CRect. Dal questo brano di codice si comprende che la sequenza di passi per disegnare su di una window e‟ sempre composta da: Creazione di un oggetto device context Creazione di eventuali oggetti che modificano le caratteristiche del disegno (come CPen e CBrush) Salvataggio del vecchio oggetto associato al device context Utilizzo delle funzioni di disegno con le nuove caratteristiche tramite il device context Ripristino delle condizioni iniziali del device context Rilascio del device context (distruzione); implicita quando la funzione OnPaint esce di visibilita‟. Inte rcettazione di dispositivi di input - Come si puo‟ gia‟ intuire da quanto detto fino ad ora riguardo alla programmazione Windows, l‟intercettazione dell‟input proveniente dal mouse e dalla tastiera (dispositivi di input) avviene tramite la gestione di appositi messaggi da parte dell‟applicazione. Ad esempio la pressione dei tasti del mouse ed il suo movimento sono comunicati dal sistema operativo alle applicazioni tramite una serie di messaggi come ad esempio WM_LBUTTONDOWN, che indica che il pulsante sinistro del mouse e‟ stato premuto, oppure WM_MOUSEMOVE che indica che il mouse e‟ stato mosso al di sopra della finestra dell‟applicazione. Il programma che vuole gestire questi eventi deve aggiungere alla mappa dei messaggi l‟indicazione dei messaggi a cui e‟ interessata e deve fornire le corrispondenti routine di gestione per gli eventi ricevuti. Prendendo in esame un‟ applicazione di esempio risulta possibile effettuare la seguente analisi del file header: 16 Capitolo 2 27 novembre 2008 // l’applicazione gestisce la pressione del tasto destro // e sinistro del mouse e il doppio click del tasto // sinistro afx_msg void OnLButtonDown (UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point); afx_msg void OnRButtonDown (UINT nFlags, CPoint point); Nella mappa dei messaggi si ha l‟associazione tra eventi e corrispondenti routine di gestione: BEGIN_MESSAGE_ MAP (C MainWindow, CWnd) … ON_WM_LBUTTONDOWN () ON_WM_LBUTTONDBLCLK () ON_WM_RBUTTONDOWN () … END_MESSAGE_ MAP () e nel file.cpp si troveà l‟implementazione delle varie routine di gestione degli eventi legati al mouse: // in questo caso l’implementazione della routine di // gestione per l’evento double click del pulsante sinistro void CMainWindow::OnLButtonDblClk (UINT nFlags, CPoint point) { … CClientDC dc (this); if (dc.GetPi xel (point) == RGB (0, 0, 0)) ResetGame (); … } Ogni messaggio derivante da una operazione di input da parte dell‟utente su mouse e tastiera avra‟ argomenti di input diversi che poi andranno utilizzati nell‟implementazione della funzione di gestione; ad esempio il messaggio LBUTTONDBLCLK invia alla funzione di gestione un oggetto di tipo CPoint contenente le coordinate del punto in cui e‟ stato effettuato il doppio click del mouse ed un intero contenente un flag che specifica informazioni addizionali come lo stato dell‟altro pulsante oppure se alcuni tasti speciali della tastiera come CONTROL e SHIFT risultavano premuti al momento del doppio click. Le coordinate vengono gia‟ passate dal sistema operativo in maniera relativa rispetto alla finestra dell‟applicazione e quindi possono essere facilmente utilizzate dal programmatore. 17 Capitolo 2 27 novembre 2008 I controlli di base – I controlli possono essere considerati come i “mattoni” che servono a costruire l‟interfaccia di qualsiasi applicazione Windows. In generale un controllo e‟ uno speciale tipo di finestra che permette di interagire con l‟utente finale, sia in termini di input che di output; i piu‟ comuni controlli utilizzati in pressoche‟ tutte le applicazioni Windows sono bottoni, label, textbox, listbox, combo box e check box. Naturalmente esistono molti altri controlli, ed altri se ne aggiungono via via che l‟interfaccia delle varie versioni di Windows viene migliorata ed evoluta; il programmatore li puo‟ utilizzare come oggetti preconfezionati (classi in MFC) tramite la loro interfaccia senza preoccuparsi di come siano stati implementati. Per utilizzare i controlli di Windows all‟interno delle applicazioni MFC in genere si procede in questo modo: Nel .h della classe che implementa la finestra si aggiungono gli oggetti che si vorranno visualizzare nella finestra sotto forma di oggetti membro di tipo variabile a seconda della classe di oggetto (es. CButton m_myButton, CListbox m_myListBox ecc.) Al momento della creazione della una finestra si creano anche i controlli che sono contenuti nella finestra (chiamando i metodi create delle varie classi MFC che implementano i controlli come CButton, CListbox ecc.). Nella mappa dei messaggi vanno aggiunti gli eventi generati dai controlli utilizzati; ad esempio il controllo bottone genera un evento ON_BN_CLICKED che viene generato al momento della pressione. Gli eventi dei controlli che si desidera gestire vanno associati alle corrispondenti routine di gestione (es. OnButtonClicked). Le routine di gestione vanno implementate all‟interno dell‟applicazione. Ogni controllo ha le sue caratteristiche, attributi ed eventi che puo‟ generare con relativi differenti prototipi delle routine di gestione; e‟ chiaro quindi che 18 Capitolo 2 27 novembre 2008 e‟ necessario leggere la documentazione di ogni tipo di controllo per poterlo utilizzare correttamente all‟interno delle proprie applicazioni. Dialog Box - Nelle applicazioni Windows di solito i controlli non vengono posizionati nella finestra principale dell‟applicazione, ma in speciali finestre progettate per la raccolta dell‟input da parte dell‟utente dette “dialog box”. Un esempio puo‟ essere la finestra di salvataggio dei file che troviamo in tutte le applicazioni Windows oppure la finestra in cui si definiscono le opzioni di stampa. Per inserire una dialog box in una applicazione MFC e‟ necessario ereditare una propria classe dalla classe base CDialog, inserire i controlli all‟interno della dialog, ed eventualmente implementare alcune funzioni virtuali che gestiscono il comportamento della dialog. In sintesi i passi da seguire per aggiungere ad una applicazione una semplice dialog box che richiede l‟input di un campo test e di uno numerico (listato 2_2_4), sono questi: Sebbene sia possibile fare tutto manualmente e‟ bene in questo caso avvalersi dell‟aiuto dell‟ambiente di sviluppo per la creazione della struttura base della dialog e quindi dal menu Insert selezionare New Form, scegliere come classe base CDialog e specificare il nome; il Visual Studio provvedera‟ a creare il file di risorse, ed i file .cpp e .h per la nostra nuova dialog. A questo punto risulta possibile aprire il resource editor e modificare l‟aspetto della dialog box aggiungendo i controlli di input necessari (i bottoni OK e CANCEL con la relativa gestione base sono gia‟ stati implementati dallo Wizard). Una volta aggiunti i controlli di input graficamente e assegnato loro un nome (es. IDC_EDITNAME) e‟ necessario aggiungere nel .h tante variabili membro della classe CMyDialog quanti sono i campi associati ai controlli di input. 19 Capitolo 2 27 novembre 2008 E‟ necessario poi effettuare l‟associazione tra le variabili membro ed i controlli di input e definire se devono essere effettuati controlli di validazione sull‟input inserito Una volta preparata la dialog box e‟ possibile richiamarla dall‟applicazione istanziando un oggetto di tipo CMyDialog in maniera “modale” (la dialog non si chiude fino a che non si preme Ok o Cancel e non si puo‟ passare ad altre finestre) oppure “non modale” la dialog puo‟ esssere chiusa e trattata come le finestre tradizionali. Viene proposta una finestra di dialogo di default, che risulta personalizzabile con i controlli (già in parte illustrati), che possono contenere: immagini testo statico testo editabile raggruppamenti pulsanti Check box radio box combo box list box scroll bar orizzontali scroll bar verticali spin progress slider hot key list control tree control tab control animazioni 20 Capitolo 2 27 novembre 2008 rich edit custom control Figura 2.8 – Finestra di dialogo di default, che si può personalizzare con l‟ inserimento grafico deii controlli prelevabili dalla barra verticale a destra e precedentemente elencati. Dal ClssWizard relativo ad una Dialog risulta possibile l „intera gestione delle variabili membro e delle funzioni membro associate ad essa. Figura 2.9 – Con il ClssWizard relativo ad una Dialog risulta possibile l „intera gestione delle variabili membro e delle funzioni membro associate ad essa . 21 Capitolo 3 27 novembre 2008 3. - PANORAMICA SUL PROTOCOLLO MIDI 3.1 – INTRODUZIONE MIDI è l'acronimo di Musical Instrument Digital Interface, ovvero una interfaccia digitale per strumenti musicali. Più precisamente il midi è un protocollo di comunicazione in quanto standard riconosciuto nel mondo da tutti i costruttori, non solo di strumenti musicali, ma anche d‟apparecchiature informatiche e audio/video. Il protocollo MIDI quindi stabilisce le specifiche sia hardware ( interfaccia, cavi, connettori ), sia software ( linguaggio informatico, modalità di trasmissione, tipologia dei messaggi ) che ogni apparecchiatura deve “saper comprendere ” se desidera essere veramente MIDI compatibile. Perciò è stato necessario costruire una interfaccia di comunicazione e creare un linguaggio di comunicazione. I messaggi che gli strumenti si possono scambiare sono svariati: suona il DO della terza ottava, abbassa il volume al pianoforte (manda sul canale audio destro il suono del violino ecc.). Questi messaggi viaggiano attraverso un cavo che collega gli strumenti e risultano caratterizzati da valori discreti compresi tra 0 e +5 volt; e si distinguono dai messaggi audio (che sono variazioni continue di tensione). Rappresentano quindi segnali digitali cioè sequenze di 0 ed 1. 3.2 – L’ INTERFACCIA E LE PORTE MIDI L'interfaccia midi è il dispositivo (costruito secondo regole ben precise) che consente alle apparecchiature di dialogare tra loro ed utilizza delle porte d'ingresso e di uscita per ricevere e trasmettere i dati. Queste porte si chiamano rispettivamente MIDI-IN e MIDI-OUT. L'interfaccia garantisce il funzionamento della trasmissione. La trasmissione dei dati avviene alla velocità di 31250 bit al secondo, ovvero 1 bit ogni 32 milionesimi di secondo e risulta definita come asincrona, ovvero occorre un bit di start e 22 Capitolo 3 27 novembre 2008 uno di stop oltre agli otto bit della trasmissione. Quindi un byte che viene ricevuto o trasmesso dall' interfaccia midi è composto da 10 bit . Le porte, come già è stato detto, si dividono in: MIDI-IN: è la porta che riceve i dati trasmessi da una apparecchiatura musicale. MIDI-OUT: è la porta che serve a trasmettere i dati ad altre apparecchiture musicali. MIDI-THRU: è una porta che bypassa il segnale ricevuto alla porta MIDI-IN. Il segnale così, inalterato, può essere trasmesso ad un'altra apparecchiatura musicale. E' importante quando si esegue il collegamento di apparecchiature musicali ricordare che: La porta MIDI-IN va collegata ad una porta MIDI-OUT o MIDI-THRU. La porta MIDI-OUT deve essere collegata esclusivamente ad una porta MIDI-IN. La porta MIDI-THRU deve essere collegata esclusivamente ad una porta MIDI-IN. 3.3 – CAVI DI COLLEGAMENTO MIDI I cavi utilizzati per la trasmissione di dati midi sono composti da cinque fili, anche se in realtà ne vengono utilizzati solo tre. Il connettore per il collegamento alle porte MIDI-IN,OUT, THRU è a cinque poli. Due poli e due cavi non vengono praticamente utilizzati. 23 Capitolo 3 27 novembre 2008 Figura 3.1 – Connettore standard MIDI di tipo DIN a 5 pin Il connettore standard MIDI (Figura 3.1 ) è di tipo DIN (Deutshe Industre Normen) a 5 pin: Il pin 1 e 3 sono riservati per sviluppi futuri (non utilizzati). Il pin 2 è collegato a massa (per evitare interferenze). Il pin 5 collega il filo necessario per la trasmissione dei dati, mentre il pin 4 collega il cavo che assicura l'alimentazione corretta a + 5V. Importante risulta anche il tipo di collegamento USB to MIDI con cui è possibile connettere un‟ applicazione su PC, che sfrutta il protocollo midi, con uno o più device (dispositivi) esterni. Figura 3.2 – Cavo di collegamento “USB to Midi” (per interfacciamento di un PC con un hardware esterno midi) 3.4 – MESSAGGI MIDI E LORO STRUTTURAZIONE FISICA Con l'interfaccia MIDI è possibile collegare insieme tastiere, expanders (moduli contenenti suoni), computer,pedaliere MIDI, e tutto ciò che abbia un'interfaccia MIDI. L'insieme di tutti i messaggi che le macchine si possono scambiare costituiscono il protocollo MIDI. 24 Capitolo 3 27 novembre 2008 Dunque è un sistema di comunicazione dati, che consente ad uno strumento musicale o altro dispositivo (detto master) di controllarne un altro (detto slave), e anche più di uno, in modo da suonare insieme le stesse note, cambiare i timbri nello stesso momento, iniziare (da capo o dal segno) i brani memorizzati, mantenere la sincronizzazione. In alcuni casi , è anche possibile, controllare altri parametri, come modulation wheel, pitch bender, e anche i valori di inviluppo, filtri e volume. Il protocollo MIDI dunque stabilisce le modalità con le quali devono essere inviati i messaggi tra le varie porte MIDI di un dispositivo. Un messaggio MIDI è composto da un insieme di byte. Questi possono essere status byte e data byte. BYTE STATUS BYTE DATA BYTE Un messaggio MIDI deve sempre iniziare con uno status byte. Gli status byte servono per definire in modo univoco un comando. Il bit più significativo dello status byte è uguale ad 1 e quindi puòassumere un valore compreso tra 128 e 255.Gli status byte trasmettono quindi il tipo di informazione (suona una nota, alza il volume, ecc). I data byte invece servono per inviare gli eventuali parametri necessari per uncorretto funzionamento dello status byte. Il bit più significativo (MSB) del data byte è uguale a 0 e quindi un data byte può assumere un valore decimale compreso tra 0 e 127. Struttura di un singolo messaggio midi: STATUS BYTE DATA BYTE 1 DATA B YTE 2 25 Capitolo 3 27 novembre 2008 3.5 – CANALI MIDI Le informazioni per la comunicazione fra strumenti passano attraverso il cavo di trasmissione sfruttando un certo numero di Canali MIDI Infatti, utilizzare un cavo MIDI per trasmettere un solo messaggio non sarebbe conveniente e per questo motivo una linea MIDI è stata divisa in 16 canali (logici) di comunicazione. Ognuno di essi può trasmettere una determinata informazione che può essre ricevuta da un dispositivo sintonizzato su quel canale Figura 3.3 – Canali MIDI. Conversione in binario ed esadecimale dei 16 canali. 26 Capitolo 3 27 novembre 2008 3.6 – STRUTTURA DEI MESSAGGI MIDI Figura 3.4 – Strutturazione dei Messaggi Midi: Channel Message e System Message. I messaggi MIDI si dividono in due categorie principali: Channel Message System Message Channel Message - Un messaggio di canale utilizza 4 bits nello Status Byte per l‟ indirizzamento del messaggio verso uno dei 16 canali Midi e 4 bits per definire il messaggio stesso. Quindi i messsaggi di canale sono quelli che possono essere indirizzati ad uno qualsiasi dei sedici canali MIDI, essi si dividono in: Channel voice message Channel mode message 27 Capitolo 3 27 novembre 2008 System Message - Questi messaggi, a differenza dei Channel message non contengono informazioni di canale,bensì di sistema, quindi possono essere ricevuti da qualsiasi apparecchiatura MIDI. Essi si dividono ulteriormente in: System common message System Real time message System exclusive message 3.7 – CHANNEL VOICE MESSAGE Analizziamo ora il particolare messaggio : Note on - Note off Quando in uno strumento musicale elettronico si preme un tasto, esso manda via MIDI un messaggio definito come Note on con le specifiche di che nota si tratta, con quale intensità si stà suonando (velocity = dinamica) e in quale ottava. Un esempio di messaggio di Note on è il seguente: Status Byte: 1001cccc (Note on), cccc indica il canale di trasmissione. Data Byte 1: 0nnnnnnn (Key number) gli n indicano il numero della nota che deve essere suonata. Data Byte 2: 0vvvvvvv (Velocity number), le v indicano la velocità con cui si preme il tasto. Le note sono rappresentate da numeri: 0 per la nota C, 2 per C#, 3 per D ecc. Quando il tasto viene rilasciato lo strumento produce un messaggio di Note off indicante quale tasto è stato rilasciato. 28 Capitolo 3 27 novembre 2008 3.8 – SYSTEM EXCLUSIVE MESSAGE I messaggi di sistema esclusivo non sono assegnati ad alcun canale Midi in particolare ed in base allo strumento specifico a cui devono essere indirizzati assumono un propria formattazione speciale relativamente al produttore. Infatti si è pensato di introdurre all‟interno di un messaggio di sistema esclusivo un ID numbe r che identifichi la marca produttrice dello strumento stesso. Di seguito una tabella esplicativa dei principali ID number delle principali marche: Roland 0x41 Korg 0x42 Yamaha 0x43 Casio 0x44 Akai 0x45 Waldorf 0x3E Tale codice identificativo indica alle apparecchiature aventi lo stesso ID che il messaggio di sistema esclusivo è per loro e tutte le altre apparecchiature collegate, ma con ID differente, ignoreranno quel messaggio. Tramite i messaggi esclusivi si possono inviare dati tipo: parametri relativi alle patch dello strumento (esempio la curva di inviluppo relativa di un timbro dello strumento) bulk dump (scaricare su un sequencer tutti i parametri che sul nostro strumento stanno costruendo il suono) Quindi programmando dati di sistema esclusivo si ha un controllo completo sul comportamento dello strumento, spesso riuscendo ad accedere a funzioni non raggiungibili in altri modi. Tipico esempio è la costruzione di un Editor Grafico su PC che permetta di controllare i parametri e l‟ editing di uno specifico strumento con l‟ invio di appropriati messaggi esclusivi via midi. 29 Capitolo 3 27 novembre 2008 3.9 – STRUTTURA SYSTEM EXCLUSIVE MESSAGE System exclusive Status Byte: 11110000 (Sistema esclusivo) F0H Lo status bytes è seguito da una serie di byte che costituiscono l‟informazione (compreso l‟ID). End of exclusive Status Byte: 11110111 (Fine messaggio) F7H Indica la fine di messaggio di sistema esclusivo. In particolar modo l‟ implementazione Midi realitiva al costruttore ROLAND utilizza la seguente formattazione per tutti i messaggi esclusivi: BYTE Description F0H Exclusive status 41H Manufactures ID (Roland) DEV Device ID MDL Model ID 11H Command ID [BODY] MainData Sum Check sum F7H End of exclusive Figura 3.6 – Strutturazione dei Messaggi Midi: Channel Message e System Message. MIDI status: F0H, F7H .Un messaggio esclusivo deve essere compreso tra 2 ……………… status bytes, partendo con il Manufactures- ID ……………… immediatamente dopo F0H. 30 Capitolo 3 27 novembre 2008 Manufactures- ID: 41H Manufactures- ID relativo alla Roland. Device-ID: DEV Il device- ID contiene un unico valore che identifica il …………… singolo device Relativamente alla connessione midi di più strumenti . Model- ID: MDL Contiene un valore che unicamente identifica un :::::::::::::::::::: modello da un altro. Command- ID: CMD Indica la funzione di un messaggio esclusivo. Main Data: BODY Questo campo contiene i dati che si vogliono inviare ……………… con il messaggio Esclusivo. Check Sum: sum Somma di controllo del messaggio. 3.10 – GENERAL MIDI Quando si cambia un timbro di uno strumento tramite il program change (tipo di messaggio di canale) esso dà l'informazione ad un modulo sonoro che a seconda dell'implementazione può far corrispondere il suono desiderato oppure un altro suono completamente diverso da quello scelto. Ad esempio, se su di un sintetizzatore A la patch 1 corrisponde ad un organo non è detto che la patch 1 di un sintetizzatore B sia uguale, anzi potrebbe corrispondere tutt'altro strumento. 31 Capitolo 3 27 novembre 2008 Si tratta quindi di stabilire uno standard in modo che tutti i sintetizzatori, sebbene con suoni diversi, facciano corrispondere gli strumenti con patch già prestabilite. Nel 1991 fu introdotto lo standard GM, general MIDI level 1. 3.11 – SPECIFICHE GENERAL MIDI Sound Generating System PCM wave generator + TVF (filtro) + TVA(ampiezza) + Reverb & Chorus Il minimo numero di polifonia deve essere 24 (o superiore) Una risorsa sonore (modulo sonoro) deve includere 16 parti strumentali. Ogni parte deve corrispondere ad un canale midi individualmente. Uno strumento musicale per essere GM compatibile deve associare ad ogni valore di program change una patch stabilita. Il numero dei timbri GM deve essere 128, suddivisi in 16 famiglie con 8 strumenti ognuna. La batteria e le percussioni devono risiedere obbligatoriamente nel canale midi 10. Di seguito la mappa degli strumenti GM con la patch corrispondente. 32 Capitolo 3 27 novembre 2008 3.12 – ROLAND GS GENERAL SYSTEM Lo standard GS è un evoluzione dello standard general MIDI level 1 ed è stato introdotto dalla Roland. Questo standard permette infatti di utilizzare più strumenti rispetto ai 128 consentiti dal GM,e introduce nuovi tipi di messaggi e di controller. Il GS introduce il concetto di BankSelect, esso unito al program change permette di avere 128 variazioni per ogni singolo strumento. Da sottolineare il fatto che il numero di banchi a disposizione in un sintetizzatore dipende esclusivamente dal produttore dello strumento e non dalle specifiche GS. Inoltre lo standard GS permette di modificare effetti audio come il chorus e il reverbero ecc. 33 Capitolo 4 27 novembre 2008 4. - SVILUPPO PROGETTO RODGERS i538 4.1 – INTRODUZIONE Lo svolgimento progettuale di ideazione e costruzione di un editor grafico di interfacciamento con l‟ organo liturgico Rodgers i538 e di interazione con l‟ utente finale ha attraversato diverse fasi di elaborazione e studio riassumibili nel seguente modo: analisi delle parti di software già sviluppate ed implementate dall‟ azienda nello strumento elettronico individuazione ed estrapolazione dei files (C++) da utilizzare nella modellazione dell‟ interfaccia standardizzazione del messaggio Midi per la corretta comunicazione con il Rodgers i538 ideazione,organizzazione ed ottimizzazione della Graphic User Interface su più livelli di finestre (dialog) implementazione software dell‟ interfaccia tramite MFC nell‟ ambiente di sviluppo Visual C++ verifica finale dei risultati ottenuti mediante l‟ utilizzo di un Midi-Monitor In collaborazione con gli ingegneri e progettisti che hanno sviluppato lo strumento elettronico in questione, si è potuto analizzare il sofware già implementato focalizzando le caratteristiche salienti e le modalità di funzionamento basilari; sono stati individuati i seguenti 2 files di progetto su cui porre le basi di collegamento e integrazione con l‟ interfaccia utente da costruire : baroque.cpp e voicedata.h . In essi è inclusa tutta la complessa strutturazione su più livelli delle voci caratterizzanti il Rodgers i538 evidenziandone anche il numero ed il tipo di parametri editabili al loro interno. Nei paragrafi successivi si opererà un approfondimento su tali parti di software e si porranno le basi per l „ideazione dell‟ interfaccia con la 34 Capitolo 4 27 novembre 2008 prerogativa di rispettare gli standard imposti sia dal protocollo midi sia dai modelli grafici di costruzione trattati nei capitoli introduttivi. 4.2 – DESCRIZIONE FILES DI PROGETTO: BAROQUE.CPP E ………VOICEDATA.H Il pannello principale dell‟ organo liturgico Rodgers i538 (Figura 1.1) e l‟ “Appendice B” che ne descrive le caratteristiche tecniche, mettono in risalto la sua strutturazione fisica di base; le parti dello strumento suonabili da un musicista sono 3: 1. Pedaliera 2. Tastiera inferiore 3. Tastiera superiore Ad ognuna di esse l‟ esecutore può assegnare una o più voci selezionando i tasti relativi ad ogni parte sul pannello principale. In esso è possibile individuare (da sinistra a destra) le 3 sezioni corrispondenti alle rispettive voci assegnabili dall‟ utente: 1. Pedal 2. Manual II 3. Manual I Ogni voce principale è costituita da 2 livelli di sottovoci che simulano altrettanti livelli di canne dell‟ organo. Ognuno di tali sottosezioni è composta a sua volta da altre 8 sottovoci Figura 4.1 – Livelli di strutturazione delle voci interne al Rodgers i538 Tutte le voci possiedono una stessa struttura di base descritta da un certo numero di parametri direttamente editabili dall‟ utente. 35 Capitolo 4 27 novembre 2008 L‟ intera complessa architettura multilivello definita e residente nell‟ organo liturgico appare implementata nel file baroque.cpp . E‟ evidente quindi la sua importanza basilare nello sviluppo del progetto e nella modellazione grafica dell‟ interfaccia. Il codice sorgente completo è visionabile nel workspace dell‟ ambiente di sviluppo Visual C++ (sono più di mille pagine di codice e sarebbe stato impossibile allegarlo interamente in tale progetto di tesi); è possibile estrapolarne l‟ elemento costitutivo fondamentale: baroque_ROM[NUMBER_OF_TIMBRES][COUPLED_VOICES][NUMBER_OF_VOICES] Si tratta di una matrice multidimensionale composta da strutture costanti di tipo “TRIP” le cui parti fondamentali sono individuate in un estratto, dal suo codice completo, visionabile di seguito: const TRIP baroque_ROM[NUMBER_OF_TIMBRES][COUPLED_VOICES][NUMBER_OF_VOICES] = { { { { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette0] /*unsigned char Sysexcode;*/ 155, /*unsigned char NomeVoce[LENGTH_OF_NAMES+1];*/ /*PrincipalBass16'", /*unsigned char orgPar t_exist;*/ 0, /*unsigned char cc32;*/ 4, /*unsigned char cc0;*/ 0, /*unsigned char pc;*/ 0, /*unsigned char volume;*/ 115, /*unsigned char filter;*/ 89, /*unsigned char micro_panpot;*/ 3, /*unsigned char Panpot;*/ 64, /*unsigned char voiceFamily;*/ 8, /*char PoliphonicPriority;*/ 1, /*unsigned char Env_Attack;*/ 64, /*unsigned char Env_Decay;*/ 64, /*unsigned char Env_Release;*/ 64, /*unsigned char Env_Delay;*/ 40, /*unsigned char RndTune_Probability;*/ 4, /*unsigned char RndTune_MaxCents;*/ 4, /*unsigned char pitch;*/ 59, /*unsigned char pipe;*/ 0, /*unsigned char outch;*/ 1, /*unsigned char Dummy2[7];*/ {0,0,0,0,0,0,0}, /*unsigned char ShortNomeVoce[9];*/ "Princ 16", /*unsigned char volume_nbn[128];*/ { 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 36 Capitolo 4 27 novembre 2008 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 }, /*unsigned char tone_nbn[128];*/ { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, /*unsigned char pitch_nbn[128];*/ { 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 } }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][ VoicePalette1] /*unsigned char Sysexcode;*/ 152, /*unsigned char NomeVoce[LENGTH_OF_NAMES+1];*/ ……………………………………………………………………………."Violon Bass 16' ", /*unsigned char orgPar t_exist;*/ 0, /*unsigned char cc32;*/ 4, /*unsigned char cc0;*/ 0, /*unsigned char pc;*/ 2, /*unsigned char volume;*/ 113, /*unsigned char filter;*/ 64, /*unsigned char micro_panpot;*/ 3, /*unsigned char Panpot;*/ 64, /*unsigned char voiceFamily;*/ 8, /*char PoliphonicPriority;*/ 1, /*unsigned char Env_Attack;*/ 64, /*unsigned char Env_Decay;*/ 64, /*unsigned char Env_Release;*/ 64, /*unsigned char Env_Delay;*/ 40, /*unsigned char RndTune_Probability;*/ 4, /*unsigned char RndTune_MaxCents;*/ 4, /*unsigned char pitch;*/ 64, /*unsigned char pipe;*/ 0, /*unsigned char outch;*/ 1, /*unsigned char Dummy2[7];*/ {0,0,0,0,0,0,0}, /*unsigned char ShortNomeVoce[9];*/ "Viol 16", /*unsigned char volume_nbn[128];*/ 37 Capitolo 4 27 novembre 2008 { 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 }, /*unsigned char tone_nbn[128];*/ { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, /*unsigned char pitch_nbn[128];*/ { 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 } }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette2] … /*unsigned char NomeVoce[LENGTH_OF_NAMES+1];*/ "Vuoto ", …. }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette3] … … }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette4] … … }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette5] … … }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette6] … 38 Capitolo 4 27 novembre 2008 … }, { //PEDAL >> Timbri_RAM[Tab0][Rank0][VoicePalette7] … … }, { { }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette1] …. }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette2] … }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette3] … } }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette4] … }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette5] … }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette6] … }, { //PEDAL >> Timbri_RAM[Tab0][Rank1][VoicePalette7] } } }, { { { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette0] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette1] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette2] … }, { 39 Capitolo 4 27 novembre 2008 //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette3] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette4] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette5] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette6] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank0][VoicePalette7] … } }, { { //PEDAL >> Timbri_RAM[Tab1][Rank1][VoicePalette0] … }, { //PEDAL >> Timbri_RAM[Tab1][Rank1][VoicePalette1] …. }, { … … … } } } Come si può facilmente evincere dal codice precedente, l‟ accesso alle voci risulta possibile mediante 3 indici fondamentali: NUMBER_OF_TIMBRES: identifica la voce di registro principale. COUPLED_VOICES: identifica il rank (livello di canne simulate). Vale 0 o 1. NUMBER_OF_VOICES: identifica la “sottovoce” (da 0 a 7) del timbro principale. L‟ organizzazione delle voci verifica quanto già anticipato in precedenza e messo in risalto dall‟ evidenziazione degli indici presenti nella successione 40 Capitolo 4 27 novembre 2008 delle voci. Ogni Tabx è datato di 2 livelli di “Rank” (Rank1 e Rank2), ciascuno dotato di 8 Voice Palette (VoicePalette0 – VoicePalette7). Importante anche la definizione della struttura tripletta (TRIP) relativa ad ogni singola voce. Tale struttura è ripetuta per ogni sottovoce e risulta definita nel file voicedata.h. typedef struct Tripletta { unsigned char Sysexcode; char NomeVoce[LENGTH_OF_NAMES+1]; unsigned char orgPar t_exist; unsigned char cc32; unsigned char cc0; unsigned char pc; unsigned char volume; unsigned char filter; unsigned char micro_panpot; unsigned char Panpot; unsigned char voiceFamily; char PoliphonicPriority; unsigned char Env_Attack; unsigned char Env_Decay; unsigned char Env_Release; unsigned char Env_Delay; unsigned char RndTune_Probability; unsigned char RndTune_MaxCents; unsigned char pitch; unsigned char pipe; unsigned char outch; unsigned char Dummy2[7]; char ShortNomeVoce[LENGTH_OF_SHORT_NAMES+1]; unsigned char volume_nbn[128]; unsigned char tone_nbn[128]; unsigned char pitch_nbn[128]; } TRIP; Questa “Tripletta” contiene tutti i parametri di cui l‟ utente può effettuarne l‟ editing. La maggior parte di essi sono definiti da un unico valore numerico assegnabile da un range di valori ben predefinito. Altri sono stringhe di testo editabili direttamente dall‟ utente con un numero massimo di caratteri fissato. Infine è possibile evidenziare la presenza di parametri caratterizzati da più valori memorizzati in vettori monodimensionali (es. tone_nbn[128]). Queste caratteristiche di diversificazione strutturale riscontrabili nella gamma dei parametri modificabili sono alla base delle scelte operate nella Graphic User Interface finale. 41 Capitolo 4 27 novembre 2008 Risulta logico come non tutti i parametri siano fisicamente implementabili all‟ interno di unica finestra, ma vi sia la necessità di posizionarli su più interfacce opportunamente interconnesse e accessibili agevolmente dall‟ utente. 4.3 – STANDARDIZZAZIONE MESSAGGIO MIDI Rispettando la struttura principale di un sistema esclusivo, già illustrata precedentemente, nella prima fase progettuale si è cercato di studiare la soluzione migliore per il corretto invio delle informazioni allo strumento elettronico fondamentali per l‟ accesso e la modifica dei parametri strutturanti una singola voce . Si è giunti al seguente modello compositivo del messaggio midi da impiegare per l‟ invio dei dati al Rodgers i538: F0 SYSEX INDICI VOCE OFFSET NUM ERO VA LORI MODIFICATI VA LORI MODIFICATI CK F7 Figura 4.2 – Strutturazione standardizzata del messaggio midi da inviare allo strumento elettronico per una corretta comunicazione. I parametri standard già illustrati nella panoramica sul midi sono, l‟ F0 , il sysexcode , il cecksum (CK) e l‟ F7 . Come terzo dato si è deciso di inserire i 3 indici della matrice che consentono l‟ accesso alla voce. Poi l‟ OFFSET relativo all‟ indirizzo di memoria del parametro modificato. Come quinto dato il nume ro dei valori modificati in quanto esistono parametri che hanno più di un valore editabile (ad esempio volume_nbn , pitch_nbn); ciò contribuisce ad una allocazione dell‟ opportuno spazio (in byte) in base al numero di dati effettivi da inviare. Infine prima del cecksum vengono inseriti i VALORI MODIFICATI (da un minimo di 1 ad un massimo di 128). Tale strutturazione riflette un costruzione dinamica del sistema esclusivo ottimizzando la comunicazione con l‟ hardware midi esterno. 42 Capitolo 4 27 novembre 2008 4.4 – PRESENTAZIONE DELL’ INTERFACCIA GRAFICA Col supporto dei progettisti e sviluppatori dell‟ azienda Roland si è cercato di analizzare quali fossero le soluzioni migliori nella creazione grafica della GUI definitiva. Si è deciso di plasmare un‟ interfaccia che sia in grado di selezionare univocamente una singola voce tra tutte quelle presenti sul pannello principale, consentendo successivamente la scelta dei vari livelli di “rank ” e “sottovoce” avvalendosi di appositi dispositivi di interfaccia. L‟elevato numero di parametri e la loro sostanziale diversificazione hanno indotto ad organizzare e presentare le funzionalità dell‟ applicazione su due finestre indipendenti una dall‟ altra e direttamente accessibili dalla interfaccia principale. Utilizzando uno stile di interazione per manipolazione diretta l‟ utente agisce sui vari oggetti nello schermo tramite l‟ utilizzo di tastiera e mouse. Il feedback dell„ interfaccia risulta rafforzato dalla presenza di elementi interagenti di tipo attivo e reattivo ovvero in grado di cambiare il loro aspetto e posizione a seconda dell‟ azione svolta dall‟ utente. Figura 4.2 – Finestra di dialogo principale da cui è possibile accedere alle 2 …………... finestre secondarie per l‟ editing dei parametri. Le immagini delle tre finestre di dialogo principali evidenziano un perfetto connubio tra funzionalità grafiche avanzate, stile di interazione semplice e 43 Capitolo 4 27 novembre 2008 sintesi delle possibilità di editig. L‟ accesso ai parametri di una singola voce risulta notevolmente semplificato e velocizzato rispetto agli innumerevoli passaggi di sottomenù occorrenti nello strumento reale. Le funzionalità grafiche e i dispositivi d‟ interfaccia offerti dalla perfetta integrazione tra MFC e ambiente di sviluppo Visual C++ hanno contribuito alla creazione di un aspetto estetico attraente che si avvicina molto alle sembianze dello strumento reale, agevolandone la comprensione e l‟ utilizzo. La prima dialog si avvicina molto alle forme del pannello anteriore reale del Rodgers i538 disponendo verticalmente le 3 sezioni di voci principali; i pulsanti del pannello sono oggetti di tipo reattivo che evidenziano la voce attualmente selezionata dall‟ utente con un colore rosso. Nella parte destra della dialog principale si possono notare 3 dispo sitivi di interfaccia di tipo combo box; i primi 2 offrono la possibilità di selezione i vari livelli di “rank” e “voci secondarie”. Il terzo consente la scelta del dispositivo su cui inviare il messaggio midi. Le 26 tipologie di parametri editabili dall‟ utente appaiono facilmente visualizzabili nelle 2 dialog figlie direttamente accessibili (dalla finestra principale) per mezzo dei pulsanti EDIT_PAR_NOTA ed EDIT_PAR_VOCE. La disposizione ottimizzata di vari elementi attivi ed inerti consente l‟ adozione di efficaci soluzioni grafiche garantendo una ottima tolleranza agli errori ed una visione globale dei parametri; essi risultano individuabili per mezzo di un nome ed uno o più valori numerici rispettivamente raffigurati tramite un‟ etichetta e uno o più editbox. Tutto ciò è stato possibile con l‟ utilizzo di opportuni costrutti di programmazione software e grafica (tramite appositi editor grafici) le cui potenzialità risultano evidenziabili soprattutto nella seconda dialog di editing (Figura 4.2 ). La necessità di modificare 3 parametri di tipo vettore caratterizzati da ben 128 elementi ciascuno ha contribuito all‟ ideazione di una doppia tastiera virtuale costituita da 128 note totali. Adottando altrettanti dispositivi di interfaccia di tipo bottone appare possibile visualizzare i valori dei 3 parametri relativi alla nota selezionata dall‟ utente. 44 Capitolo 4 27 novembre 2008 La modifica numerica di tutti i parametri risulta effettuabile con un elemento d‟ interfaccia reattivo di tipo slider (figura 3.3) implementato all‟ interno di un‟ apposita dialog richiamabile dai pulsanti >EDITA< presenti nelle due interfacce di editing. Figura 4.3 – Dispositivo di interfaccia di tipo slider per la modifica del valore numerico; essa risulta richiamabile col pulsante >EDITA< dalle 2 finestre di editing. Lo slider inoltre garantisce un „ottimo supporto alla tolleranza verso gli errori dell‟ utente segnalando visualmente i valori massimo e minimo assumibili dal parametro selezionato. Ogni finestra (tranne quella principale) ha un‟ apposita funzione d‟ invio delle modifiche effettuate con l‟ “impacchettamento” e la trasmissione delle informazioni tramite protocollo midi; cliccando sull‟ elemento reattivo SEND SYSEX l‟ utente è in grado di comunicare all‟ hardware esterno le variazioni apportate alla voce in editing. La finestra di dialogo principale consente l‟ accesso ad una ulteriore interfaccia le cui funzionalità sono totalmente indipendenti dalle caratteristiche di editor grafico analizzate finora. Il pulsante SELEZIONE VOCI MULTIPLE effettua l‟ apertura di una dialog per la selezione multitimbrica dell‟ intero elenco di voci (APPENDICE D) caratterizzanti il Rodgers i538. Per tale scopo si è ritenuta opportuna l‟ implementazione di un particolare dispositivo d‟ interfaccia reattivo che consentisse una veloce visualizzazione e multi selezione delle 244 voci totali. Le potenzialità delle MFC hanno permesso l‟ ideazione di una lista di elementi reattivi fruibile rapidamente con una scroll bar laterale. 45 Capitolo 4 27 novembre 2008 Figura 4.3 – Finestra di dialogo che consente la selezione multi timbrica delle 244 voci. Ciascun bottone è caratterizzato da un nome della voce ad esso relazionata e da un led laterale. L‟ interazione con l‟ utente è attuabile con lo spegnimento o l’ accensione del led corrispondente all‟ elemento cliccato ottenendo un risultato esteticamente gradevole e funzionalmente comodo. L‟ interfaccia opera anche l‟ invio del messaggio midi ad ogni azione dell‟ utente, attivando o disattivando il bit relativo all‟ ultima voce selezionata. 46 Capitolo 5 27 novembre 2008 5. - COSTRUZIONE INTERFACCIA GRAFICA 5.1 – PANORAMICA DAL RESOURCE VIEW Il visualizzatore di risorse presente nell‟ ambiente di sviluppo Visual C++ mette in evidenza le 5 finestre di dialogo costituenti l‟ interfaccia grafica ideata individuabili da un proprio nome di progetto (IDD_nome_della _dialog). Figura 5.1 – Elenco delle 5 Dialog di progetto visualizzabili dal Developer Studio. Nella tabella seguente ne vengono descritte le singole caratteristiche funzionali ed estetiche. NOME_DIALOG Descrizione IDD_ DIALOG_ NOTA Finestra relat iva all‟ ed iting dei parametri dei 3 parametri di tipo vettore modificali tramite l‟ imp lementazione grafica d i una tastiera virtuale costituita da 128 tasti musicali. IDD_ DIALOG_PAR Finestra relativa a ll‟ editing di 23 parametri indiv iduabili per mezzo d i un nome ed uno o più valori nu merici rispettivamente raffigurati tramite un‟ etichetta e uno o più edit-box. IDD_ DIALOG_SLIDER Finestra relat iva al dispositivo di interfaccia di t ipo slider per la modifica del valore numerico; essa risulta rich iamabile col pulsante >EDITA< dalle 2 dialog precedenti. 47 Capitolo 5 27 novembre 2008 IDD_ RODGERS_i385_GUI_ DIALOG IDD_S ELEZIONE_ VOCI_ MULTIPLE Finestra principale dell‟ interfaccia che opera l‟ accesso alle voci dello strumento e successivamente a tutte le altre dialog di editing e multi selezione. Il suo aspetto grafico ricrea le semb ianze del pannello principale dell‟ o rgano liturgico favorendo l‟ interazione con l‟ utente. Finestra relativa alla lista di bottoni che consente una veloce visualizzazione e mu lti selezione delle 244 voci totali. Le voci attive vengono individuate dalla accensione del corrispondente led. Di seguito vengono riportate la lista dei file .cpp e .h sviluppati nel Visual C++ e l‟ elenco delle immagini utilizzate nelle creazioni grafiche dell‟ intera interfaccia. Figura 5.2 – Lista dei file .cpp e .h sviluppati nel Visual C++ e l‟ elenco .delle immagini utilizzate nelle creazioni grafiche dell‟ intera interfaccia. 48 Capitolo 5 27 novembre 2008 5.2 – DIALOG PRINCIPALE : IDD_RODGERS_i385_GUI_DIALOG Il codice C++ per la costruzione della finestra di dialogo principale risulta definito nel file RODGER_i385_GUIDlg.cpp; ad essa è stato assegnato il nome identificativo: IDD_RODGERS_i385_GUI_DIALOG. Dal editor grafico del Visual C++ è possibile visionarne la composizione individuando i diversi dispositivi d‟ interfaccia integrati tramite l‟ apposito GUI designer tipico dell„ ambiente di sviluppo usato. Figura 5.3 – Composizione strutturale della dialog principale con l‟ utilizzo di ………………...elementi attivi e reattivi d‟ interfaccia. Compaiono: 26 Pulsanti “ON” che consentono la selezione della rispettiva voce 2 Combo_Box che permettono la selezione del Rank e della Voice Palette 1 Combo_Box realitava alla selezione del Device_Midi 2 Pulsanti che aprono le 2 Dialog di visualizzaione dei parametri editabili. 1 Pulsante che opera l‟ accesso alla dialog di multi selezione (senza editing) di più voci. 49 Capitolo 5 27 novembre 2008 Prima di poter effettivamente sfruttare tali elementi attivi e reattivi d‟ interfaccia per l‟ editing della voce desiderata, appare evidente la necessità di un accesso diretto alla struttura delle voci (residente nel file baroque.cpp); in un calcolatore ciò risulta possibile operando il caricamento dei dati da modificare in RAM. Ciò risulta svolto opportunamente attraverso la funzione CaricaBaroqueRom() in cui avviene l' inizializzazione dei dati in Ram tramite una matrice di strutture costanti (baroque_ROM). All‟ intero del file risulta dichiarata globalmente una struttura TRIP del tipo: g_dati_RAM[NUMBER_OF_TIMBRES][COUPLED_VOIC ES][NUMBER_OF_VOICES]; In seguito nella CRODGERS_i385_GUIDlg::OnInitDialog() avviene la inizializzazione della funzione CaricaBaroqueRom() caratterizzata dal seguente codice: void CRODGERS_i385_GUIDlg::CaricaBaroqueRom() { int i , j, k ; for (i=0 ; i<=NUMBER_OF_TIMBRES; i++) { for (j=0 ; j<=COUPLED_VOIC ES; j++) { for (k=0 ; k<=NUMBER_OF_VOIC ES; k++) { g_dati_RAM[i][j][k] = baroque_ROM[i][j][k]; } } } return; } Le voci principali (ognuna corrispondente ad un pulsante del pannello frontale del RODGERS) sono 26 suddivise in 3 sezioni : PEDAL MANUAL I MANUAL II Per tale motivo si è scelto di ricreare un‟ interfaccia simile a quella del pannello frontale posizionando 26 pulsanti suddivisi su 3 livelli in base al gruppo di appartenenza. Tali elementi vengono dichiarati nel RODGERS_i385_GUIDlg.h 50 Capitolo 5 27 novembre 2008 come elemetni reattivi di tipo CBitmapButton in quanto permettono il caricamento di immagini sulla superficie del bottone. CBitmapButton … … CBitmapButton m_Button_ON1; m_Button_ON26; La gestione dell‟ evento di Click tramite mouse su ogni singolo pulsante è stata possibile con l‟ utilizzo del ClassWizard, associando ad ogni tasto il tipo di messaggio ON_BN_CLICKED secondo la seguente mappatura: ON_BN_CLICKED(IDC_ON1, OnOn1) … … ON_BN_CLICKED(IDC_ON26, OnOn26) L‟ evento di click su un tasto (del pannello virtuale) richiama automaticamente la corrispondete funzione membro OnOn1 … OnOn26; di seguito il codice c++ corrispondente (al primo elemento attivo di tipo pulsante): void CRODGERS_i385_GUIDlg::OnOn1() { ModificaTab(0); timbre = 0; m_cEdButtonParVoce.EnableWindow(TRUE); m_cEdButtonParNota.EnableWindow(TRUE); } Tale funzione membro attiva la chiamata della ModificaTab() passando come parametro il numero identificativo del pulsante selezionato (progressivamente da 0 a 25); in essa inoltre si evidenzia un‟ operazione fondamentale per il collegamento parametrico delle varie dialog dell‟ applicativo. Compare, infatti, l‟ assegnazione dell‟ ID del pulsante cliccato alla variabile globale timbre ovvero una delle 26 voci di registro principali; si è quindi ottenuto il primo indice della matrice delle voci. I pulsanti d‟ accesso alle 2 dialog di editing risultano disabilitati ( ovvero si trasformano da elementi inerti a elementi attivi) fino a quando l‟ utente non effettua la selezione della voce principale. Tale abilitazione dinamica è attivabile 51 Capitolo 5 27 novembre 2008 implementando la funzione EnableWindow(TRUE) nella variabile membro associata ai 2 bottoni. Ciò è riscontrabile in tutte le 26 funzioni membro collegate alle voci principali evincibile dal seguente codice di programmazione : void CRODGERS_i385_GUIDlg::OnOn2() { ModificaTab(1); timbre = 1; m_cEdButtonParVoce.EnableWindow(TRUE); m_cEdButtonParNota.EnableWindow(TRUE); } … … void CRODGERS_i385_GUIDlg::OnOn26() { ModificaTab(25); timbre = 25; m_cEdButtonParVoce.EnableWindow(TRUE); m_cEdButtonParNota.EnableWindow(TRUE); } 52 Capitolo 5 27 novembre 2008 5.2.1 – DESCRIZIONE DELLA FUNZIONE: MODIFICATAB( ) Codice relativo alla prima parte della funzione: void CRODGERS_i385_GUIDlg::ModificaTab(int tab) { int i; int curr_rank=0; CString str; str.Empty(); m_cCmbRank.ResetContent(); m_cCmbPalette.ResetContent(); str.Format(_T(g_dati_RAM[tab][1][0].NomeVoce)); if( str!="Vuoto ") { curr_rank=1; } Tale funzione membro innanzitutto resetta il contenuto delle ComboBox relative al Rank e alla Voice Palette; successivamente verifica se la voce principale (corrispondente all‟ indice intero “tab” passato come parametro) è dotata anche del Rank 1 (ovvero un secondo livello di Rank), altrimenti viene rilevato un unico valore selezionabile (nella combo corrispondente) di Rank 0. Per far ciò viene verificato se il NomeVoce del primo elemento (0) del livello di rank [1] nel registro principale selezionato [tab] sia una stringa corrispondente a “Vuoto” (di conseguenza per costruzione anche tutte le successive sono Vuote). Se non si verifica tale condizione allora risulta settato ad 1 il valore del current_rank attuale. for ( i=0 ; i<=curr_rank; i { str.Format(_T("rank % d"),i); m_cCmbRank.AddString(str); } m_cCmbRank.SetCurSel(0); for ( i=0 ; i<NUMBER_OF_VOICES ; i { str.Format("% s",_T(g_dati_RAM[tab][0][i].NomeVoce)); if( str!="Vuoto ") { m_cCmbPalette.AddString(str); } } m_cCmbPalette.SetCurSel(0); } 53 Capitolo 5 27 novembre 2008 Nella seconda parte vengono riempite le combo box Rank e VoicePalette; la prima considerando se la voce è dotata anche del rank 1 e la seconda (voice palette) col campo NomeVoce fino a quando non viene rilevata una stringa vuota. 5.2.2 – ELEMENTI COMBO BOX: RANK E VOICE PALETTE void CRODGERS_i385_GUIDlg::OnSelchangeComboRank() { int i; int selection = m_cCmbRank.GetCurSel(); coupled = selection; Il metodo OnSelchange relativo alla combo box permette di intercettare l’ evento di selezione di un elemento (all‟ interno del menù a tendina della combo) da parte dell‟ utente. La funzione GetCurSel() permette di memorizzare la selezione all‟ interno della varibile selection successivamente assegnata alla variabile globale coupled (rappresenta il secondo indice della matrice g_dati_ram[][][]). m_cCmbPalette.ResetContent(); for ( i=0 ; i<NUMBER_OF_VOICES ; i++) { str.Format("% s",_T(g_dati_RAM[timbre][selection][i].NomeVoce)); if( str!="Vuoto ") { m_cCmbPalette.AddString(str); } } m_cCmbPalette.SetCurSel(0); La selezione del Rank provoca anche il cambiamento degli elementi della Combo Voice Palette; dopo il suo reset tale combo viene riempita con gli elementi NomeVoce in base alla variabile globale timbre e alla selection (rank selezionato) con un indice i di scorrimento. Alla fine viene settato di default il primo elemento. void CRODGERS_i385_GUIDlg::OnSelchangeComboPalette { int selection = m_cCmbPalette.GetCurSel(); voice = selection; 54 Capitolo 5 27 novembre 2008 } La selezione di un elemento all‟ interno della combo Voice Palette permette la memorizzazione di tale valore nella variabile globale voice (terzo indice della matrice voci). 5.2.3 – ELEMENTO COMBO BOX: MIDI DEVICE Nella funzione membro ::OnInitDialog() avviene la definizione della combo box relativa alla selezione del MIDI DEVICE, ovvero della periferica midi da utilizzare per operare una corretta comunicazione tra l‟ interfaccia grafica e lo strumento elettronico esterno collegato. m_cCmbMidiDevice.ResetContent(); int num_dev = midiOutGetNumDevs(); for (int i=0 ; i<=num_dev; i++) { str.Format(_T("Midi Device % d"), i); m_cCmbMidiDevice.AddString(str); } m_cCmbMidiDevice.SetCurSel(0); Le librerie di sistena windows.h e mmsystem.h permettono di utilizzare delle apposite funzioni per la gestione dei messaggi e delle periferiche midi. In questo caso si è utilizzata la funzione midiOutGetNumDevs() che restituisce come valore di ritorno il numero di periferiche midi di Output presenti nel sistema e viene assegnato alla variabile num_dev. La combo risulta riempita tramite un ciclo for creando la visualizzazione di un elenco indicante MidiDevice 0 , MidiDevice 1 , … . int selection = m_cCmbMidiDevice.GetCurSel(); midi_device = selection; La selezione di un midi device viene assegnata alla variabile globale midi_device. 55 Capitolo 5 27 novembre 2008 5.2.4 – ELEMENTI GRAFICI La possibilità di caricare immagini di background e sulla superficie dei vari pulsanti permette di dare una visione molto più gradevole e precisa alla dialog principale ricreando l‟ aspetto del pannello principale dello strumento elettronico reale. Figura 5.4 – Composizione strutturale della dialog principale con l‟ utilizzo di elementi attivi e reattivi d‟ interfaccia sfruttando le possibilità di integrazione grafica tramite il caricamento di immagini di background e sulla superfice dei vari oggetti d‟ interfaccia. L‟ implementazione della bitmap di background nell‟ intera dialog è effettuata tramite il metodo OnPaint caratterizzato dal seguente codice: void CRODGERS_i385_GUIDlg:: OnPaint() { CPaintDC dc( this); CBitmap bitmap; CDC memdc; bitmap.LoadBitmap( IDB_BACK_SEL_VOCE ); BITMAP bmp; bitmap.GetBitmap( &bmp ); memdc.CreateCompatibleDC( &dc ); memdc.SelectObject( &bitmap ); CRect rect; GetClientRect(rect); 56 Capitolo 5 27 novembre 2008 StretchBlt (dc.m_hDC,0, 0, rect.r ight, rect.bottom, memdc.m_hDC, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY ); } Si parte dalla definizione di una variabile dc di tipo CPaintDC ovvero di tipo device context e viene inizializzata tramite un puntatore all‟ oggetto corrente (this). La classe CPaintDC richiama automaticamente le funzioni API necessarie affinchè ogni applicazione Windows possa disegnare un qualsiasi elemento grafico. La classe CBitmap permette un controllo completo su una bitmap. Si definisce lo oggetto bitmap su cui carichiamo l‟ immagine desiderata (esplicitando il nome del suo ID assegnato nelle risorse del developer studio). Si crea poi un secondo device context di tipo CDC su cui caricare la bitmap, che viene reso compatibile col device context principale; successivamente tramite la funzione SelectObject() si seleziona la bitmap nel nuovo DC. Successivamente, dopo aver determinato l‟ area disponibile (la quale assume la forma di un rettangolo), l‟ immagine viene copiata dal secondo context a quello principale attraverso la funzione StretchBlt che ne consente anche un ridimensionamento. Per quanto riguarda, invece, il caricamento di bitmap sulla superficie dei pulsanti per la selezione delle voci principali, si è sfruttata la proprietà Owne r Draw e i messaggi per gli eventi del mouse (click, rilascio … ) definiti dai metodi di Windows: WM_LBUTTONDOWN e WM_LBUTTONUP. m_Button_ON1.LoadBitmaps(IDB_UP, IDB_DOWN , IDB_FOCUS, ID B_DISABLE); … … m_Button_ON26.LoadBitmaps(IDB_UP, IDB_DOWN , IDB_FOCUS,IDB_DISABLE); Come si può evincere dal codice la variabile di controllo associata ad ogni pulsante (m_Button_ONx) richiama il metodo LoadBitmaps(); quest‟ ultima funzione permette di caricare 4 immagini tramite il loro ID. Nel nostro caso sono: 57 Capitolo 5 27 novembre 2008 IDB_UP IDB_DOWN IDB_FOCUS IDB_DISABLE (bianco) (bianco) Pulsante non cliccato Pulsante in fase di click (schiacciato) Pulsante rilasciato (effettuata la selezione) Pulsante disabilitato Evidente come quello più importante (nel nostro caso) sia il FOCUS che permette di mantenere evidenziata la voce di registro principale selezionata (mentre gli altri 25 pulsanti restano di default bianchi). 5.2.5 – PULSANTI EDIT_PAR_VOCE ED EDIT_PAR_NOTA void CRODGERS_i385_GUIDlg::OnButtonEditParVoce() { CEditParDlg dlg; dlg.DoModal(); return; } void CRODGERS_i385_GUIDlg::OnButtonEditParNota() { CEditParNotaDlg dlg; dlg.DoModal(); return; } I 2 metodi associati al click di tali pulsanti aprono le 2 dialog relative all‟ editing di tutti i parametri della voce selezionata; segue il codice relativo anche al caricamento delle apposite bitmaps sulla lora superficie grafica. m_cEdButtonParVoce.LoadBitmaps(IDB_EDIT_VOCE_UP, IDB_EDIT_VOCE_DOWN , IDB_EDIT_VOCE_UP, IDB_EDIT_NOTA_DISABLE); … … m_cEdButtonParNota.LoadBitmaps(IDB_EDIT_NOTA_UP, IDB_EDIT_NOTA_DOWN , IDB_EDIT_NOTA_UP, IDB_EDIT_NOTA_DISABLE); 58 Capitolo 5 27 novembre 2008 5.3 – DIALOG EDIT PARAMETRI VOCE: IDD_DIALOG_PAR Figura 5.4 – Composizione strutturale della dialog per la modifica di 23 parametri. 21 sono tipo numerico ed editabili tramite lelemento d‟ interfaccia >EDITA<. Gli altri 2 sono stringhe di testo modificabili direttamente dal dispositivo di input di tipo tastiera. Lo stile di interazione a manipolazione diretta consente all‟ utente, in tale finestra, di effettuare l‟ editing su 23 parametri caratteristi della voce selezionata nell‟ interfaccia principale basandosi sui 3 indici della matrice voci g_dati_Ram[][][]. La “costruzione” di tale finestra utente è implementata nei file EditParDlg.cpp e EditParDlg.h. Ad ogni parametro risulta associato un elemento inerte di tipo editbox (casella di testo) visualizzante il valore corrispondente. Nell maggior parte dei casi si tratta di un valore numerico , mentre i 2 parametri “NOME VOCE” e “SHORT NOME VOCE” sono costituiti da una stringa di testo di lunghezza ben definita (è possibile l‟ inserimento di un numero massimo di caratteri). Altra eccezione è rappresentata dal parametro “DUMMY[7]” che, essendo un vettore, richiede la visualizzazione dei sui 7 elementi su altrettante caselle di testo. CString CString CString CString CString CString m_cEdNomeVoce; m_cEdSysexCode; m_cEdOrgPart_Exist; m_cEdControlChange32; m_cEdControlChange0; m_cEdProgramChange; 59 Capitolo 5 27 novembre 2008 CString CString CString CString CString CString CString CString CString CString CString CString CString CString CString CString … CString CString m_cEdVolume; m_cEdFilter; m_cEdMicroPanpot; m_cEdPanpot; m_cEdVoiceFamily; m_cEdPoliphonicPriority; m_cEdEnvAttack; m_cEdEnvDecay; m_cEdEnvRelease; m_cEdEnvDelay; m_cEdRandomTuneProbability; m_cEdRandomTuneMaxCents; m_cEdPitch; m_cEdPipe; m_cEdOutch; m_cEdDummy0; m_cEdDummy6; m_cEdShortNomeVoce; Come si può evincere dal codice estratto dal file header corrispondente, ad ogni parametro è assegnata una variabile (membro) di controllo di tipo CString. Per i 2 parametri stringa l‟ editing viene effettuato direttamente immettendo o cambiando caratteri da tastiera. Per tutti gli altri, che assumono principalmente valore numerico da 0 a 128 , la modifica può essere effettuata cliccando sul corrispondente pulsante “EDITA”, attraverso l‟ apertura di un‟ apposita finestra di editing (le cui caratteristiche verranno specificate in seguito). Ogni bottone di edit è stato identificato con un opportuno ID evidenziando in modo univoco il parametro a cui risulta associato. Con il ClassWizard, successivamente, ad ogni tasto è stato associato il tipo di messaggio ON_BN_CLICKED secondo la seguente mappatura: ON_BN_CLICKED(IDC_BUTTON_SYSEX, OnButtonSysex) ON_BN_CLICKED(IDC_BUTTON_ORGPARTEX, OnButtonOrgpar tex) ON_BN_CLICKED(IDC_BUTTON_CC32, OnButtonCc32) ON_BN_CLICKED(IDC_BUTTON_CC0, OnButtonCc0) ON_BN_CLICKED(IDC_BUTTON_PC, OnButtonPc) ON_BN_CLICKED(IDC_BUTTON_VOLUME, OnButtonVolume) ON_BN_CLICKED(IDC_BUTTON_FILTER, OnButtonFilter) ON_BN_CLICKED(IDC_BUTTON_MICRO_PANPOT, OnButtonMicroPanpot) ON_BN_CLICKED(IDC_BUTTON_PANPOT, OnButtonPanpot) ON_BN_CLICKED(IDC_BUTTON_VOICE_FAMILY, OnButtonVoiceFamily) ON_BN_CLICKED(IDC_BUTTON_POLIPHONIC_PRIORITY, OnButtonPoliphonicPriority) ON_BN_CLICKED(IDC_BUTTON_ENV_ATTACK, OnButtonEnvAttack) ON_BN_CLICKED(IDC_BUTTON_ENV_DECAY, OnButtonEnvDecay) ON_BN_CLICKED(IDC_BUTTON_MICRO_ENV_RELEASE, OnButtonMicroEnvRelease) ON_BN_CLICKED(IDC_BUTTON_ENV_DELAY, OnButtonEnvDelay) ON_BN_CLICKED(IDC_BUTTON_RANDOM_TUNE_PROB, OnButtonRandomTuneProb) 60 Capitolo 5 27 novembre 2008 ON_BN_CLICKED(IDC_BUTTON_RANDOM_TUNE_MAXCENTS, OnButtonRandomTunMaxcents) ON_BN_CLICKED(IDC_BUTTON_PITCH, On ButtonPitch) ON_BN_CLICKED(IDC_BUTTON_PIPE, OnButtonPipe) ON_BN_CLICKED(IDC_BUTTON_OUTCH, OnButtonOutch) ON_BN_CLICKED(IDC_BUTTON_DUMMY_0, OnButtonDummy0) … ON_BN_CLICKED(IDC_BUTTON_DUMMY_6, OnButtonDummy6) 5.3.1 – SPECIFICHE: ONINITDIALOG( ) BOOL CEditParDlg::OnInitDialog() { CDialog::OnInitDialog(); m_cEdSysexCode.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].Sysexcode)); m_cEdNomeVoce.For mat("% s",_T(g_dati_RAM[timbre][coupled][voice].NomeVoce)); m_cEdOrgPart_Exist.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].orgPar t_exist)); m_cEdControlChange32.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].cc32)); m_cEdControlChange0.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].cc0)); m_cEdProgramChange.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].pc)); m_cEdVolume.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].volume)); m_cEdFilter.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].filter)); m_cEdMicroPanpot.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].micro_panpot)); m_cEdPanpot.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].Panpot)); m_cEdVoiceFamily.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].voiceFamily)); m_cEdPoliphonicPriority.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].PoliphonicPriority )); m_cEdEnvAttack.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].Env_Attack)); m_cEdEnvDecay.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].Env_Decay)); m_cEdEnvRelease.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].Env_Release)); m_cEdEnvDelay.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].Env_Delay)); m_cEdRandomTuneProbability.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice]. RndTune_Probability)); m_cEdRandomTuneMaxCents.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice]. RndTune_MaxCents)); m_cEdPitch.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].pitch)); m_cEdPipe.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].pipe)); m_cEdOutch.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].outch)); m_cEdDummy0.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].Dummy2[0])); … m_cEdDummy6.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].Dummy2[6])); m_cEdShortNomeVoce.For mat("% s",_T(g_dati_RAM[timbre][coupled][voice].Shor tNomeVoce) UpdateData(FALSE); return TRUE; } La fase di inzializzazione della Dialog richiede il corretto caricamento dei valori nelle caselle di testo relativamente a tutti i parametri visualizzati. Tale operazione è possibile tramite le 3 variabili globali (timbre, coupled, voice) memorizzate attraverso la Dialog principale, e che operano l‟ accesso nella matrice generica di 61 Capitolo 5 27 novembre 2008 voci (g_dati_RAM) ai campi corrispondenti. Il valore correlato ad ogni parametro viene formattato, memorizzato nella rispettiva variabile di controllo e visualizzato nella casella di testo dell‟ interfaccia grafica. 5.3.2 – MODIFICA DEI PARAMETRI Ad ogni parametro viene associata la corrispondente funzione membro (secondo il codice precedentemente illustrato) con cui realizzare l‟ editing del suo valore. Di seguito il codice relativo alle prime 2 funzioni membro presenti nel file .cpp di sviluppo in quanto le successive hanno tutte l‟ identica struttura. void CEditParDlg::OnButtonSysex() { CSLIDERDlg dlg; min_range = 0; max_range = 255; p = &g_dati_RAM[timbre][coupled][voice].Sysexcode; if(dlg.DoModal() == ID_BUTTON_SLIDER_MODIFICA) { m_cEdSysexCode.For mat("% ld", *p); UpdateData(FALSE); } return; } void CEditParDlg::OnButtonOrgpartex() { CSLIDERDlg dlg; min_range = 0; max_range = 127; p = &g_dati_RAM[timbre][coupled][voice].orgPar t_exist; if(dlg.DoModal() == ID_BUTTON_SLIDER_MODIFICA) { m_cEdOrgPart_Exist.For mat("% ld", *p); UpdateData(FALSE); } return; } Nella funzione membro viene aperta una istanza della CSLIDERDlg (dialog per l' editing del parametro selezionato), successivamente si fissano i range massimo e minimo nelle rispettive variabili globali necessarie per l' utente e per la funzione 62 Capitolo 5 27 novembre 2008 opportuna affinchè si possa fissare (in fase di editing) un corretto valore tra quelli assumibili dal parametro stesso. Importante anche l' assegnazione dell' indirizzo del campo relativo alla quantità in editing alla varibile globale p; tale puntatore passato alla dialog CSLIDERDlg si caratterizza come l' unico modo per identificare e modificare correttamente il campo della matrice g_dati_RAM[][][] contenente il valore da editare. Il codice nella parte finale permette di aggiornare il valore della varibile di controllo (e quindi del parametro) quando l' utente decide di confermare la modifica effettuata nell' apposita Dialog (CSLIDERDlg) per cui, tramite il "puntatore globale" p, si può facilmente aggiornarne il valore nella finestra di origine (che altrimenti, senza tale operazione, avrebbe visualizzati i "vecchi" valori senza update). 5.3.3 – INVIO MESSAGGIO MIDI Tutto ciò che riguarda la costruzione, l‟ impiego delle idonee API di windows per la corretta organizzazione del messaggio e per l‟ utilizzo del Device selezionato, e quindi l‟ invio midi finale è contenuto nella funzione membro : void CEditParDlg::MakeSysex (unsigned char *pValue , int dim) Il midi exclusive message deve rispettare lo standard Roland sviluppato e specificato nelle prime fasi dell‟ intero progetto secondo la seguente struttura (già illustrata): F0 SYSEX INDICI VOCE OFFSET NUM ERO VA LORI MODIFICATI VA LORI MODIFICATI CK F7 Essa risulta costituita da una serie di byte opportunamente “impacchettati” su un‟ idonea disposizione di base possibile tramite la dichiarazione del seguente Array: unsigned char midi_array[300]; costituito da 300 elementi di tipo unsigned char (1 byte). Analizziamo ora più approfonditamente la parte di codice riguardante la MakeSysex. 63 Capitolo 5 27 novembre 2008 void CEditParDlg::MakeSysex (unsigned char *pValue , int dim) { Tale funzione riceve come parametro l‟ indizzo (*pValue) della quantità in editing e il numero (in byte) dim di valori assumibili. int i , sum ,ck ,w ; short offset; unsigned char midi_array[300]; int midipor t; HMIDIOUT device; Tra le dichiarazioni di variabili interne alla funzione membro spiccano: la variale device di tipo HMIDIOUT che identifica l‟ interfaccia midi device per i messaggi midi in uscita la variabile midiport che seleziona la porta midi di uscita da aprire e quindi da utilizzare offset = pValue - &g_dati_RAM[timbre][coupled][voice].Sysexcode; Nei 2 byte costituenti la varibile offset viene memorizzata la differenza di indirizzo di memoria tra il puntatore al valore del parametro corrente (pValue) e il primo elemento della struttura della voce in editing (Sysexcode). Ad esempio se si sta editando il parametro g_dati_RAM[21][0][4]. filter la variabile offset assumerà il seguente valore: &g_dati_RAM[21][0][4].filter - &g_dati_RAM[21][0][4].Sysexcode; ovvero la differenza di indirizzo tra il parametro filter e il primo parametro della stessa voce di registro. //MESSAGGIO MIDI midi_array[0] = 0xF0; midi_array[1] = 0x41; midi_array[2] = 0x10; midi_array[3] = 0x30; midi_array[4] = 0x12; midi_array[5] = timbre; midi_array[6] = coupled; midi_array[7] = voice; Nella costruzione del messaggio midi_array[] viene specificato il valore esadecimale da assegnare ad ogni byte partendo dall‟ F0 (inizio messaggio 64 Capitolo 5 27 novembre 2008 esclusivo) proseguendo con i successivi byte dall‟ elemento 1 al 4 che caratterizzano il messaggio esclusivo. Nei bytes 5-7 vengono disposti rispettivamente gli indici di accesso alla matrice delle voci principali. Da sottolineare il fatto che nei messaggi esclusivi gli unici bytes che possano assumere come bit significativo 1 sono esclusivamente il primo e l‟ ultimo (F0 F7) all‟ interno del Midi Exclusive Message. In tutti gli altri possiamo avere al massimo una configurazione di bit del tipo 01111111 corrispondete ad un range di valori compreso tra 0 e 127. Come verrà in seguito illustrato si dovranno implementare delle opportune operazioni su bit per poter trattare valori superiori a 127. #ifdef INTEL_TYPE midi_array[11] = (char) (offset & 0x000F) ; midi_array[10] = (char) ((offset & 0x00F0) >> 4); midi_array[9] = (char) ((offset & 0x0F00) >> 8); midi_array[8] = (char) ((offset & 0xF000) >> 12); #else midi_array[8] = (char) (offset & 0x000F) ; midi_array[9] = (char) ((offset & 0x00F0) >> 4); midi_array[10] = (char) ((offset & 0x0F00) >> 8); midi_array[11] = (char) ((offset & 0xF000) >> 12); #endif Il parametro di offset valorizzato su 2 byte viene splittato opportunamente con nibble (unità di misura per indicare quattro bit) su 4 byte. I primi 4 bit più significativi vengono splittati a destra di 12 bit ed assegnanti al byte 8. Le stesse operazioni di “and” logico e “splittin”g di bit vengono impiegate per i successivi 3 nibble. Come sappiamo le operazioni di immagazzinamento in memoria dei dati superiori al byte vengono interpretate in modo diverso a seconda del calcolatore utilizzato. E‟ questa la cosiddetta problematica Big-endian e little-endian : Big-endian è la memorizzazione che inizia dal byte più significativo per finire col meno significativo; è utilizzata dai processori Motorola Little-endian è la memorizzazione che inizia dal byte meno significativo per finire col più significativo; è utilizzata dai processori Intel 65 Capitolo 5 27 novembre 2008 Nel nostro caso tramite la #define INTEL_TYPE e il costrutto #ifdef - #else si è potuta interpretare correttamente tale problematica operando prima una memorizzazione dal byte più significativo e nel secondo caso da quello meno significativo. midi_array[12] = dim; for (i=0; i<=dim-1; i++) { midi_array[13+i] = *pValue++; } In base al numero di valori (dim) da inviare tramite il messaggio midi vengono riempiti gli elementi dell‟ array successivi al 12, attraverso il ciclo for che assegna ogni valore successivo, sfruttando il puntatore al primo elemento incrementato di 1 ad ogni iterazione. um = 0; for (w=5 ; w <=13 + (dim-1) ; w++) { sum += midi_array[w] ; } ck = (128 - sum) & 0x7F; midi_array[13+dim] = ck; midi_array[13+dim+1] = 0xF7; Nella parte finale del messaggio midi viene calcolato il checksum ovvero una somma di controllo. Si tratta di una sequenza di bit che risulta utilizzata per verificare l'integrità del messaggio sommando tutti i byte dall‟ elemento subito dopo i bytes che identificano l‟ exclusive message. fino a quello che identifica l‟ ultimo dei valori assumibili dal parametro. Il checksum risulta memorizzato nel penultimo elemento prima dell‟ F7 finale nel midi_array[]. Per controllare l'integrità del messaggio in ricezione sarà sufficiente effettuare la stessa operazione di somma e confrontarla con il checksum memorizzato. Se i due valori coincidono, i dati possono essere considerati integri. La funzione MakeSysex implementa anche la parte di codice con cui vengono richiamate le opportune funzioni API necessarie al sistema per l‟ interazione col 66 Capitolo 5 27 novembre 2008 device selezionato consentendo l‟ aperture delle porte midi ed inviando correttamente l‟ Exclusive Midi Message. Eccone il codice relativo: midipor t = midi_device; MIDIHDR* pHeader = new MIDIHDR; pHeader->lpData = (char *) &midi_array[0]; pHeader->dwBufferLength = 200; pHeader->dwBytesRecorded = (13+dim+1); pHeader->dwFlags=0; La libreria Windows Multimedia definisce la struttura MIDIHDR nel seguente modo: typedef struct midihdr_tag { LPSTR lpData; DWORD dwBufferLength; DWORD dwBy tesRecorded; DWORD_PTR dwUser; DWORD dwFlags; struct midihdr_tag * lpNext; DWORD_PTR reserved; DWORD dwOffset; DWORD_PTR dwReserved[4]; } MIDIHDR, *LPMIDIHDR; Definendo il puntatore pHeader ad una nuova struttura di questo tipo possiamo caratterizzarne i vari elementi come definito nel codice illustrato : lpData definisce il puntatore al messaggio midi da inviare che nel nostro caso è il midi_arraa[]. Viene assegnato infatti l‟ indirizzo del primo elemento dell‟ array. Si definisce la grandezza del buffer di “lettura” del messaggio midi pari a 200. Si definisce la grandezza attuale dei dati da “bufferizzare” in base al numero dei valori del parametro considerato : 13+dim+2. In seguito si utilizzano le funzioni API Midi idonee: midiOutOpen(&device, midipor t, 0, 0, CALLBACK_NULL); Tramite le varibili device e midiport si effettua l‟ apertura della porta midi selezionata (combo_midi_device). 67 Capitolo 5 27 novembre 2008 midiOutPrepareHeader(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); La funzione prepara il Messaggio Midi per l‟output indicando la periferica su cui inviarlo, il puntatore alla struttura MIDIHDR per la preparazione del buffer ed infine la dimensione in byte di quest‟ ultima. midiOutLongMsg(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); La funzione è quella che permette l‟ invio del messaggio e deve essere preceduta obbligatoriamente dalla midOutPrepareHeader da cui riprende la stessa tipologia di parametri. midiOutUnprepareHeader(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); Quest‟ ultima elimina i dati creati dalla _PrepareHeader. midiOutClose(device); Eseguito l‟ invio del messaggio si realizza la chiusura della periferica midi di connessione. L‟ evento che opera la chiamata del metodo MakeSysex è il click sul pulsante per l‟ invio delle modifiche apportate alla voce : Figura 5.4 – Pulsante SEND SYSEX che consente la formattazione e l‟ invio del messaggio midi (exclusive system message) contenenti le varie modifiche nella voce interessata. void CEditParDlg::OnOK() { MakeSysex2 (&g_dati_RAM[timbre][coupled][voice].Sysexcode , 2); unsigned char * p; char *p_temp; p_temp = &g_dati_RAM[timbre][coupled][voice].NomeVoce[0]; p = (unsigned char*) p_temp; MakeSysex(p , 17); MakeSysex(&g_dati_RAM[timbre][coupled][voice].orgPart_exist , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].cc32 , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].cc0, 1); 68 Capitolo 5 27 novembre 2008 MakeSysex(&g_dati_RAM[timbre][coupled][voice].pc , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].volume , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].filter , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].micro_panpot , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Panpot , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].voiceFamily , 1); p_temp = &g_dati_RAM[timbre][coupled][voice].PoliphonicPriority; p = (unsigned char*) p_temp; MakeSysex(p , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Env_Attack , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Env_Decay , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Env_Release , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Env_Delay , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].RndTune_Probability , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].RndTune_MaxCents , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].pitch , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].pipe , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].outch , 1); MakeSysex(&g_dati_RAM[timbre][coupled][voice].Dummy2[0] , 7); p_temp = &g_dati_RAM[timbre][coupled][voice].Shor tNomeVoce[0]; p = (unsigned char*) p_temp; MakeSysex(p , 9); CDialog::OnOK(); } La funzione membro relativa al bottone richiama la MakeSysex per ogni singolo parametro indipendentemente da quali sono stati effettivamente modificati o meno. Ad ogni chiamata viene passato l‟ indirizzo del parametro e la sua dimensione. Da notare come per i parametri NomeVoce, PoliphonicPriority, ShortNomeVoce sia stato necessario implementare l‟ operazione di casting essendo di tipo iniziale char. Da sottolineare anche il fatto che esiste anche un funzione definita MakeSysex2 utilizzata esclusivamente per il sysexcode. Quest‟ ultimo parametro infatti risulta l‟ unico ad assumere valori superiori a 127 quindi risulta necessario l‟ utilizzo di 2 byte (invece di 1). Tale metodo è identico alla MakeSysex originale e di seguito viene riportata l‟ unica parte di codice diversa necessarria per lo split del suo valore su 2 nibble (considerando di nuovo l‟ aspetto Big-endian Little-endian): #ifdef INTEL_TYPE midi_array[14] = (*p_value & 0x0F) ; midi_array[13] = ((*p_value & 0xF0) >> 4); #else 69 Capitolo 5 27 novembre 2008 midi_array[13] = (*p_value & 0x0F) ; midi_array[14] = ((*p_value & 0xF0) >> 4); #endif 5.3.4 – ELEMENTI GRAFICI Il caricamento della bitmap di background e l‟ elaborazione dell‟ aspetto grafico generale riutilizza il metodo ::ONPAINT e la proprietà OnDraw già illustrate e specificate nella Dialog principale. Anche per tutte le altre Dialog del progetto per quanto riguarda l‟ immagine di background il codice relativo per il suo caricamento risulta essere sempre lo stesso, mentre si evidenzieranno caratteristiche diverse di costruzione grafica deò dispositivo di interfaccia di tipo “lista di bottoni” relativamente all‟ ultima dialog che verrà presentata nel paragrafo 5.6 . 70 Capitolo 5 27 novembre 2008 5.4 – DIALOG EDIT PARAMETRI NOTA: IDD_DIALOG_NOTA Figura 5.5 – Composizione strutturale della dialog per la modifica di 3 parametri multi valore. La tastiera virtuale consente una visione globale dei 3 parametri consentendo un‟ interazione agevolata rispetto all‟accesso nello strumento reale. In tale Dialog l‟ utente può effettuare l‟ editing di 3 parametri ralativi ad ogni singola nota corripondente alla voce selezionata nella dialog principale per mezzo dei 3 solti indici della matrice g_dati_Ram[][][], cliccando sul tasto corrispondente nella virtual keyboard. La “costruzione” di tale finestra utente è implementata nei file EditParNotaDlg.cpp e EditParNotaDlg.h. Il nome della nota in editing viene opportunamente visualizzato nella casella a sinistra della tastiera virtuale ed i 3 parametri Volume , Tone, Pitch vengono valorizzati numericamente nei rispettivi editbox. La costruzione della virtual keyboard è stata possibile operando una opportuna disposizione di elementi reattivi (pulsanti) nella dialog osservabile nel graphic editor del Visual C++ e illustrata dalla seguente immagine. 71 Capitolo 5 27 novembre 2008 Figura 5.6 - Virtual keyboard (tastiera virtuale) la cui implemnetazione è stata possibileoperando una opportuna disposizione di elementi reattivi (pulsanti) di tipo CBitmapButton che consentano un caricamento dinamico di immagini sulla proprio superficie. Nel file header relativo alla dialog corrente è possibile visionare la definizione di tutti i 128 pulsanti nel seguente modo: CBitmapButton … … CBitmapButton m_cNOTA0; m_cNOTA127; Anche in questo caso ogni bottone è stato identificato con un opportuno ID a cui risulta associato, con il ClassWizard, successivamente, il tipo di messaggio ON_BN_CLICKED secondo la seguente mappatura: ON_BN_CLICKED(IDC_NOTA0, OnNota0) … … ON_BN_CLICKED(IDC_NOTA0, OnNota127) In questo modo il click sulle note richiama il metodo relativo OnNota0 … OnNota127. E‟ evidente che sia possibile l‟ editing di una singola nota per ogni invio. Di seguito il codice della funzione membro corrispondente alla nota0. void CEditParNotaDlg::OnNota0() { m_cEdVolumeNbn.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].volume_nbn[0])); m_cEdToneNbn.For mat("% ld",_T(g_dati_RAM[timbre][coupled][voice].tone_nbn[0])); m_cEdPitchNbn.Format("% ld",_T(g_dati_RAM[timbre][coupled][voice].pitch_nbn[0])); 72 Capitolo 5 27 novembre 2008 Il click sulla nota0 per prima cosa opera la formattazione dei valori nelle 3 variabili membro delle rispettive editbox impostando, logicamente, come indice dei 3 vettori volume_nbn[], tone_nbn[] e pitch_nbn[] il numero 0. p_volume = &g_dati_RAM[timbre][coupled][voice].volume_nbn[0]; p_tone = &g_dati_RAM[timbre][coupled][voice].tone_nbn[0]; p_pitch = &g_dati_RAM[timbre][coupled][voice].pitch_nbn[0]; Successivamente risultano definiti i 3 puntatori ai rispettivi valori dei e parametri principali in editing necessari per la loro manipolazione. m_cEdSelezioneNota.For mat("> C0 <"); Il nome della nota selezionata viene visualizzata nella rispettiva casella di testo dell‟ interfaccia grafica. m_cButtonVolumeNbn.EnableWindow(TRUE); m_cButtonToneNbn.EnableWindow(TRUE; m_cButtonPitchNbn.EnableWindow(TRUE); All‟apertura della dialog corrente non risulta selezionata alcuna nota e di conseguenza vengono disabilitati i 3 pulsanti di edit. Al successivo click nella tastiera virtuale vi è la conseguente abilitazione tramite l‟ apposita funzione EnableWindow. UpdateData(FALSE); } La modifica dei 3 parametri avviene tramite altrettanti pulsanti di EDIT identici a quelli già analizzati nella precedente dialog a cui corrispondono le seguenti variabili membro: CButton m_cButtonPitchNbn; CButton m_cButtonToneNbn; CButton m_cButtonVolumeNbn; che utilizzano i seguenti metodi: ON_BN_CLICKED(IDC_BUTTON_VOLUME_NBN, OnButtonVolumeNbn) ON_BN_CLICKED(IDC_BUTTON_TONE_NBN, OnButtonToneNbn) ON_BN_CLICKED(IDC_BUTTON_PITCH_NBN, OnButtonPitchNbn) 73 Capitolo 5 27 novembre 2008 caratterizzati dal codice: void CEditParNotaDlg::OnButtonVolumeNbn() { CSLIDERDlg dlg; min_range = 0; max_range = 127; p = p_volume; if(dlg.DoModal() == ID_BUTTON_SLIDER_MODIFICA) { m_cEdVolumeNbn.For mat("% ld", *p); UpdateData(FALSE); } return; } void CEditParNotaDlg:: OnButtonToneNbn() { … p = p_tone; … } void CEditParNotaDlg:: OnButtonPitchNbn() { … p = p_pitch; … } Si evidenzia il fatto che le varie funzioni membro risultano identiche a quelle già illustrate per i pulsanti di EDIT presenti nella Dialog dei parametri generali (Parametri Voce); l‟ unica differenza sta nell‟ assegnazione dei 3 puntatori temporanei (p_volume, p_tone, p_pitch) a p in base al tipo di parametro che si indende modificare. 5.4.1 – PULSANTE SEND SYSEX Tale pulsante permette l‟ impiego della stessa MakeSysex già utilizzata in precedenza e l‟ evento di click è gestito dalla seguente funzione membro: void CEditParNotaDlg::OnButtonInviaModifiche() { MakeSysex (&g_dati_RAM[timbre][coupled][voice].volume_nbn[0] , 127); MakeSysex (&g_dati_RAM[timbre][coupled][voice]. tone_nbn[0] , 127); MakeSysex (&g_dati_RAM[timbre][coupled][voice].pitch_nbn[0] , 127); 74 Capitolo 5 27 novembre 2008 EndDialog(ID_BUTTON_INVIA_MODIFICHE); } Indipendentemente di valori modificati si verifica l‟ invio completo dei 3 vettori relativi alla voce selezionata; il metodo makesysex è richiamato per ognuno dei 3 parametri, memorizzando nel messaggio esclusivo tutti i 127 valori caratterizzanti le note della virtual keyboard. Infine il click del pulsante SEND_SYSEX chiude la dialog corrente. 5.4.2 – ELEMENTI GRAFICI L‟ immagine di background si dimostra caricata tramite il metodo OnPaint() le cui funzionalità sono state illustrate recentemente. Per quanto riguarda, invece, il caricamento di bitmap sulla superficie dei pulsanti per la creazione e il funzionamento della virtual keyboard, si è sfruttata la proprietà Owner Draw e i messaggi per gli eventi del mouse definiti dai metodi di Windows: WM_LBUTTONDOWN e WM_LBUTTONUP. Come già evidenziato in altre circostanze, anche in tale contesto la variab ile di controllo associata ad ogni pulsante (m_cNOTAx) richiama il metodo. LoadBitmaps(); il caricamento delle bitmap, in tale caso, risulta diversificato distinguendo la parte di tasti bianchi e la restante parte di tasti neri, come si può evincere dal seguente codice implementato all‟ intero della OnInitDialog(): //Caricamento bitmaps TASTI BIANCHI m_cNOTA0.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABLE); m_cNOTA2.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABL E); m_cNOTA4.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABLE); m_cNOTA5.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABLE); m_cNOTA7.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABLE); m_cNOTA9.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, ID B_DISABLE); m_cNOTA11.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA12.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA14.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA16.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA17.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA19.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA21.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA23.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA24.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA26.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA28.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); 75 Capitolo 5 27 novembre 2008 m_cNOTA29.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA31.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA33.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA35.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA36.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA38.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA40.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA41.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA43.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA45.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA47.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA48.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA50.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA52.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA53.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA55.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA57.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , ID B_NOTA_FOCUS, IDB_DISABLE); m_cNOTA59.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA60.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA62.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA64.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA65.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA67.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISA BLE); m_cNOTA69.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA71.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA72.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA74.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA76.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA77.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA79.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA81.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA83.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA84.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA86.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA88.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA89.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DO WN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA91.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA93.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA95.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA96.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA98.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA100.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA101.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA103.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA105.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA107.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA108.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA110.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA112.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA113.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA115.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA117.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA119.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA120.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA122.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA124.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA125.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); m_cNOTA127.LoadBitmaps(IDB_NOTA_UP, IDB_NOTA_DOWN , IDB_NOTA_FOCUS, IDB_DISABLE); 76 Capitolo 5 27 novembre 2008 Le 4 immagini caricate tramite il loro ID sono: IDB_NOTA_ UP IDB_NOTA_ DOWN IDB_NOTA_ FOCUS (bianco) Tasto non cliccato IDB_ DISABLE (bianco) Tasto in fase di click (schiacciato) Tasto rilasciato (effettuata la selezione) Tasto disabilitato Figura 5.7 - Caricamento di bitmap sulla superficie dei pulsanti relativi ai tasti bianchi per la creazione e il funzionamento della virtual keyboard, sfruttando la proprietà Owner Draw //Caricamento bitmaps TASTI NERI m_cNOTA1.LoadBitmaps(IDB_NOTA_BLACK_UP, IDB_NOTA_BLACK_DOWN , IDB_NOTA_FOCUS, IDB_NOTA_BLACK_DISABLE); … … m_cNOTA126.LoadBitmaps(IDB_NOTA_BLACK_UP, IDB_NOTA_BLACK_DOWN , IDB_NOTA_FOCUS, IDB_NOTA_BLACK_DISABLE); Ora le 4 immagini caricate tramite il loro ID sono: IDB_NOTA_ BLACK_UP IDB_NOTA_ BLACK_DOWN IDB_NOTA_ FOCUS IDB_NOTA_ BLACK_DISABLE Tasto non cliccato Tasto in fase di click (schiacciato) Tasto rilasciato (effettuata la selezione) Tasto disabilitato Figura 5.8 - Caricamento di bitmap sulla superficie dei pulsanti relativi ai tasti neri per la creazione e il funzionamento della virtual keyboard, sfruttando la proprietà Owner Draw 77 Capitolo 5 27 novembre 2008 Lo stesso metodo è utilizzato per il caricamento del bottone SEND_SYSEX : m_cSendSysex.LoadBitmaps(IDB_SEND_UP, IDB_SEND_DOWN , IDB_DISABLE, IDB_DISABLE); 5.5 – DIALOG SLIDER: IDD_DIALOG_SLIDER La modifica del valore numerico dei vari parametri ralativi alla voce selezionata nella dialog principale, avviene attraverso il click sull‟ apposito pulsante >EDITA< che opera l‟ apertura di una nuova finestra qui visualizzata: Figura 5.9 – Dialog per la modifica del valore relativo al parametro in editing. L‟ elemento di tipo reattivo implementato è un cursore scorribile orizzontalmente definot come Slider. L‟ elemento principale della dialog risulta essere il cursore in alto che l‟ utente può muovere orizzontalmente tramite il mouse. Si tratta di un nuovo oggetto implementabile direttamente dal developer studio definito come slide r. Ai suoi estremi sono sistemate 2 editbox (non modificabili dall‟ utente) cui vengono fissatti i range massimo e minimo del parametro corrente in editing. Ciò garantisce anche un„ ottima tolleranza verso gli errori dell‟ utente evitando l‟ inserimento di valori non validi. Nella parte centrale vi è posizionato un altro editbox per la visualizzazione del valore attuale del parametro che risulta aggiornato opportunamente ad ogni spostamento del cursore interno allo slider. Per rendere effettiva la modifica apportata al valore, l‟ utente deve cliccare sul 78 Capitolo 5 27 novembre 2008 pulsante in basso. In tale modo viene aggiornato sia il valore del campo corrispondente al parametro nella struttura voci in RAM, sia la quantità visualizzata nella dialog di origine. Il funzionamento dettagliato di quanto illustrato risulta definito nei file di progetto SLIDERDlg.cpp e .h . 5.5.1 –SPECIFICHE DI FUNZIONAMENTO Importante la definizione delle seguenti variabili globali: extern int min_range, max_range; extern unsigned char *p; tramite cui, dalla finestra “madre”, la chiamata della SLIDERDlg consente di fissare il puntatore al parametro da modificare e il range di valori massimo e minimo ammissibili. BOOL C SLIDERDlg::OnInitDialog() { CDialog::OnInitDialog(); m_cEdSlider1.SetRangeMin(("% d", min_range), false); m_cEdSlider1.SetRangeMax(("% d", max_range), false); m_cEdSliderValue.For mat("% ld", *p); m_cEdMinRange.For mat("% ld", min_range); m_cEdMaxRange.For mat("% ld", max_range); m_cButtonSlider.LoadBitmaps(IDB_SLIDER_UP, IDB_SLIDER_DOWN , IDB_SLIDER_UP, ID B_DISABLE); … } Nell„ inizializzazione della dialog si assegnano i valori di massimo e minimo sia alla variabile membro dello slider, tramite le funzioni SetRangeMin e SetRangeMax, sia alle variabili membro delle editbox situate agli estremi dello slider. Inoltre viene inizializzata al valore corrente, letto da RAM tramite il puntatore globale p, la casella di testo centrale identificata tramite l‟ apposita variabile m_cEdSliderValue. Infine, sfruttando la già nota proprietà Owner Draw dei pulsanti, il metodo LodBitmaps() permette il corretto caricamento delle immagini sul pulsante “Conferma Modifica”. 79 Capitolo 5 27 novembre 2008 La funzione membro interna alla dialog che gestisce le funzionalità dello slider è la seguente: void CSLIDERDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if(nSBCode == SB_THUMBPOSITION) { m_cEdSliderValue.For mat("% ld", nPos); slider_value = 0; slider_value = nPos; UpdateData( false); } else { CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } Essa coordina lo spostamento del cursore aggiornando il valore visualizzato nella casella sottostante, assegnandolo alla variabile globale slider_value. 80 Capitolo 5 27 novembre 2008 5.6 – DIALOG: IDD_SELEZIONE_VOCI_MULTIPLE La funzionalità di tale dialog si distacca dal progetto centrale che si è basato sull‟ editing di tutti i parametri relativi ad una singola voce selezionata dall‟ utente. In questa nuova finestra si è voluto creare un elenco di tutte le voci presenti all’ inte rno dello strume nto elettronico Rodgers i538 per operarne sostanzialmente una selezione multipla senza alcuna operazione di editing. Ovvero si è reputato necessario avere a disposizione uno strumento grafico che permetta all‟ utente di attivare o disattivare da una fino a 245 voci di registro contemporaneamente e indistintamente dall‟ ordine di selezione. L‟ apertura di tale dialog è possibile tramite il pulsante Figura 5.10 – Pulsante di inizializzazione ed apertura della finestra di dialogo con funzionalità di multi selezione timbrica delle 245 voci di registro. presente nell‟ interfaccia principale il cui click richiama il seguente metodo: void CRODGERS_i385_GUIDlg:: OnMenuSelezioneVociMultiple() { CSelezioneVociMultipleDlg dlg; int i =0; int sum ,ck ,w ; ck = 0; midi_voci[0] = 0xF0; midi_voci[1] = 0x41; midi_voci[2] = 0x10; midi_voci[3] = 0x30; midi_voci[4] = 0x12; midi_voci[5] = 0x01; midi_voci[6] = 0x00; midi_voci[7] = 0x00; midi_voci[8] = 0x00; midi_voci[9] = 0x00; midi_voci[10] = 0x00; midi_voci[11] = 0x00; midi_voci[12] = 0x00; midi_voci[13] = 0x00; midi_voci[14] = 0x00; midi_voci[15] = 0x00; midi_voci[16] = 0x00; midi_voci[17] = 0x00; midi_voci[18] = 0x00; 81 Capitolo 5 27 novembre 2008 midi_voci[19] = 0x00; midi_voci[20] = 0x00; midi_voci[21] = 0x00; midi_voci[22] = 0x00; midi_voci[23] = 0x00; midi_voci[24] = 0x00; midi_voci[25] = 0x00; midi_voci[26] = 0x00; midi_voci[27] = 0x00; midi_voci[28] = 0x00; midi_voci[29] = 0x00; midi_voci[30] = 0x00; midi_voci[31] = 0x00; midi_voci[32] = 0x00; midi_voci[33] = 0x00; midi_voci[34] = 0x00; midi_voci[35] = 0x00; midi_voci[36] = 0x00; midi_voci[37] = 0x00; midi_voci[38] = 0x00; midi_voci[39] = 0x00; midi_voci[40] = 0x00; midi_voci[41] = 0x00; // Calcolo del Checksum sum = 0; for (w=7 ; w <=41 ; w++) { sum += midi_voci[w] ; } sum &= 0x7F; ck = (128 - sum)&0x7F; midi_voci[42] = ck; midi_voci[43] = 0xF7; dlg.DoModal(); return; } Praticamente tale metodo inizializza il midi_voci[43] definito globalmente come un array di elementi unsigned char. Dall‟ Appendice D è possibile visionare l‟ intero elenco delle 245 voci presenti all‟ interno dello strumento. Come è possibile notare, ogni singolo nome_voce è associato al bit (da 0 a 6) di un certo byte. L‟ attivazione delle voci può essere configurata su 35 byte totali. Ad esempio, se si volesse selezionare la voce 1 (16‟ Principal) bisognerebbe mandare un messaggio esclusivo in cui settare ad 1 il bit 1 del byte 0 e lasciare a 0 i restanti byte. Per tale scopo è stato creato l‟ array midi_voci[43] dove effettivamente i primi 6 elementi identificano la codifica del messagio esclusivo, me ntre nei successivi (tranne gli ultimi 2 byte utilizzati dal checksum e dall‟ F7) si 82 Capitolo 5 27 novembre 2008 dispongono i byte relativi all‟ attivazione delle voci corrispondenti. Quindi, al momento in cui l‟ utente interagisce sul pulsante di apertura della dialog, automaticamente viene inizializzato in RAM un array dove, dall‟ elemento 6 all’ elemento 41, il valore risulta settato a 0x00 che corrisponde ad una logica inizializzazione con tutte le voci spente (maggiori specifiche sono visibili nel par. 6.2 al 3° esempio). 5.6.1 – SPECIFICHE_1: LISTA DI BOTTONI Figura 5.11 – Dialog presentante il dispositivo di interfaccia: Lista di Bottoni. E‟ data la possibilità di multi-selezione timbrica sfruttando elementi d‟ interfaccia di tipo reattivo contraddistinguenti le voci attivate con un led rosso accesso. 83 Capitolo 5 27 novembre 2008 Nella lista ad ogni bottone corrisponde una voce; le voci selezionate hanno il rispettivo led acceso (rosso) mentre le altre risultano spente. I file di progetto per la costruzione e il funzionamento di questo dispositivo d‟ interfaccia sono: SelezioneVociMultipleDlg.cpp, LedButton.cpp, Led.cpp, ButtonListBox.cpp e i rispettivi file header. La creazione di ogni singolo bottone della lista è difinita dalla seguente classe dichiarata nel file LedButton.h : class CLedButton : public C Button { // Construction public: CLed* GetLed() {return ………….&m_Led;} void SetTextColour(COLORREF TxtColour) {m_TextColour = ………….TxtColour;} void SetButtonColour(COLORREF ButtonColour) {m_ButtonColour = ………….ButtonColour;} CWnd* GetParent() {return m_pParent;} void CentreLED(); CLedButton(CString ButtonText,long Bu ttonId,C Wnd* Parent); CString GetButtonText() {return ………….m_ButtonText;} long GetButtonId() {return m_ButtonId;} // Attr ibutes private: CRect COLORREF COLORREF CWnd* CString long m_LEDLocation; m_TextColour; m_ButtonColour; m_pParent; m_ButtonText; m_ButtonId; public: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); //}}AFX_VIRTUAL // Implementation public: virtual ~CLedButton(); // Generated message map functions protected: //{{AFX_MSG(CLedButton) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnClicked(); //}}AFX_MSG 84 Capitolo 5 27 novembre 2008 CLed m_Led; BOOL CreateLED(); DECLARE_MESSAGE_MAP() }; La creazione di una nuova istanza all‟ interno della lista bottoni è data dal metodo void CButtonListBox::AddItem(LPCTSTR ButtonTex t,long idButton) { CLedButton* pAddedButton; static int count = 0; CRect rect; CRect ButtonRect; count++; GetClientRect( rect ); pAddedButton = new CLedButton(ButtonText,idButton,this); ButtonRect.SetRect( 0, 0, rect.Width(), (rect.Height() / NUMBER_OF_BUTTONS_IN_LIST_VIEW) ); pAddedButton->Create( "", WS_CHILD|BS_OWNERDRAW, ButtonRect, this, 1001+count ); … … Si verifica quindi la generazione di un nuovo bottone, identificabile tramite il nome della voce, a cui risulta assegnato un idButton progressivo, in seguito necessario per l‟ accensione o lo spegnimento della voce stessa. La creazione dell‟ intera lista risulta operata all‟ interno della funzione di inizializzazione della dialog come si può evincere dal codice seguente (l „intero codice è visionabile nell‟ Appendice C_1): BOOL C SelezioneVociMultipleDlg::OnInitDialog() { CDialog::OnInitDialog(); m_curr_indice=0; InsertVoce("unused"); InsertVoce("16'Principal"); InsertVoce("16'Flute"); InsertVoce("8'Principal"); … … InsertVoce("_spare_"); InsertVoce("Test/Tune Mode"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); 85 Capitolo 5 27 novembre 2008 Rispettando l‟ elenco originale Roland di successione delle voci, viene richiamata per 245 volte la funzione membro InsertVoce() che riceve come parametro la stringa da assegnare alla nuova istanza della ButtonListBox. void CSelezioneVociMultipleDlg::Inser tVoce(LPCSTR name) { m_ButtonListBox.AddItem(name,m_curr_indice); m_curr_indice++; } Tale metodo effettivamente richiama la funzione di creazione della lista già illustrata precedentemente (AddItem) , a cui passa come parametri la stringa e l‟ indice corrente incrementato ad ogni iterazione. Tale prerogativa fa sì che ad ogni bottone corrisponda lo stesso numero di voce progressivo della lista voci originale Roland, permettendone una univoca e precisa individuazione. Ciò è necessario per la corretta gestione dei messaggi di spegnimento e accensione contemporanei di più voci da parte dell‟ interfaccia grafica e del sistema operativo. 5.6.2 – SPECIFICHE_2: MODALITA’ DI FUNZIONAMENTO Nella programmazione win32 ad ogni applicazione risulta associata un veicolazione dei massaggi tramite un canale con cui ogni oggetto della finestra comunica con la applicazione principale. In pratica tutti gli eventi (pressione di un tasto,movimento del mouse ecc) generano un messaggio che viene inserito in questo canale dal quale il sistema lo preleva e lo consegna (dispatch) all'applicazione associata. Su questo schema si basa il funzionamento della lista di bottoni implementata nel progetto in analisi. Il click di un singolo bottone è gestito dal seguente metodo: void CLedButton::OnClicked() { HWND ParentHwnd = m_pParent->m_hWnd; ::PostMessage(ParentHwnd,UM_BUTTON_CLICK,0,(DWORD)this); } 86 Capitolo 5 27 novembre 2008 Importante nella funzione è il metodo di windows ::PostMessage() l‟ invio di messaggi in quel canale di comunicazione con la finestra principale di cui si è parlato recentemente. Il click su uno qualsiasi dei 245 bottoni invia alla finestra windows attuale il preciso messaggio UM_BUTTON_CLICK , ovvero avverte l‟ applicazione che è stato pigiato un pulsante della lista. Ora il sistema operativo deve operare una interpretazione di questo messaggio inviatogli per rimandarlo in modo opportuno all‟ applicazione principale. E‟ ciò che viene effettivamente fatto dal metodo PreTranslateMessage(MSG* pMsg) come dimostra il seguente codice: BOOL C ButtonListBox::PreTranslateMessage(MSG* pMsg) { CLedButton* pButton = NULL; CWnd* Parent = NULL; Parent = GetParent(); if(pMsg->message == UM_BUTTON_CLICK) { pButton = (CLedButton*)pMsg->lParam; if(pButton->GetLed()->GetLedMode() == CLed::LED_ON) { pButton->GetLed()->SetLed (CLed::LED_COLOR_YELLOW,CLed::LED_OFF,CLed::LED_ROUND); } else { pButton->GetLed()->SetLed (CLed::LED_COLOR_RED,CLed::LED_ON,CLed::LED_ROUND); } Tale metodo riceve il messaggio dalla PostMessage() precedentemente illustrata. Se tale mesaggio risulta proprio uguale a UM_BUTTON_CLICK effettua un secondo controllo. Verifica se il led del pulsante cliccato risulta acceso; nel primo caso vuol dire che è stato selezionata una voce già accesa e quindi con il nuovo click si deve effettuarne il suo spegnimento. Nel caso in cui non sia già accesa bisogna effettivamente attivarla (accensione led). acceso = false; acceso = pButton->GetLed()->GetLedMode()==CLed::LED_ON; CString sel = pButton->GetButtonText(); long idSel = pButton->GetButtonId(); if(Parent) { ::PostMessage(Parent-> m_hWnd,WM_SEND_PARAM,idSel,(DWORD) this); 87 Capitolo 5 27 novembre 2008 } } return CListBox::PreTranslateMessage(pMsg); } Nella parte finale importante è l‟ assegnazione del valore dell‟ ID, relativo al bottone selezionato tramite il metodo della classe CButton definito GetButtonId(), alla variabile idSel. Successivamente viene richimata la funzione membro di windows PostMessage() di cui verranno approfondite le sue specifiche. Il suo costrutto base è dato da: BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM w Param, LPARAM lParam ); Invia un messagio alla finestra dell‟ applicazione corrente. Il messaggio (Msg) in tal caso risulta essere WM_SEND_PARAM; la sua definizione è stata effettuata nel file header Global.h : #define WM_SEND_PA RAM WM_USER+2100 è stato utilizzata la WM_USER+2100 per distinguerlo dai vari messaggi predefiniti di windows. Inoltre risulta presente anche un messaggio supplementare come terzo parametro di trasmissione (wParam) corrispondente all‟ idSel. Riassumendo questa procedura invia alla finestra di windows un messaggio in cui indica l‟ evento di click di un bottone della lista col relativo id corrispondete al nome della voce selezionata. Ora, per mezzo della ON_MESSAGE( WM_SEND_PARAM, OnSendParam ) l‟ appliazione rileva l‟ evento di click di un bottone e attiva la funzione membro che consente la costruzione effettiva del messaggio esclusivo midi_voci[43] settando ad 1 il bit (nel byte) della voce selezionata. 88 Capitolo 5 27 novembre 2008 LRESULT CSelezioneVociMultip leDlg::OnSendParam(WPARAM wParam, LPARAM lParam) { long idSel=wParam; int param_1,param_ 2; unsigned char param_3; param_ 1 = (idSel/7) + 7; param_ 2 = idSel%7; param_ 3 = 0; switch(param_2) { case 0: // Bit 0 settato a 1 param_ 3 = 0x01; break; case 1: // Bit 1 settato a 1 param_ 3 = 0x02; break; case 2: // Bit 2 settato a 1 param_ 3 = 0x04; break; case 3: // Bit 3 settato a 1 param_ 3 = 0x08; break; case 4: // Bit 4 settato a 1 param_ 3 = 0x10; break; case 5: // Bit 5 settato a 1 param_ 3 = 0x20; break; case 6: // Bit 6 settato a 1 param_ 3 = 0x40; break; Tale metodo riceve come parametro l‟ id del bottone cliccato e lo assegna alla variabile locale idSel. Effettua poi il calcolo dei parametri 1 e 2. Sfruttando il fatto che le voci risultano disposte in gruppi di 7 (ogni gruppo identifica 1 byte), si possono calcolare facilmente il byte e il bit corrispondenti alla voce selezionata. Il param_1 indica il numero del byte a cui appartiene la voce e viene calcolato con una semplice divisione incrementato poi di 7 in quanto i primi 6 elementi dell‟ array sono occupati dall codifica del messaggio esclusivo. Il param_2 indica il numero del bit da settare ad 1, ed è ottenuto come resto della divisione precedente. 89 Capitolo 5 27 novembre 2008 Il successivo costrutto switch(param_2) permette la converione in esadecimale (param_3) del bit calcolato come resto della divisione. if(acceso) { midi_voci[param_1] = midi_voci[param_ 1] | param_ 3; Tramite il settaggio della variabile bool globale si memorizza il byte relativo alla voce corrente. Se la voce deve essere attivata (acceso = vero) avviene il settaggio del bit corrisondente utilizzando l‟ operazione di OR col byte corrispondente in modo di lasciare a 0 (o ad 1) gli altri bit già eventualmente impostati. } else { midi_voci[param_1] = midi_voci[param_1] - param_3 } Se la voce, invece, deve essere disattivata (acceso = false) viene effettuata l‟ operazione di sottrazione col numero di bit corrispondete in esadecimale. SendSysex(); A questo punto si richiama il metodo SendSysex() per la formattazione del messaggio midi e l‟ apertura del device di output midi, le cui funzionalità sono state ampiamente descritte precedentemente. Il codice implementativo relativi allo sviluppo di quest‟ ultmima finestra di dialogo dell‟ applicativo compare integralmente nell‟ Appendice C_2. 90 Capitolo 6 27 novembre 2008 6. - VERIFICA FUNZIONAMENTO DEL’ INTERFACCIA 6.1 – UTILIZZO DI UN MIDI-MONITOR L‟ ingegnerizzazione della Graphic User Interface finale si è avvalsa dell‟ utilizzo di apposite strumentazioni software ed hardware di analisi e supporto dello applicativo nelle varie fasi di sviluppo progettuale. Una di esse è sicuramente il midi-monitor uno strumento di anali dei dati midi in grado di visualizzare la sequenze di byte costitutivi di un sistema esclusivo o di un messaggio di canale. Esistono midi- monitor sia hardaware che software entrambi dotati di schermo o di un‟ interfaccia visiva e di una o più porte di I/O midi. La visualizzazione degli eventi può essere selettiva operando un filtraggio dei dati in entrata o in uscita identificando chiaramente le sequenze di eventi midi tramite l‟ assegnazione di un codice identificativo della categoria a cui appartiene.………………………… Lo sviluppo progettuale si è avvalso di entrambi le tipologie, ma per questioni di praticità in tale breve sezione di verifica si prende in considerazione solo la tipologia software facilmente “trasportabile” e accessibile in qualsiasi momento. Nell‟ ambito applicativo considerato bisogna mettere in risalto il fatto che l‟implementazione delle funzionalità midi dell‟ interfaccia rispetta pienamente le specifiche GM e del costruttore Roland. Tuttavia l‟ organo liturgico Rodgers i538 non implementa ancora le parti software necessarie all’ interfacciament o con questo nuovo applicativo; tale questione verrà trattata proprio nel prossimo ed ultimo paragrafo concernente gli sviluppi futuri. Quindi si comprende subito quale sia stato il ruolo del midi- monitor in tale contesto d‟ utilizzo garantendo un supporto di verifica rapido ed affidabile. Di seguito possiamo vedere un‟ esempio di midi-monitor software in quanto in rete è possibile trovarne facilmente di vari tipi (si diversificano soprattutto per l‟ interfaccia utente), ma che racchiudono tutti le stesse funzioni di base. 91 Capitolo 6 27 novembre 2008 Figura 6.1 – Midi-Monitor software in grado di creare una connessione midi virtuale del sistema operativo interfacciandosi (virtualmente) con l‟ applicativo d‟ interfaccia sviluppato per la verifica dei messaggi midi inviati. Il software crea una connessione midi virtuale all‟ interno del sistema operativo in cui è installato (8 device di I/O denominati MIDI Yoke 1 – 8). Quindi lanciando l‟ eseguibile dell‟ interfaccia grafica del Rodgers i538 nella combo box relativa alla scelta del Device Midi da utilizzare per la comunicazione, si seleziona il MidiDevice0 corrispondente al canale MIDI Yoke 1. Risulta quindi possibile visualizzare i messaggi midi in “uscita” dall‟ interfaccia verificandone la correttezza compositiva. Quindi si effettuerà la dimostrazione della coerenza costruttiva dell‟ applicativo d‟ interfaccia tramite 3 esempi essenziali. 6.2 – 3 ESEMPI ESSENZIALI Nel primo esempio si esegue la verifica del messaggio inviabile dalla prima interfaccia di editing (PARAMETRI VOCE). Dopo aver selezionato una voce principale semplicemente cliccando sul pulsante SEND SYSEX (senza effettuare alcuna modifica ai 23 parametri visualizzati) la GUI invierà il messaggio midi standardizzato (par. 5.3.3) sul canale di comunicazione indicato. 92 Capitolo 6 27 novembre 2008 Dal midi- monitor (già attivo) vengono immediatamente visualizzate le sequenze di byte (esadecimale) ricevute evidenziando la loro composizione strutturale. Figura 6.2 – Dal Midi-Monitor software risultano visualizzabili 23 blocchi consecutivi di messaggi relativi ai rispettivi parametri della Dialog di editing dei parametri voce (par.5.3) Si possono individuare 23 blocchi di byte consecutivi rappresentanti ciascuno la sequenza di implementazione midi utilizzata ed ampiamente illustrata (par 5.3.3) 93 Capitolo 6 27 novembre 2008 per la trasmissione dei 23 parametri modificabili. Ogni blocco ha dimensioni diverse e presenta le caratteristiche salienti relative allo standard midi del costruttore Roland analizzato nel paragrafo 3.6 . Ogni sequenza esibisce la formattazione del messaggio esclusivo standard (fig . 4.2) riscontrabile dai seguenti byte: F0 41 10 30 12 06 _[DATI]_ CK F7 La lunghezza di ogni blocco è direttamente proporzionale al numero di valori editabili e caratterizzanti il parametro considerato. Infatti dal 3° al 21° blocco si evidenzia una stessa lunghezza delle sequenze; essi risultano legati ai rispettivi parametri costituiti da un unico valore. I restanti blocchi 1, 2, 22 e 23 presentano una strutturazione fondata su un numero di byte maggiore; ciò verifica l‟ effettiva coerenza dell„ applicativo sviluppato avvalendosi della perfetta integrazione tra potenzialità di sviluppo grafico ed interazione con le funzioni Win32 di comunicazione ed interfacciamento midi. Nel secondo esempio si esegue la verifica del messaggio inviabile dalla seconda interfaccia di editing (PARAMETRI NOTA). Figura 6.2 – Dal Midi-Monitor software risultano visualizzabili 23 blocchi consecutivi di messaggi relativi ai rispettivi parametri della Dialog di editing dei parametri voce (par.5.3) 94 Capitolo 6 27 novembre 2008 Tale finestra di dialogo, come già visto, è caratterizzata dal particolare dispositivo dì interazione grafica di tipo tastiera virtuale. Il messaggio esclusivo relativo ai suoi 3 parametri editabili è esaminabile nell‟ interfaccia del midi- monitor in Figura 6.2 . Le 3 sequenze mostrano una stessa lunghezza complessiva dei messaggi esclusivi inviati dall‟ interfaccia; ciò riflette la prerogativa di inserimento e formattazione dei blocchi midi conformemente alla natura dai parametri rappresentati, che in questo caso hanno una strutturazione vettoriale monodimensionale costituita di 128 elementi ciascuno. Si riscontra quindi che il software implementato è perfettamente operativo ed efficiente nell‟ osservanza dei protocolli di comunicazione sviluppati ed ottimizzati per l‟ interazione diretta con l‟ hardware midi esterno. In particolare risulta efficace la soluzione adottata per la composizione base di ogni system exclusive message attuabile per mezzo di unico costrutto di programmazione identificato nella funzione MakeSysex (par. 5.3.3). Il messaggio midi inviato risulta impacchettato in un Array costruito dinamicamente in relazione al numero di valori caratterizzanti quel particolare parametro. La funzione MakeSysex ne effettua la standardizzazione con l‟ inclusione dei byte di apertura e chiusura del messaggio (F0 – F7); nel corpo del system exclusive message inserisce dinamicamente le sequenze di byte relative ai valori editabili (1 byte = 1 valore) 95 Capitolo 6 27 novembre 2008 Nel terzo ed ultimo esempio si esegue la verifica del messaggio inviabile dall‟ interfaccia di multi selezione timbrica nell‟ insieme delle voci strutturanti l‟ organo Rodgers i538. Figura 6.3 – Dal Midi-Monitor software risultano visualizzabili 23 blocchi consecutivi di messaggi relativi ai rispettivi parametri della Dialog di editing dei parametri voce (par.5.3) Come già visto ampiamente nel precedente capitolo, la finestra di dialogo consente all‟ utente di attivare o disattivare una o più voci contemporaneamente (senza però effettuarne l‟ editing) . La situazione in cui le voci risultano tutte spente è quella visionabile nel blocco 2 di Figura 6.3 dove tutti i byte della sequenza riservati a tale funzione (dal byte numero 8) risultano settati a 0x00. I blocchi in sequenza inoltre non sono contemporanei come nei due esempi precedenti; ma risultano ottenuti in più interazioni consecutive con la lista di bottoni eseguendo le seguenti operazioni: Block#1 - accensione voce numero 1 (16‟ Principal) Block#2 - spegnimento voce numero 1. Q uindi coincide con la situazione in cui tutte le voci risultano disattivate. Block#3 – riaccensione voce numero 1 Block#4 – accensione voce numero 2 96 Capitolo 6 27 novembre 2008 Block#5 - accensione voce numero 3 Block#6 - accensione voce numero 4 Block#7 - accensione voce numero 5 Effettivamente ciò verifica quanto già visionato nelle modalità di funzionamento espletate nel paragrafo 5.6; dall‟ Appendice D è possibile capire come ogni singolo nome_voce sia associato al bit (da 0 a 6) di un certo byte. L‟ attivazione delle voci può essere configurata su 35 byte totali. Per tale scopo è stato creato l‟ array midi_voci[43] dove effettivamente i primi 6 elementi identificano la codifica del messagio esclusivo, mentre nei successivi (tranne gli ultimi 2 byte utilizzati dal checksum e dall‟ F7) si dispongono i byte relativi all‟ attivazione delle voci corrispondenti. Nell‟ esempio quindi l‟ attivazione delle prime 5 voci effettua il cambiamento solo del primo byte (7° elemento della sequenza) utile per l‟ inserimento delle voci di registro dell‟ organo. Da notare come in questo caso non vi sia una creazione dinamica del messaggio esclusivo come avviene invece negli esempi precedenti. All‟ apertura di tale finestra si inizializza un vettore di 44 elementi la cui lunghezza rimane costante nell‟ intera fase di interazione con l‟ utente in quanto il numero di voci rimane costante; varia sol il numero di quelle effettivamente attive. 97 Capitolo 7 27 novembre 2008 7. - SVILUPPI FUTURI E CONCLUSIONI La complessa architettura della Graphic User Interface ingegnerizzata in collaborazione col personale specializzato dell‟ azienda Roland mette alla luce una perfetta integrazione tra funzionalità grafiche ottimizzate, stile di interazione diversificato ed ottima implementazione delle funzionalità midi standardizzate permettendo il raggiungimento degli obbiettivi basilari di questa tesi sperimentale. Le potenzialità dell‟ applicato sviluppato mettono alla luce anche le sue prospettive di crescita ed evoluzione future incentrando l‟ attenzione: sulle modalità di implementazione software da sviluppare e collaudare all‟ interno dello strumento musicale elettronico reale; sull‟ ampliamento delle funzionalità d‟ interfaccia includendo anche il supporto di tutte e 4 le configurazioni delle voci di registro costituenti il RODGERs i538; sui miglioramenti apportabili nell‟ interfacciamento con l „intera gamma di strumenti musicali della famiglia RODGERs (organi liturgici). Si è potuto constatare dal capitolo precedente come il funzionamento generale della Graphic User Interface non sia stato collaudato in interazione diretta con l‟ organo liturgico reale. La verifica di coerenza dei dati midi inviati dall‟ applicativo, infatti , si è avvalsa del supporto di midi- monitor (software ed hardware) consentendo un riscontro grafico dei vari messaggi esclusivi transitanti nel canale (midi) di comunicazione preso in esame. Appare quindi la necessità di creare l‟ archetipo delle strutture software da implementare e collegare adeguatamente all‟ interno dello strumento fisico reale. Il Rodgers i538 deve essere in grado di poter interpretare correttamente le varie sequenze di messaggi esclusivi che entrano sulla porta di midi input. Presumibilmente si dovranno realizzare costrutti di programmazione software in grado di effettuare un „operazione di “spacchettamento” per ogni blocco di messaggi in ingresso individuando il tipo di parametri cui si riferiscono e il numero di valori globali (editati) che li caratterizzano. 98 Capitolo 7 27 novembre 2008 E‟ possibile quindi effettuare un eventuale confronto generale tra le funzioni di creazione del messaggio (nell‟ interfaccia) e quelle di “lettura” nello strumento musicale reale. Elenco delle funzionalità dell‟ interfaccia adibite all‟ invio dei messaggi esclusivi garantendo anche l‟ apertura e il corretto utilizzo delle componenti API del sistema: individuazione dei 3 indici di accesso alla voce desiderata accesso all‟ indirizzo di memoria del parametro (della voce) selezionato dall‟ utente tramite appositivi dispositivi d‟ interfaccia lettura del valore o dei valori caratteristici del parametro editato costruzione dinamica del system exclusive message posizionando i valori letti e l‟ indirizzo di memoria del parametro negli appositi byte prestabiliti dallo standard di implementazione sviluppato invio multiplo dei messaggi (più sequenze di blocchi racchiusi dai byte F0 – F7) Elenco delle funzionalità del software da implementare nell‟ organo liturgico adibite ad una corretta ricezione ed interpretazione dei messaggi esclusivi provenienti dall‟ interfaccia grafica esterna: ricezione multipla dei system exclusive message focalizzazione delle parti principali del messaggio standardizzato individuazione dei 3 indici di accesso alla matrice tridimensionale strutturante l‟ organizzazione multi timbrica dello strumento elettronico individuazione del tipo di parametro da modificare e del numero di valori che lo compongono conversione dell‟ indirizzo di memoria del parametro ricevuto in uno valido per lo strumento elettronico inserimento dei nuovi valori nei relativi campi della struttura parametri per ogni livello di sottovoci a cui si riferisce 99 Capitolo 7 27 novembre 2008 In secondo luogo le caratteristiche tecniche dell‟ organo liturgico RODGERs Insignia 538-WD (Appendice_B) evidenziano 4 configurazioni di registro voci standard dello strumento: francese barocca inglese americana L‟ applicativo d‟ interfaccia progettato si è basato esclusivamente sulle voci di registro della configurazione barocca come risulta evidente dal paragrafo 4.2 dall‟ analisi del file baroque.cpp. Nella strutturazione delle altre voci di registro (francese, inglese, americana) i cambiamenti sono riscontrabili esclusivamente nell‟ organizzazione dei sottolivelli di RANK e VOICE PALETTE mantenendo la strutturazione standard già analizzata per la configurazione barocca. Si potrebbero così integrare nella GUI le nuove funzionalità di accesso alle 4 configurazioni di registro voci tramite 3 semplici operazioni: inclusione nel devoloper studio dei files contenenti la strutturazione delle 4 configurzioni (es. french.cpp, english.cpp,america.cpp) implementazione nella finestra di dialog principale di una nuova combo box per la scelta di una delle 4 configurazioni da parte dell‟ utente caricamento in RAM della configurazione selezionata In particolare, l‟ ultimo punto è facilmente implementabile riprendendo il codice sorgente già sviluppato (visionato nel paragrafo 5.2), operando l‟ inizializzazione dei dati in RAM con 4 funzioni diverse simili alla CaricaBaroqueRom(); si diversificano solo le strutture di origine prese da uno dei 4 files (baroque.cpp, french.cpp, english.cpp,american.cpp). L‟ ultima prerogativa di sviluppo futuro dell‟ applicativo GUI EDITOR si incentra sui miglioramenti d‟ interfacciamento con l‟ hardware esterno apportabili nell‟ ambito di interoperatività con l‟ intera gamma dei prodotti RODGERs. 100 Capitolo 7 27 novembre 2008 Di seguito è possibile prendere in esame le tipologie di organi liturgici della famiglia RODGERs prodotti dall‟ azienda ROLAND . Essi si distinguono principalmente per un diverso numero di manuali (tastiere) con cui risultano strutturati, consentendone la seguente classificazione: 101 Capitolo 7 27 novembre 2008 Organi a 2 MANUALI Organi a 3 MANUALI Organi a 4 MANUALI Organi a 5 MANUALI Tutte le 4 tipologie (ognuna caratterizzata da più modelli) possiedono una configurazione di registro delle voci simile a quella analizzata per il RODGERs i538 (par. 4.2) e le variazioni possibili riguardano il numero di voci di registro principali e il numero dei livelli del “RANK”, ovvero di canne simulate dall‟ organo; mentre la quantità e le tipologie di parametri interni editabili (per ogni singola voce) rimangono sostanzialmente invariate, conservando la strutturazione riscontrata per il Rodgers i538 (par 4.2) Appare estremamente interessante l‟ idea di poter usufruire di un GUI EDITOR “universale” capace di offrire un interfacciamento con l‟ intera gamma di organi RODGERs. Si potrebbe ipotizzare un‟ interfaccia di tipo modulare in grado di cambiare l‟ aspetto grafico e funzionale in base alla tipologia e al modello di organo con cui si desidera operare una comunicazione midi. L‟ operazione più lunga e complicata da implementare sarebbe quella di crearsi una dialog per ogni modello della gamma RODGERs; ciascuna finestra presenterebbe le sembianze estetiche del modello a cui si riferisce ripetendo le operazioni già collaudate con l‟ interfaccia del RODGERs i538 (par. 5.2). Nell‟ applicativo ogni finestra sarebbe richiamabile identificativo simile a quello usato tramite un idoneo codice per l‟ i538 del tipo: “IDD_DIALOG_RODGERs_XX” (la sigla XX indica il modello relativo alla finestra considerata). All‟ utente sarà consentita la scelta della tipologia e del modello di organo con cui interagire mediante un‟ interfaccia iniziale formalizzabile nel seguente schema: 102 Capitolo 7 27 novembre 2008 FINESTRA INIZIALE: L’ UTENTE SCEGLIE TIPOLOGIA E MODELLO DI ORGANO CON CUI COMUNICARE FINESTRA (o più) RELATIVA AL MODELLO SELEZIONATO EDITING PARAMETRI VOCE EDITING PARAMETRI NOTA La struttura rispecchia molto quella individuabile in figura 4.2 preponendo come elemento aggiuntivo l‟ interfaccia di selezione del tipo di strumento a cui l‟ utente intende interfacciarsi. Aspetto non meno rilevante risulterebbe la costruzione grafica delle dialogs relative all‟ intera gamma RODGERs. Prendendo, ad esempio, un organo liturgico con 5 manuali si presenterebbe la problematica di visualizzare ben 6 sezioni di tasti nell„ interfaccia (il doppio rispetto a quelle dell‟ i538); sicuramente i tools di sviluppo offerti dal VISUAL C++ e delle MFC propongono svariati espedienti d‟ implementazione e solo un vero e proprio studio approfondito permetterebbe di individuare la soluzione più congeniale a questa e alle altre tematiche di architettura di programmazione. In conclusione le potenzialità di sviluppo del GUI EDITOR elencate mettono in risalto le qualità del software sviluppato, facendo risaltare la dedizione riposta nell‟ intera conduzione dell‟ attività, portando a compimento gli obbiettivi prefissati in tale tesi sperimentale. La grande passione per l‟argomento e l‟ impegno da me profuso, unito alle competenze del personale altamente specializzato che mi ha accompagnato e mi ha affiancato in questo percorso formativo, hanno arricchito le mie conoscenze, sviluppando nuove applicazioni, approfondendo e contestualizzando le nozioni che questa università mi ha trasmesso nel corso del mio ciclo di studi. 103 27 novembre 2008 104 APPENDICE A 27 novembre 2008 - APPENDICE A - Strutturazione schematica di un sintetizzatore Caratteristica Descrizione MULTITIMBRICITÀ Definibile anche come politimbricità : si intende la capacità di generare simultaneamente diverse timbriche, assegnando a ciascuna di esse un numero variaile delle voci polifoniche a disposizione. SEQUENCER E‟ defininibile come un dispositivo hardaware (ma oggi è molto utilizzato anche la tipologia software) che permette di creare e produrre delle sequenze di segnal idi controllo per comandare uno strumento elettronico POLIFONIA Si intende il numero di note (assegnate a uno o più timbri) suonabili nello stesso istante. Gli strumenti moderni hanno molta più polifonia dei sintetizzatori Vintage raggiungendo anche 128 o più note suonabili contemporaneamente. UNITÀ EFFETTI INTEGRATA (DSP) CONNESSIONI AUDIO CONNESSIONI MIDI L‟ unità effetti integrata si avvale di un apposito processore di segnali definito digital signal processing (DSP) applicando effetti come chorus, delay, pictch, reverbero sui suoni generati internamente o su fonti esterne dall‟ ingresso audio (analogico o digitale). Un synth (sintetizzatore) può possedere diverse tipologie di connessioni audio: analogiche e digitali. Ciò permette molteplici caratteristiche di collegamento con diversi dispositivi esterni. Solitamente ogni strumento è dotato di almeno una porta di MIDI-IN e una di MIDI-OUT. E‟ possibile riscontrare la presenza di più porte per l‟ interazione. con più device esterni. 105 APPENDICE B 27 novembre 2008 - APPENDICE B - 106 APPENDICE C_1 27 novembre 2008 - APPENDICE C_1Files di progetto per la costruzione della dialog relativa alle voci della lista bottoni del par. 5.6. InsertVoce("8'Voce Umana"); InsertVoce("8' Gemshorn Celeste II"); InsertVoce("8' Flute Harmonique"); InsertVoce("8' Rohrflote"); InsertVoce("8' Gemshorn"); InsertVoce("8' Flauto Dolce"); InsertVoce("8' Flute Celeste"); InsertVoce("8' Unda Maris II"); InsertVoce("4' Octave"); InsertVoce("4' Gedacktflote"); InsertVoce("2' 2/3 Nasat"); InsertVoce("2' Superoctave"); InsertVoce("2' Waldflote"); InsertVoce("1' 1/3 Quint"); InsertVoce("Sesquialtera II"); InsertVoce("1' 3/5 Terz"); InsertVoce("IV - V Mi xture"); InsertVoce("III Sharf"); InsertVoce("16' Posaune"); InsertVoce("8' Trompete"); InsertVoce("8' Trompette Harmonique"); InsertVoce("8' Krummhorn"); InsertVoce("8' French Horn"); InsertVoce("8' Klarine"); InsertVoce("Harp"); InsertVoce("Carillon"); InsertVoce("Harpsichord"); InsertVoce("Piano"); InsertVoce("Great Tremulant"); InsertVoce("16' Great to Great"); InsertVoce("4' Great to Great"); InsertVoce("4' Flauto Dolce"); InsertVoce("4' Flauto Celeste"); InsertVoce("_spare_"); InsertVoce("8' Prestant"); InsertVoce("8' Gedackt"); InsertVoce("4' Principal"); InsertVoce("4' Koppeflote"); InsertVoce("2' 2/3 Nasard"); InsertVoce("2' Oktave"); InsertVoce("1' 1/3 Spitzquinte"); InsertVoce("1' Sifflote"); InsertVoce("III -IV Sharf"); InsertVoce("16' Dulzian"); InsertVoce("8' Krummhorn"); InsertVoce("Harpsichord"); InsertVoce("Positiv tremulant"); InsertVoce("positiv Unisson Off"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("16' Bourdon Doux"); InsertVoce("String"); InsertVoce("8' Viola Pomposa"); InsertVoce("8' Viola Celeste"); InsertVoce("8' Viola Celeste II"); InsertVoce("8' Muted Viols II"); 107 APPENDICE C_1 27 novembre 2008 InsertVoce("8' Bourdon"); InsertVoce("8' Flauto Dolce"); InsertVoce("8' Flute Celeste"); InsertVoce("8' Flute Celeste II"); InsertVoce("8' Gamba"); InsertVoce("8' Echo Gamba"); InsertVoce("4' Principal"); InsertVoce("4' Nachthorn"); InsertVoce("2' 2/3 Nasard"); InsertVoce("2' Doublette"); InsertVoce("2' Blockflote"); InsertVoce("1' 3/5 Tierce"); InsertVoce("1' Sifflote"); InsertVoce("Sesquialtera II"); InsertVoce("IV Plein jeu"); InsertVoce("III C ymbale"); InsertVoce("Contre Basson"); InsertVoce("8' Festival Trumpet"); InsertVoce("8' Trompette"); InsertVoce("8' Hautbois"); InsertVoce("8' Voix Humaine (mf)"); InsertVoce("8' Voix Humaine (mp)"); InsertVoce("4' Clairon"); InsertVoce("Swell Tremulant"); InsertVoce("16' swell to swell"); InsertVoce("Swell Unison off"); InsertVoce("4' swell to swell"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("Pedal Chamade"); InsertVoce("Choir Chamade"); InsertVoce("Great Chamade"); InsertVoce("Swell Chamade"); InsertVoce("Solo Chamade"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("16' Contre Erzahler"); InsertVoce("8' Salicional"); InsertVoce("8' Viola Celeste"); InsertVoce("8' Hohflote"); InsertVoce("8' Echo Celeste II"); InsertVoce("4' Spitzpincipal"); InsertVoce("4' Lieblichflote"); InsertVoce("4' Octave Erzahler"); InsertVoce("2' 2/3 Nasard"); InsertVoce("2' Octave"); InsertVoce("2' Flachflote"); InsertVoce("1' 1/3 Quintflote"); InsertVoce("1' Sifflote"); InsertVoce("Mouted Cornet III"); InsertVoce("III Mi xture"); InsertVoce("8' Troumpette Harmonique"); InsertVoce("8' Major Tuba"); InsertVoce("8' Krummhorn"); InsertVoce("8' Harpsichord"); InsertVoce("8' Harp"); InsertVoce("Choir Tremulant"); InsertVoce("16' choir tp choir"); InsertVoce("Choir Unison off"); InsertVoce("4' choir tp choir"); InsertVoce("8' Concert Flute"); InsertVoce("8' Erzhler"); 108 APPENDICE C_1 27 novembre 2008 InsertVoce("8' Erzhler CelesteII"); InsertVoce("8' Flauto Mirabilis"); InsertVoce("8' Gross Gamba"); InsertVoce("8' Gamba Celeste"); InsertVoce("4' Doppelflute"); InsertVoce("2' 2/3 Flute twelfth"); InsertVoce("2' Piccolo"); InsertVoce("Grand jeu VIII"); InsertVoce("16' Contre Bombarde"); InsertVoce("8' Fanfare Trumpet"); InsertVoce("8' Tube Mirabilis"); InsertVoce("8' English horn"); InsertVoce("8' French horn"); InsertVoce("8' Clarinet"); InsertVoce("4' Clarion Harmonique"); InsertVoce("Carillon"); InsertVoce("Solo Tremulant"); InsertVoce("16' Solo to Solo"); InsertVoce("Solo Unison Off"); InsertVoce("4' Solo to Solo"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("32' Contra Pincipal"); InsertVoce("32' Contre Bourdon"); InsertVoce("16' Major Bass"); InsertVoce("16' Principal"); InsertVoce("16' Subass"); InsertVoce("16' Violone"); InsertVoce("16' Gemshorn"); InsertVoce("16' Bourdon Doux"); InsertVoce("16' Erzahler"); InsertVoce("8' Octave"); InsertVoce("8' Violoncello"); InsertVoce("8' Gedackt Pommer"); InsertVoce("8' Bourdon (sw)"); InsertVoce("4' Choralbass"); InsertVoce("4' Gedacktpfeife"); InsertVoce("2' Kleinflote"); InsertVoce("IV Mi xture"); InsertVoce("V Harmonics"); InsertVoce("III Cornet"); InsertVoce("32' Contre Bombarde"); InsertVoce("32' Contre Basson"); InsertVoce("16' Bombarde"); InsertVoce("16' Pousane (gt)"); InsertVoce("16' Basson"); InsertVoce("8' Trompette"); InsertVoce("Oktave Pousane (gt)"); InsertVoce("4' Clarion"); InsertVoce("4' Krummhorn"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("8' Great to Pedal"); InsertVoce("4' Great to Pedal"); InsertVoce("8' swell to Pedal"); InsertVoce("4' swell to Pedal"); InsertVoce("8' Choir to Pedal"); InsertVoce("4' Choir to Pedal"); InsertVoce("8' Solo to Pedal"); InsertVoce("4' Solo to Pedal"); InsertVoce("8' Positiv to Pedal"); InsertVoce("16' Swell to Great"); 109 APPENDICE C_1 27 novembre 2008 InsertVoce("8' Swell to Great"); InsertVoce("4' Swell to Great"); InsertVoce("16' Choir to Great"); InsertVoce("8' Choir to Great"); InsertVoce("4' Choir to Great"); InsertVoce("16' Solo to Great"); InsertVoce("8' Solo to Great"); InsertVoce("4' Solo to Great"); InsertVoce("16' Positiv to Great"); InsertVoce("8' Positiv to Great"); InsertVoce("16' Swell to Choir"); InsertVoce("8' Swell to Choir"); InsertVoce("4' Swell to Choir"); InsertVoce("16' Solo to Choir"); InsertVoce("8' Solo to Choir"); InsertVoce("4' Solo to Choir"); InsertVoce("Positiv Off Choir"); InsertVoce("16' Choir to Swell"); InsertVoce("8' Choir to Swell"); InsertVoce("4' Choir to Swell"); InsertVoce("8' Solo to Swell"); InsertVoce("Positiv Off Swell"); InsertVoce("8' Positiv to Swell"); InsertVoce("8' Positiv to Swell"); InsertVoce("8' Positiv to Solo"); InsertVoce("Great tuttu on Solo"); InsertVoce("Pipe Tremulant"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("All Pipes Off"); InsertVoce("All Ancillary On"); InsertVoce("Main Tremulant"); InsertVoce("Flute Tremulant Full"); InsertVoce("Great/pedal Ancillary On"); InsertVoce("Choir Pipes Off"); InsertVoce("Choir Ancillary On"); InsertVoce("Main Off"); InsertVoce("Antiphonal On"); InsertVoce("Swell Pipes Off"); InsertVoce("Festival Trumpet FF"); InsertVoce("Swell Ancillary On"); InsertVoce("Zimbelstern"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); 110 APPENDICE C_2 27 novembre 2008 - APPENDICE C_2 - FIles di progetto per la costruzione della dialog relativa alla lista di bottoni (par. 5.6) FILE ButtonListBox.cpp // ButtonListBox.cpp : implementation file // #include "stdafx.h" #include "ButtonListBox.h" #include "Global.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif bool acceso; ///////////////////////////////////////////////////////////////////////////// // CButtonListBox CButtonListBox::CButtonListBox() { } CButtonListBox::~CButtonListBox() { for(int i=0; i<= m_ButtonArray.GetUpperBound();i ++) delete m_ButtonArray.ElementAt(i); } /////////////////////////////////////////////////////////////////// /////Creazione di un nuovo elemento (bottone) all' interno///////// /////della lista bottoni inserendo la stringa e l' id relativi///// /////////////////////////////////////////////////////////////////// void CButtonListBox::AddItem(LPCTSTR ButtonText,long idButton) { CLedButton* pAddedButton; static int count = 0; CRect rect; CRect ButtonRect; count++; GetClientRect( rect ); pAddedButton = new CLedButton(ButtonText,idButton,this); ButtonRect.SetRect( 0, 0, rect.Width(), (rect.Height() NUMBER_OF_BUTTONS_IN_LIST_VIEW) ); / 111 APPENDICE C_2 27 novembre 2008 pAddedButton->Create( "", WS_CHILD|BS_OWNERDRAW, ButtonRect, this, 1001+count ); int AddedAt = m_ButtonArray.Add( p AddedButton ); //Adds a string and assigns nIndex the index of the current item int nIndex = AddString( "" ); // Set a hight for the data item CListBox::SetItemHeight( nIndex, NUMBER_OF_BUTTONS_IN_LIST_VIEW ); rect.Height() / //If no error, associates the index with the button if( nIndex!=LB_ERR && nIndex!=LB_ERRSPACE ) SetItemData( nIndex, (DWORD)m_ButtonArray.ElementAt(AddedAt) ); } BEGIN_MESSAGE_ MAP(CButtonListBox, CListBox) //{{AFX_ MSG_ MAP(CButtonListBox) ON_WM_DRAWITEM_REFLECT() ON_WM_ERASEBKGND() //}}AFX_ MSG_ MAP END_MESSAGE_ MAP() ///////////////////////////////////////////////////////////////////////////// // CButtonListBox message handlers void CButtonListBox::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class CListBox::PreSubclassWindow(); } void CButtonListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CRect LedButtonRect = lpDrawItemStruct->rcItem; //Gets the rect of the item CLedButton* pLedButton = reinterpret_cast<CLedButton*>(lpDrawItemStruct>itemData); if(!pLedButton) return; pLedButton->MoveWindow(LedButtonRect); pLedButton->CentreLED(); pLedButton->ShowWindow( SW_SHOW ); } BOOL CButtonListBox::OnEraseBkgnd(CDC* pDC) { // Doing this speeds up drawing apparently //return CWnd::OnEraseBkgnd(pDC); return TRUE; } ////////////////////////////////////////////////////////////////// ////Gestione evento di attivazione o disattivazione di una//////// ////singola voce (accensione o spegnimento led corrispondete)//// ///all' interno della listbox//////////////////////////////////// 112 APPENDICE C_2 27 novembre 2008 ///////////////////////////////////////////////////////////////// BOOL CButtonListBox::PreTranslateMessage(MSG* pMsg) { CLedButton* pButton = NULL; CWnd* Parent = NULL; Parent = GetParent();//(?????) //Se ricevo un messaggio di click su una voce verifico se tale voce deve //essere spenta o accesa (a seconda dello stato iniziale) if(pMsg->message == UM_BUTTON_CLICK) { pButton = (CLedButton*)pMsg->lParam; if(pButton->GetLed()->GetLedMode() == CLed::LED_ON)//se la voce è accesa { pButton->GetLed()>SetLed(CLed::LED_COLOR_YELLOW,CLed::LED_OFF,CLed::LED_ROUND); //spegnimento voce } else { pButton->GetLed()>SetLed(CLed::LED_COLOR_RED,CLed::LED_ON,CLed::LED_ROUND); //accensione voce } acceso = false; acceso = pButton->GetLed()->GetLedMode()==CLed::LED_ON; CString sel = pButton->GetButtonText(); // L' id del bottone selezionato viene assegnato a idsel // entrambi i valori hanno un range da 0 a 244 long idSel = pButton->GetButtonId(); if(Parent) { //In vio alla finestra di un messaggio definito WM_SEND_PARAM // e il secondo messaggio (idSel) relativo all' ID del bottone //cliccato ::PostMessage(Parent>m_hWnd,WM_SEND_PARAM,idSel,(DWORD)this); } } return CListBox::PreTranslateMessage(pMsg); } FILE Led.cpp #include "stdafx.h" #include "resource.h" #include "Led.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE 113 APPENDICE C_2 27 novembre 2008 static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CLed #define TIMER_ID_PING 1 #define TIMER_ID_FLASH 2 // Timer Ping ID // Flash Timer ID /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// CLed::CLed() { m_LedBitmap.LoadBitmap(IDB_LEDS); m_nLedColor = LED_COLOR_RED; m_nLedMode = LED_OFF; m_nLedShape = LED_ROUND; m_Flashing = FALSE; } CLed::~CLed() { VERIFY(m_LedBitmap.DeleteObject()); } BEGIN_MESSAGE_ MAP(CLed, CStatic) //{{AFX_ MSG_ MAP(CLed) ON_WM_PAINT() ON_WM_TIMER() ON_WM_CREATE() ON_WM_ERASEBKGND() //}}AFX_ MSG_ MAP END_MESSAGE_ MAP() ///////////////////////////////////////////////////////////////////////////// // CLed message handlers /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void CLed::OnPaint() { CPaintDC dc(this); // device context for painting DrawLed(&dc,m_nLedColor,m_nLedMode,m_nLedShape); // Do not call CStatic::OnPaint() for painting messages } /////////////////////////////////////////////////////////////////////////////// // Name: SetLed // Description: This method will draw the LED to the specified DC. // // Entry: // CDC *pDC - DC to draw to // // int iLedColor - Where color is defined by: // LED_COLOR_RED // LED_COLOR_GREEN // LED_COLOR_YELLOW // LED_COLOR_BLUE // // int iMode - where mode is defined by: 114 APPENDICE C_2 27 novembre 2008 // LED_ON // LED_OFF // LED_DISABLED // // int iShape - where shape is defined by: // LED_ROUND // LED_SQUARE /////////////////////////////////////////////////////////////////////////////// void CLed::DrawLed(CDC *pDC,int nLEDColor, int nMode, int nShape) { CRect rect; GetClientRect(&rect); // // Center led within an oversized window // if(rect.Width() >= LED_SIZE && rect.Height() >= LED_SIZE) { int nWidth = rect.Width(); int nHeight = rect.Height(); rect.left += (nWidth - LED_SIZE)/2; rect.right -= (nWidth - LED_SIZE)/2; rect.top += (nHeight - LED_SIZE)/2; rect.bottom -= (nHeight - LED_SIZE)/2; } // // Prepare temporary DCs and bitmaps // CBitmap TransBitmap; TransBitmap.CreateBitmap(LED_SIZE,LED_SIZE,1,1,NULL); CBitmap bitmapTemp; CBitmap* pBitmap = &m_LedBitmap; CDC srcDC; CDC dcMask; CDC TempDC; TempDC.CreateCompatibleDC(pDC); srcDC.CreateCompatibleDC(pDC); dcMask.CreateCompatibleDC(pDC); CBitmap* pOldBitmap = srcDC.SelectObject(pBitmap); CBitmap* pOldMaskbitmap = dcMask.SelectObject(&TransBitmap); bitmapTemp.CreateCompatibleBitmap(pDC,LED_SIZE,LED_SIZE); // // Work with tempDC and bitmapTemp to reduce flickering // CBitmap *pOldBitmapTemp = TempDC.SelectObject(&bitmapTemp); TempDC.BitBlt(0, 0, LED_SIZE, LED_SIZE, pDC, rect.left, rect.top, SRCCOPY); // // Create mask // COLORREF OldBkColor = srcDC.SetBkColor(RGB(255,0,255)); dcMask.BitBlt(0, 0, LED_SIZE, LED_SIZE,&srcDC, nMode*LED_SIZE, nLEDColor+nShape, SRCCOPY); TempDC.SetBkColor(OldBkColor); // // Using the IDB_LEDS bitmap, index into the bitmap for the appropriate // LED. By using the mask color (RGB(255,0,255)) a mask has been created // so the bitmap will appear transparent. // 115 APPENDICE C_2 27 novembre 2008 TempDC.BitBlt(0, 0, LED_SIZE, LED_SIZE, &srcDC, nMode*LED_SIZE, nLEDColor+nShape, SRCINVERT); TempDC.BitBlt(0, 0, LED_SIZE, LED_SIZE,&dcMask, 0, 0, SRC AND); TempDC.BitBlt(0, 0, LED_SIZE, LED_SIZE, &srcDC, nMode*LED_SIZE, nLEDColor+nShape, SRCINVERT); // // Since the actual minipulation is done to tempDC so there is minimal // flicker, it is now time to draw the result to the screen. // pDC->BitBlt(rect.left, rect.top, LED_SIZE, LED_SIZE, &TempDC, SRCCOPY); 0, 0, // // House cleaning // srcDC.SelectObject(pOldBitmap); dcMask.SelectObject(pOldMaskbitmap); TempDC.SelectObject(pOldBitmapTemp); VERIFY(srcDC.DeleteDC()); VERIFY(dcMask.DeleteDC()); VERIFY(TempDC.DeleteDC()); VERIFY(TransBitmap.DeleteObject()); VERIFY(bitmapTemp.DeleteObject()); } /////////////////////////////////////////////////////////////////////////////// // Name: SetLed // Description: This method will draw and set led parameters. // // Entry: int iLedColor - Where color is defined by: // LED_COLOR_RED // LED_COLOR_GREEN // LED_COLOR_YELLOW // LED_COLOR_BLUE // // int iMode - where mode is defined by: // LED_ON // LED_OFF // LED_DISABLED // // int iShape - where shape is defined by: // LED_ROUND // LED_SQUARE /////////////////////////////////////////////////////////////////////////////// void CLed::SetLed(int nLedColor, int nMode, int nShape) { m_nLedColor = nLedColor; m_nLedMode = nMode; m_nLedShape = nShape; CDC *pDC; pDC = GetDC(); DrawLed(pDC,m_nLedColor,m_nLedMode,m_nLedShape); ReleaseDC(pDC); } /////////////////////////////////////////////////////////////////////////////// // Name: Ping // Description: This method will turn the led on for dwTimeout milliseconds and // then turn it off. // // Entry: DWORD dwTimeout - Time out in milliseconds /////////////////////////////////////////////////////////////////////////////// 116 APPENDICE C_2 27 novembre 2008 void CLed::Ping(DWORD dwTimeout) { // Return if pinging if(m_bPingEnabled == TRUE) { KillTimer(TIMER_ID_PING); } m_bPingEnabled = TRUE; SetLed(m_nLedColor,CLed::LED_ON,m_nLedShape); SetTimer(TIMER_ID_PING,dwTimeout,NULL); } void CLed::Flash(DWORD dwTimeout) { m_FlashRate = dwTimeout; SetLed(m_nLedColor,CLed::LED_ON,m_nLedShape); SetTimer(TIMER_ID_FLASH,dwTimeout,NULL); m_Flashing = TRUE; } void CLed::SetLedState(BOOL state) { if(state) SetLed(m_nLedColor,CLed::LED_ON,m_nLedShape); else SetLed(m_nLedColor,CLed::LED_OFF,m_nLedShape); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void CLed::OnTimer(UINT nIDEvent) { if(nIDEvent == TIMER_ID_PING) { SetLed(m_nLedColor,CLed::LED_OFF,m_nLedShape); KillTimer(nIDEvent); m_bPingEnabled = FALSE; } else if(nIDEvent == TIMER_ID_FLASH) { if(GetLedMode() == LED_OFF) SetLed(m_nLedColor,CLed::LED_ON,m_nLedShape); else { if(GetLedMode() == LED_ON) SetLed(m_nLedColor,CLed::LED_OFF,m_nLedShape); } KillTimer(nIDEvent); SetTimer(TIMER_ID_FLASH,m_FlashRate,NULL); } CStatic::OnTimer(nIDEvent); } /////////////////////////////////////////////////////////////////////////////// 117 APPENDICE C_2 27 novembre 2008 /////////////////////////////////////////////////////////////////////////////// BOOL CLed::OnEraseBkgnd(CDC* pDC) { // No background rendering return TRUE; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// FILE SelezioneVociMultiple.Dlg // SelezioneVociMultipleDlg.cpp : implementation file // #include "stdafx.h" #include "RODGERS_i385_GUI.h" #include "SelezioneVociMultipleDlg.h" #include <stdio.h> #include "conio.h" #include "windows.h" #include "mmsystem.h" #include "voicedata.h" #include "SLIDERDlg.h" #include "Global.h" //mdr_par #include "ButtonListBox.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif extern unsigned char midi_voci[44]; extern bool acceso; extern int midi_device; ///////////////////////////////////////////////////////////////////////////// // CSelezioneVociMultipleDlg dialog CSelezioneVociMultipleDlg::CSelezioneVociMultipleDlg(CWnd* pParent /*=NULL*/) : CDialog(CSelezioneVociMultipleDlg::IDD, pParent) { //{{AFX_DATA_INIT(CSelezioneVociMultipleDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } void CSelezioneVociMultipleDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSelezioneVociMultipleDlg) // NOTE: the ClassWizard will add DDX and DDV calls here DDX_Control(pDX, IDC_BUTTON_LIST, m_ButtonListBox); //}}AFX_DATA_MAP } BEGIN_MESSAGE_ MAP(CSelezioneVociMultipleDlg, CDialog) //{{AFX_ MSG_ MAP(CSelezioneVociMultipleDlg) ON_MESSAGE( WM_SEND_PARAM, OnSendParam ) //mdr_par (?????) 118 APPENDICE C_2 27 novembre 2008 ON_WM_PAINT() //}}AFX_ MSG_ MAP END_MESSAGE_ MAP() /////////////////////////////////////////////////////////////////////////////////////// ///////////////////SETTAGGIO BIT DELLE VOCI SELEZIONATE//////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// LRESULT CSelezioneVociMultipleDlg::OnSendParam(WPARAM wParam, LPARAM lParam) { long idSel=wParam;//id del pulsante selezionato int param_1,param_2; unsigned char param_3; param_1 = (idSel/7) + 7; // individua il numero del byte (considerando che i primi )su cui andare a scrivere all' interno del midi_voci aray param_2 = idSel%7; // individua il numero del bit da settare ad 1 oppure a 0 relativo alla voce selezionata/deselezionata param_3 = 0;// converte il numero in esadecimale switch(param_2) { case 0: // Bit 0 settato a 1 param_3 = 0x01; break; case 1: // Bit 1 settato a 1 param_3 = 0x02; break; case 2: // Bit 2 settato a 1 param_3 = 0x04; break; case 3: // Bit 3 settato a 1 param_3 = 0x08; break; case 4: // Bit 4 settato a 1 param_3 = 0x10; break; case 5: // Bit 5 settato a 1 param_3 = 0x20; break; case 6: // Bit 6 settato a 1 param_3 = 0x40; break; } if(acceso) { midi_voci[param_1] = midi_voci[param_1] | param_3;//Settaggio del bit corrisondente a 1.Si effettua l' OR col byte corrispondente in modo di lasciare a 0 (o a 1) gli altri bit già eventualmente impostati. } else 119 APPENDICE C_2 27 novembre 2008 { midi_voci[param_1] corrispondente a 0. } = midi_voci[param_1] - param_3;//Settaggio del bit SendSysex();//In vio del messaggio esclusivo midi_voci[] return 0; } ///////////////////////////////////////////////////////////////////////////// // CSelezioneVociMultipleDlg message handlers //////////////////////////////////////////////////////////////////////////// ///Fun zione membro di creazione e inserimento di un nuovo elemento///////// ///all' interno della lista di bottoni dove ognuno identifica una voce///// ///Con la InsertVoce passo la stringa "name" con cui AddItem crea una ////// ///nuova instanza contraddistinta da quel nome a cui assegna (in modo)///// ///progressivo un indice che rappresenta l' idButton. Tale indi ce ///////// //risulta necessario per identificare il bottone spento o acceso dall////// ///utente. //////////////////////////////////////////////////////////////// void CSelezioneVociMultipleDlg::InsertVoce(LPCSTR name) { m_ButtonListBox.AddItem(name,m_curr_indice); m_curr_indice++; } BOOL CSelezioneVociMultipleDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add e xtra initialization here //////////////////////////////////////////////////////// //INSERIMENTO DI TUTTO L' ELENCO COMPLETO DELLE VOCI/// ///in modo progressivo e ordinato rispetto allo//////// ///elenco originario Roland /////////////////////////// /////////////////////////////////////////////////////// m_curr_indice=0; InsertVoce("unused"); InsertVoce("16'Principal"); InsertVoce("Zimbelstern"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); … ... InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("Test/Tune Mode"); InsertVoce("_spare_"); InsertVoce("_spare_"); InsertVoce("_spare_"); //////TOTALE 244 VOCI////////////////////////// ///Disposte in modo progressivo su una lista/// //di altrettanti bottoni individuabili con un / 120 APPENDICE C_2 27 novembre 2008 //id progressivo ////////////////////////////// /////////////////////////////////////////////// return TRUE; } ////////////////////////////////////////////////////// ////FOR MATTAZIONE E IN VIO MESSAGGIO ESCLUSIVO///////// ////Alla pressione di un qualsiasi pulsante della///// //Lista bottoni (sia per accensione sia per ////////// //spegnimento di una voce) viene inviato il ////////// //messaggio midi_voci[44] contenete i bit settati///// //ad 1 delle voci accesse //////////////////////////// void CSelezioneVociMultipleDlg::SendSysex() { int i =0; int sum ,ck ,w ; ck = 0; short offset=0; int midiport; //int rc; HMIDIOUT device; //MIDI de vice interface for hold data for Midi messages; // Calcolo del Checksum sum = 0; for (w=7 ; w <=41 ; w++) { sum += midi_voci[w] ; } sum &= 0x7F; ck = (128 - sum)&0x7F; //APERTURA DEVICE E PORTA MIDI PER L' INVIO DEL MESSAGGIO midiport = midi_device; MIDIHDR* pHeader = new MIDIHDR; pHeader->lpData = (char *) &midi_voci[0]; pHeader->dwBufferLength = 44; pHeader->dwBytesRecorded = 44; pHeader->dwFlags=0; //Apre la MIDI output midiOutOpen(&device, midiport, 0, 0, C ALLBACK_NULL); midiOutPrepareHeader(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); midiOutLongMsg(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); midiOutUnprepareHeader(device , (LPMIDIHDR) pHeader, sizeof(MIDIHDR)); midiOutClose(device); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// 121 APPENDICE C_2 27 novembre 2008 void CSelezioneVociMultipleDlg::OnOK() { // TODO: Add e xtra validation here CDialog::OnOK(); } //////////////////////////////////////////////////// ////BackGround BITMAP/////////////////////////////// //////////////////////////////////////////////////// void CSelezioneVociMultipleDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here CBitmap bitmap; CDC memdc; bitmap.LoadBitmap( IDB_BACK_VOCI_ MULTIPLE ); BITMAP bmp; bitmap.GetBitmap( &bmp ); memdc.CreateCompatibleDC( &dc ); memdc.SelectObject( &bitmap ); CRect rect; GetClientRect(rect); StretchBlt (dc.m_hDC,0, 0, rect.right, rect.bottom, memdc.m_hDC, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY ); } 122 APPENDICE D 27 novembre 2008 - APPENDICE DElenco completo delle voci di resgitro relative al RODGERs i538 STOPS 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 BYTE 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 BIT 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 NOME VOCE unused 16'Principal 16'Flute 8'Principal 8'Voce Umana 8' Gemshorn Celeste II 8' Flute Harmonique 8' Rohrflote 8' Gemshorn 8' Flauto Dolce 8' Flute Celeste 8' Unda Maris II 4' Octave 4' Gedacktflote 2' 2/3 Nasat 2' Superoctave 2' Waldflote 1' 1/3 Quint Sesquialtera II 1' 3/5 Terz IV - V Mixture III Sharf 16' Posaune 8' Trompete 8' Trompette Harmonique 8' Krummhorn 8' French Horn 8' Klarine Harp Carillon Harpsichord Piano Great Tremulant 16' Great to Great 4' Great to Great 4' Flauto Dolce 4' Flauto Celeste spare_ 8' Prestant 8' Gedackt 4' Principal 123 APPENDICE D 27 novembre 2008 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 9 9 9 9 9 9 9 10 10 10 10 10 10 10 11 11 11 11 11 11 11 12 12 12 12 12 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 4' Koppeflote 2' 2/3 Nasard 2' Oktave 1' 1/3 Spitzquinte 1' Sifflote III -IV Sharf 16' Dulzian 8' Krummhorn Harpsichord Positiv tremulant positiv Unisson Off spare_ spare_ 16' Bourdon Doux String 8' Viola Pomposa 8' Viola Celeste 8' Viola Celeste II 8' Muted Viols II 8' Bourdon 8' Flauto Dolce 8' Flute Celeste 8' Flute Celeste II 8' Gamba 8' Echo Gamba 4' Principal 4' Nachthorn 2' 2/3 Nasard 2' Doublette 2' Blockflote 1' 3/5 Tierce 1' Sifflote Sesquialtera II IV Plein jeu III Cymbale Contre Basson 8' Festival Trumpet 8' Trompette 8' Hautbois 8' Voix Humaine (mf) 8' Voix Humaine (mp) 4' Clairon Swell Tremulant 16' swell to swell Swell Unison off 4' swell to swell spare_ spare_ 124 APPENDICE D 27 novembre 2008 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 12 12 13 13 13 13 13 13 13 14 14 14 14 14 14 14 15 15 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 17 17 17 18 18 18 18 18 18 18 19 19 19 19 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 spare_ Pedal Chamade Choir Chamade Great Chamade Swell Chamade Solo Chamade spare_ spare_ spare_ 16' Contre Erzahler 8' Salicional 8' Viola Celeste 8' Hohflote 8' Echo Celeste II 4' Spitzpincipal 4' Lieblichflote 4' Octave Erzahler 2' 2/3 Nasard 2' Octave 2' Flachflote 1' 1/3 Quintflote 1' Sifflote Mouted Cornet III III Mixture 8' Troumpette Harmonique 8' Major Tuba 8' Krummhorn 8' Harpsichord 8' Harp Choir Tremulant 16' choir tp choir Choir Unison off 4' choir tp choir 8' Concert Flute 8' Erzhler 8' Erzhler CelesteII 8' Flauto Mirabilis 8' Gross Gamba 8' Gamba Celeste 4' Doppelflute 2' 2/3 Flute twelfth 2' Piccolo Grand jeu VIII 16' Contre Bombarde 8' Fanfare Trumpet 8' Tube Mirabilis 8' English horn 8' French horn 125 APPENDICE D 27 novembre 2008 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 19 19 19 20 20 20 20 20 20 20 21 21 21 21 21 21 21 22 22 22 22 22 22 22 23 23 23 23 23 23 23 24 24 24 24 24 24 24 25 25 25 25 25 25 25 26 26 26 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 8' Clarinet 4' Clarion Harmonique Carillon Solo Tremulant 16' Solo to Solo Solo Unison Off 4' Solo to Solo _spare_ _spare_ _spare_ 32' Contra Pincipal 32' Contre Bourdon 16' Major Bass 16' Principal 16' Subass 16' Violone 16' Gemshorn 16' Bourdon Doux 16' Erzahler 8' Octave 8' Violoncello 8' Gedackt Pommer 8' Bourdon (sw) 4' Choralbass 4' Gedacktpfeife 2' Kleinflote IV Mixture V Harmonics III Cornet 32' Contre Bombarde 32' Contre Basson 16' Bombarde 16' Pousane (gt) 16' Basson 8' Trompette Oktave Pousane (gt) 4' Clarion 4' Krummhorn _spare_ _spare_ _spare_ 8' Great to Pedal 4' Great to Pedal 8' swell to Pedal 4' swell to Pedal 8' Choir to Pedal 4' Choir to Pedal 8' Solo to Pedal 126 APPENDICE D 27 novembre 2008 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 209 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 26 26 26 26 27 27 27 27 27 27 27 28 28 28 28 28 28 28 29 29 29 29 29 29 29 30 30 30 30 30 30 30 31 31 31 31 31 31 31 32 32 32 32 32 32 32 33 33 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 4' Solo to Pedal 8' Positiv to Pedal 16' Swell to Great 8' Swell to Great 4' Swell to Great 16' Choir to Great 8' Choir to Great 4' Choir to Great 16' Solo to Great 8' Solo to Great 4' Solo to Great 16' Positiv to Great 8' Positiv to Great 16' Swell to Choir 8' Swell to Choir 4' Swell to Choir 16' Solo to Choir 8' Solo to Choir 4' Solo to Choir Positiv Off Choir 16' Choir to Swell 8' Choir to Swell 4' Choir to Swell 8' Solo to Swell Positiv Off Swell 8' Positiv to Swell 8' Positiv to Swell 8' Positiv to Solo Great tuttu on Solo Pipe Tremulant _spare_ _spare_ _spare_ All Pipes Off All Ancillary On Main Tremulant Flute Tremulant Full Great/pedal Ancillary On Choir Pipes Off Choir Ancillary On Main Off Antiphonal On Swell Pipes Off Festival Trumpet FF Swell Ancillary On Zimbelstern spare_ _spare_ 127 APPENDICE D 27 novembre 2008 233 234 235 236 237 238 239 240 241 242 243 244 33 33 33 33 33 34 34 34 34 34 34 34 2 3 4 5 6 0 1 2 3 4 5 6 _spare_ _spare_ _spare_ _spare_ _spare_ _spare_ _spare_ _spare_ Test/Tune Mode _spare_ _spare_ _spare_ 128 27 novembre 2008 129 BIBLIOGRAFIA 27 novembre 2008 9. - BIBLIOGRAFIA 1 Mike Blaszczak, “Professional MFC with Visual C++ 6”, Asad Altimeemy , Sep 2000. 2 Microsoft Press and Microsoft Corporation, “Microsoft Visual C++: C Language Reference (Microsoft Visual C++)”, Paperback, Jul 1996. 3 Richard P. Braden, “C to C++ Conversion: For Users of Microsoft Visual C++ Development System for Windows (Hands-on Windows Programming Series/Richard P. Braden, Bk 8)”, Paperback, 1996. 4 Richard M. Jones, “Introduction to MFC Programming with Visual C++ (Microsoft Technologies Series)”, Paperback, 2000. 5 Harvey M. Deitel, Paul J. Deitel, Tem Nieto, and Edward Strassberger, "Getting Started with Microsoft Visual C++ 6 with an Introduction to MFC (2nd Edition)", Paperback, 2000. 6 Microsoft Corporation, "Microsoft Visual C++ 6.0 Reference Library (Microsoft Professional Edition)", Paperback, 1998. 7 Microsoft Corporation, "Mastering - MFC Development Using Microsoft Visual C++ 6.0", Mondadori Informatica, 2000. 8 Robert Guérin, “L'interfaccia digitale per gli strumenti musicali”, Apogeo, 2003. 9 SWANKE JOHN E., “VISUAL C++ MFC PROGRAMMING BY EXAMPLE”, CMP BOOKS, 1999 10 Jeff Prosise, "Programming Windows® with MFC, Second Edition", Microsoft Press, 1999. 11 M. Mattioli, M. Matteuzzi, "Fare Musica con il PC", Hoepli, 1999). 12 Enrico Cosimi, “Analog & Virtual Analog. Come funziona un sintetizzatore”, Il Musichiere, 2003. 13 Roland Europe S.p.a ,C.D. Gelatt, M.P. Vecchi, “Manuele interno all’ azienda: implementazione MIDI”, 2000. 14 Roland Europe S.p.a , “Manuele interno all’ azienda: caratteristiche tecniche di funzionamento dell’ organo liturgico RODGERs i538”, 2006. 130 27 novembre 2008 131 NOTE 27 novembre 2008 10. - NOTE Tale tesi sperimentale porta in allegato l‟ intero progetto di lavoro sviluppato. La cartella “FILE SORGENTI” contiene tutte le risorse necessarie per avviare l‟ applicativo del GUI EDITOR implementato. Nella sottocartella “Debug” occorre avviare il file eseguibile “Rodgers_i385 GUI.exe” Oppure è possibile ricompilare l‟ intero progetto aprendo con l‟ amiente di svuluppo Visual C++ 6.0 il file Rodgers_i385_GUI.dsw (workspace) (dalla cartella principale). 132 27 novembre 2008 133