Archivi tag: Apple

core_midi_devel

Come mostrare il tempo di una MusicSequence come nel display LCD di GarageBand

Con un nostro precedente post abbiamo già accennato a questo problema: ora approfondiamo le motivazioni tecnico/teoriche che sono alla base.

Il formato ha 4 valori:

– la misura (bar)
– la battuta (beat)
– la nota di sedicesimo nella battuta
– sottodivisioni della battuta (subbeatdivision)

Il framefork AudioToolbox (Core Audio) offre alcuni strumenti per gestire la divisione del tempo a
supporto di una MusicSequence: i principali sono le seguenti:

  • MusicTimeStamp
  • CABarBeatTime

Il primo è un valore temporale la cui unità è la misura musicale (bar): quindi posto 1 il tempo di durata di una misura, tutto è multiplo o sottomultiplo di questo: ne consegue che MusicTimeStamp è un valore in virgola mobile con la necessità di una certa precisione.

La seconda è una struttura utilizzata per le conversioni (vedi le funzioni MusicSequenceBeatsToBarBeatTime e MusicSequenceBarBeatTimeToBeats), ma la scarsa documentazione non fornisce quanto serve a che queste funzioni facciano a pieno il loro dovere.

Il lato oscuro di queste funzione è il parametro inSubbeatDivisor (per la funzione MusicSequenceBeatsToBarBeatTime) o il campo subbeatDivisor nella struttura CABarBearTime (da passare alla funzione MusicSequenceBarBeatTimeToBeats).

Nella soluzione proposta nel precedente post utilizzavamo la risoluzione della sequenza (ppq) come parametro, e sembrava funzionare egregiamente: invero su divisioni di tempo differenti avremmo potuto avere problemi di rappresentazione.

Infatti il subbeatDivisor è un valore in relazione non solo alla risoluzione (in ppq), ma anche alla divisione del tempo corrente (time signature) e per la precisione al denominatore della divisione del tempo, secondo la sequente relazione:

subbeatDivisor = ( (ppq >> 2) << sig_denominator )

dove sig_denominator è espresso come l’esponente di una potenza di 2 che determina il denominatore della divisione temporale corrente. Una breve nota: il memorizzare l’esponente è un meccanismo utilizzato anche nel meta evento di una SMF o di una MusicTrack: la Tempo Track di una MusicSequence è una MusicTrack, pertanto la divisione è registrata in questa mediante meta eventi (di tipo kMIDIMetaEvent_Time_Signature).

La risoluzione è espressa in parti (tick o pulse) di una nota di quarto (ppq o ppqn), ma quando cambia il denominatore della divisione temporale, allora per avere medesima “precisione” occorre ampliare o ridurre questo valore quando esprimiamo una posizione nel tempo al fine di consentire di avere lo stesso numero di parti nel beat (che ha cambiato dimensione per il diverso denominatore della divizione del tempo).

Dato la divisione di 6/8 e una risoluzinone di 240 ppq:

UInt8    tsig_num = 6;
UInt8    tsig_den = 3;        // 2^3 = 8
UInt16    resolution = 240;    // PPQ

allora avremo:

UInt16  subbeatDivisor = ( (resolution >> 2) << tsig_den );

Le dimensioni delle variabili sono conformi a quanto atteso dalle strutture o funzioni in AudioToolbox.

Il penultimo valore da mostrare è il numero della nota di sedicesimo corrente, e come abbiamo visto l’altra volta è ottenibile mediante la seguente:

subbeat / (subbeatDivisor / 4) + 1

dove subbeat rappresente il “pulse” corrente di una nota di quarto, quindi rapportanto alla divisione corrente (subbeatDivisore) se ne prende la quarta parte: 4 * 4 = 16 !

Per eseguire qualche esperimento ecco come scrivere un beat di tanti eventi quanti i tick indicati dalla risoluzione prescelta.

MusicTrack outTrack;
UInt8    tsig_num = 6;
UInt8    tsig_den = 3;
UInt16    resolution = 240;    // PPQ
UInt16  subbeatDivisor = ( (resolution >> 2) << tsig_den );
CABarBeatTime inBarBeatTime;
MIDINoteMessage aNoteMessage;
MusicTimeStamp       outBeats ;

MusicSequenceNewTrack (mainSequence, &outTrack);

aNoteMessage.channel = 1;
aNoteMessage.note = 23 & 0x0f;
aNoteMessage.velocity = 80 & 0x0f;
aNoteMessage.releaseVelocity = 0;
aNoteMessage.duration = 1/960;

for(int i = 0; i < resolution; i++){
inBarBeatTime.bar = 1;
inBarBeatTime.beat = 1;
inBarBeatTime.subbeat = i;
inBarBeatTime.subbeatDivisor = subbeatDivisor;

MusicSequenceBarBeatTimeToBeats(mainSequence, &inBarBeatTime, &outBeats);
MusicTrackNewMIDINoteEvent (outTrack, outBeats, &aNoteMessage);
}

Ovviamente non abbiamo detto della cosa più importante: come leggere il denominatore della divisione in relazione al tempo corrente di riproduzione (o visualizzazione).

Questo sarà oggetto di un altro post che introdurrà la descrizione di un mio progetto di ampliamento del framework AudioToolkit che presto pubblicherò su github.

Per ora basta.

osxbeep

Beep OS X, Beep!

Nell’implementazione dello strato BSD, OSX omette alcuni comandi; poco male se una alternativa è data da un comando nativo: verosimilmente l’astrazione BSD non era in grado di realizzare a pieno il compito. Ma questo non é evidentemente l’unico motivo.

Tra i vari “ports” BSD, Apple ne seleziona solo alcuni, mantenendoli spesso ad una versione “vecchia” (vedi il caso di bash, ancora rimasta alla 3).

Non dovrebbe dunque sorprendere l’assenza del port del comando beep, sebbene sia difficilmente spiegabile in termini tecnologici; al piú si spiega in termini di conformità alle metafore della interfaccia che prevedono altro per la segnalazione di eventi ad utente.

L’ironia del caso vuole che mentre il mondo Linux evolve la sua implementazione del comando beep, anche il port per FreeBSD è stato rimosso in quanto obsoleto, alla buona faccia di chi voglia ancora utilizzarlo.
Ovviamente il nostro interesse per il comando beep nel mondo OS X è indotto dalle cose espresse nei nostri precedenti articoli, ma andiamo per gradi.
Ricercando nella documentazione di sviluppo si trova la disponibilità della funzione beep() nella libreria ncurses (https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/beep.3x.html): questa peró esegue il suono di avviso configurato negli effetti sonori nelle preferenze di sistema.
Quindi, almeno per conformità alle metafore dell’interfaccia, il comando beep poteva essere implementato semplicemente utilizzando questa funzione di ncurses.

Ricercando in Internet (come è nostro solito) una soluzione per implementare un beep su OS X troviamo tante risposte ovvie, ma non quella che cercavamo (ovvio anche questo!)

E’ ovvio che un comando echo (o print) in shell riproduca con un suono il carattere audible alert (\a=ASCII 0x7, carattere di controllo
che deve i suoi natali alla Teletype Model 33 ASR):

echo -e "\a"

ma é anche ovvio che si comporti come prevede l’implementazione della libreria ncurses, come per l’equivalente:

tput bel

con cui si attiva la capability del terminale (che dipendono dalla solita libreria)

Ovvio (benchè meno diretto) la possibilitá di eseguire un beep come istruzione AppleScript:

osascript -e beep

e non ci si può stupire se anche questo metodo (piú che mai OS X) esegue Il suono di sistema previsto per gli avvisi.

Il suggerimento piú esilarante è certamente il seguente:

say beep

benchè abbia un suo fascino.
Dunque è giunto il momento di trovare una risposta in stile Nerdammer e dotare il nostro OS X del comando beep.
Volevamo scegliere come modello la piú evoluta implementazione Linux (di Johnathan Nightingale) invece di quella del port FreeBSD:
questioni pratiche e di tempo ci impongono al momento di implementare la versione FreeBSD: ritorneremo sulla implementazione per evolverla accessivamente.

Questa scelta ci consentirà comunque di utilizzare le nostre “beepsongs” come prodotte dal software descritto nel
precedente articolo (ponendo in modo FreeBSD l’output dello script). Alla fine in realtà è questo che cercavamo!

Per realizzare il nostro progetto ci avvaliamo del framework Core Audio, ed in particolare utilizziamo una Audio Unit di tipo output con definita una funzione callback per il rendering del buffer audio, il che ci consentirà di definire algoritmicamente (dunque dinamicamente) la forma d’onda emessa.

Vogliamo con questo simulare al meglio il circuito analogico del beeper delle schede madri di pc e server, evitando i limiti e la complessità che altri metodi imporrebbero volendo ottenere campo di frequenze e durata di emissione molto ampi.

Come al solito la nostra implementazione é una guida: ci si perdoni quindi l’assenza di ricerca di ottimizzazioni nelle prestazioni (ad esempio l’algoritmo di generazione dell’onda sinusoidale non é il piú efficiente in assoluto).

Potete scaricare il codice sorgente da Git Hub e compilarlo sul vostro OS X come indicato nella documentazione.

Ancora una volta buon ascolto!

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 3)

Fatto il vostro backup?

Allora siamo pronti a partire con lo sviluppo del cuore del nostro progetto: una procedura di creazione di una libreria indipendente e condivisa.

Da questo momento consideriamo la libreria del nostro utente (che è amministratore per ragioni che vedremo in seguito) la libreria master nella nostra architettura, che evidentemente è stata scelta in quanto ragionevolmente é quella che per ragioni anche storiche ha il peso maggiore di media condivisibili nel sistema.

Creiamo una nuova libreria nella Cartella condivisa (con procedura ormai nota se avete letto la documentazione consigliata).SegliLibreria

Apriamola. Configuriamola in modo che non esegua copia degli oggetti importanti (consolidamento) e non mantenga ordinata la libreria: non è il suo ruolo: questa sarà una libreria slave, non dovrebbe avere diritto di gestione (anche se vedremo possibile, entro certi limiti, utilizzando uno strumento di gestione che proporremo alla fine della trattazione).configurazione

Il problema (rilevato ma non risolto dal supporto Apple) sarà trovare il modo di rendere sincrone le due librerie. Vedremo di risolverlo.

Rimuoviamo ora la cartella Music nella nuova libreria (in iTunes Media); al suo posto ci poniamo (muovendola) la cartella Music della libreria master (è la cartella che contiene tutti i media da condividere).

Nella libreria master spoglia della cartella Music (in iTunes Media) creiamo un collegamento (semplicemente tenendo premuto alt+cmd mentre fate un drag&drop) della cartella Music ora mossa nella nuova posizione. Questo garantirà alla libreria master di mantenere la visibilità dei media a lei noti.stato_master

Come amministratore possiamo ora modificare i diritti della cartella Musica appena spostata consentendo la lettura a tutti gli utenti con cui volete condividere il suo contenuto; non autorizziamo la scrittura se vogliamo rendere la libreria puramente slave e non abbia quindi diritto di modificare i media.

Ora è il momento di riaprire la libreria master: tutto è al suo posto, giusto? Se qualcosa non è al posto giusto possiamo verificare la posizione della cartella Music nelle Preferenze ed eventualmente reimpostarla. Se iTunes pensa di dove iniziare la copia dei media potrebbe avviare tale procedura: bloccatela! Non è necessaria. Abbiamo giocato con spostamento e collegamento, andando oltre la procedura Apple che prevede necessita di spazio disco libero pari alla dimensione dei media da spostare. Se tutto è dunque a posto esportiamo la libreria master in formato xml.export

Ora riaprite la nuova libreria costituita in precedenza (slave): questa è vuota pur avendo collocati una cartella Music piena di materiale. Abbiamo detto già che la libreria è tale in quanto raccoglitore di metadati. Ora importate il file xml esportato precedentemente con la funzione “Importa playlist”.import

Chiaro no? L’importazione parla di playlist; Music è una playlist, e iTunes ci sta dicendo che l’importazione riguarderà solo musica.

Finita l’operazione ecco dunque che avrete due librerie identiche (solo nel contenuto migrato) ma che condividono anche fisicamente gli stessi media, ma solo per ciò che attiene alla musica. Per gli altri media la gestione risulterà indipendente. Potrete aggiungere libri, applicazioni o quanto altro alla nuova libreria nei modi di sempre: questo è dunque possibile. Ciò trova anche una soluzione alla gestione ad esempio delle app “legacy” per vecchi dispositivi (basta creare una libreria slave per gestire questi, pur condividendo altri media).


Ma adesso arriva il difficile: aggiungendo nuovi media musicali nella libreria master (che consolida) avremo nuovi oggetti nella cartella media condivisa, ma non li avremo come metadati nella libreria (o nelle librerie) slave (nel file .itl, per intenderci).

Ogni volta che si modifica la libreria master andrebbe aggiornata la slave aprendo iTunes su questa e aggiungendo o rimuovendo oggetti (alterare playlist, ecc).

È evidente come sia necessario automatizzare questa procedura, almeno per quanto riguarda l’aggiunta di nuovi media condivisi (le pllaylist sono raccolte soggettive che lasciamo ai singoli utenti).

Per risolvere questo problema ancora una colta ho scritto un po’ di codice AppleScript (ci ho preso gusto!).

Purtroppo iTunes non espone metodi per salvare tutte le informazioni in un colpo solo, ma solo un metodo add per aggiungere file a partire da un loro alias: questo mi sono fatto comunque bastare. Un unico script che esporti ed importi i metadati attraverso un file di interscambio (un semplice file contenente la lista dei nuovi file) tra le due istanze di libreria.

Andrà attivato prima sulla libreria sorgente (usando la playlist “Aggiunti di recente” quale fonte dell’elenco dei brani da migrare), poi su quella destinazione: questa procedura ci offre la soluzione cercata.

Possiamo eseguire esportazioni successive dalla libreria master ed importare il tutto nella libreria slave in un unico momento.  L’importazione finisce con la rimozione del file di interscambio, iniziando una nuova accumulazione.

Ma non dilunghiamoci in descrizioni ulteriori di implementazione; ecco di seguito il sorgente.

 

[codesyntax lang=”applescript”]

-- 
-- @author Andrea Tassotti


--
-- differenza tra due insiemi
--
on difference(set1, set2)
set differ to {}
repeat with o in set2
if set1 does not contain o then
set differ to differ & o
end if
end repeat
return differ
end difference

--
-- Esistenza file
--
on existsFile(migrationFile)
tell application "Finder"
if not (exists migrationFile) then
return false
else
return true
end if
end tell
end existsFile

--
-- Lettura lista migrazione
--
on readMigrationFile(migrationFile)
-- read
if existsFile(migrationFile) is true then
tell current application
set fileRef to open for access alias migrationFile
set lista to read alias migrationFile as list
close access fileRef
return lista
end tell
else
return false
end if
end readMigrationFile

--
-- Scrittura (overwrite) lista migrazione
--
on writeMigrationFile(migrationFile, lista)
-- write
try
tell current application
set fileRef to open for access alias migrationFile with write permission
write lista to fileRef as list
close access fileRef
end tell
on error
display alert "Errore scrittura:" & (migrationFile as text)
end try
end writeMigrationFile

--
-- Preleva alias da voci di playlist selezionata (no Libreria intera)
-- 
to itemsToMigrate()
tell application "iTunes"
set currentPlayList to view of window 1
set currentPlayListName to name of currentPlayList
if special kind of currentPlayList is none then
return location of every file track of currentPlayList
else
display alert "Possono essere esportate solo playlist effettive" message "Le playlist speciali (Music, Books, Podcasts, ecc) non sono contemplate" giving up after 2
return {}
end if
end tell
return {}
end itemsToMigrate

--
-- Corpo principale del programma
--
on run
set migrati to {}
set migrationFile to ((path to shared documents) as text) & "migration.itpl"
set migrati to readMigrationFile(migrationFile)

if migrati is not false then
-- - -- Verifica se master o slave
display alert "Controllo esistenza elementi in libreria. Questa operazione può durare molto. Attendere" giving up after 1
set primo to first item of migrati
tell application "iTunes"

set libreria to location of every file track of library playlist 1

if libreria does not contain primo then
-- Libreria Slave: importiamo
repeat with aFile in migrati
add aFile
end repeat
-- Rimuoviamo il file consumato
tell application "Finder"
if exists migrationFile then
delete migrationFile
end if
end tell
display alert "Fine importazione"
return
end if
end tell
end if
 

if migrati is false then
set migrati to {}
end if


-- Esportazione controllata
set daMigrare to itemsToMigrate()
if daMigrare is not {} then
-- Calcoliamo la differenza per evitare doppioni
-- poi aggiungiamo comunque quelli migrati per fare append del file
set lista to difference(migrati, daMigrare)
if lista is {} then
display alert "Contenuto migrato in precedenza"
else
writeMigrationFile(migrationFile, migrati & lista)
display alert "Esportazione lista migrazione terminata"
end if
end if
end run

[/codesyntax]

 

In quanto risultato delle nostre analisi, lo script lo rilasciamo liberamente, ma gradiremmo almeno un commento su questo blog se sarete tra quelli che lo copieranno ed utilizzeranno.

Buona condivisione!

Riferimenti

iTunes: How to share music between different user accounts on a single computer

iTunes for Mac: Moving your iTunes Media folder

How to move your iTunes library to a new computer

iTunes: How to re-create your iTunes library and playlists

cocoa_cup

Introduzione a Cocoa – Parte III

KVO, KVC & Binding
Key-Value Observing (KVO) è un meccanismo che consente agli oggetti di ricevere notifiche su cambiamenti di specifiche proprietà di altri oggetti.

E’ basato sul protocollo informale NSKeyValueObserving.

Le proprietà osservate possono essere semplici attributi, relazioni uno-a-uno o uno-a-molti. Nel contesto MVC, questo meccanismo è particolarmente importante in quanto consente agli oggetti della vista di osservare i cambiamenti negli oggetti del modello attraverso il livello controllo.
Essenziale in questo la tecnologia Cocoa Bindings.

Cocoa fornisce molti oggetti della capacità di essere osservati attraverso il protocollo NSKeyValueObserving.

Key-Value Coding
Key-Value Coding (KVC) è un meccanismo per accedere in modo indiretto agli attributi e relazioni degli oggetti usando identificatori stringa.
kvc

Tecnica sottointesa da molti meccanismi e tecnologie Cocoa; particolare rilevanza ha invero nella programmazione di applicazioni Core Data, con uso intensivo di binding, e altro.
Il meccanismo KVC è reso possibile da protocollo informale NSKeyValueCoding.
Due metodi di questo protocollo (valueForKey: e setValue:forKey:) sono particolarmente importanti in quanto consentono di leggere e scrivere valori delle proprietà di oggetti conformi mediante una chiave di accesso nominale (una stringa con il nome della proprietà). NSOject fornisce una implementazione base di questi metodi. Specializzazioni sono possibili.
Implementare questi metodi può significare dotare le classi di proprietà “virtuali”, ossia non corrispondenti a effettivi attributi negli oggetti, ma “calcolati” o comunque gestiti in modo meno diretto (composti da più proprietà, ecc).
Stabilire collegamenti tra oggetti via KVO è una capacità di Interface Builder.

Cocoa e metafore di interfaccia grafica
Cocoa trova soluzioni per le seguenti metafore:

  • gestione finestre
  • menu statici vs dinamici
  • undo e redo
  • drag & drop (vedi protocollo NSTableViewDataSource)

Cocoa, Applicazioni e Interface Builder
Nell’architettura complessiva di un’applicazione Cocoa entra in gioco anche il concetto di “bundle”.
L’applicazione MacOSX è un insieme (bundle, per l’appunto) di risorse: non solo il codice eseguibile, ma anche descrizioni formali dell’interfaccia, immagini, dati, ecc.
La costruzione di questo insieme è demandata a XCode; nello specifico vendono posti insieme il prodotto della compilazione ed in particolare tutto quanto è compreso nella cartella Resource e del progetto corrente.

In particolare in questa cartella è presente un NIB (ad oggi in formato binario, dunque si parla di XIB, ma per quel che segue è del tutto equivalente) un archivio di informazioni prodotto dal programma Interface Builder che, come appare ovvio, riguarderanno la descrizione formale delle componenti di interfaccia grafica.
xib

In realtà la cosa è più generica: nella maggiorparte dei casi Interface Builder gestisce informazioni per l’interfaccia grafica, ma nei casi pratici può qualcosa di più.
Quando si parla di informazioni per l’interfaccia, in realtà si deve parlare propriamente di istanze di oggetti di classi Cocoa (in particolare di ApplicationKit) capaci di astrarre concetti come finestra, menu, casella di testo, ecc.
Interface Builder dunque istanzia (crea) oggetti di interfaccia: la descrizione dell’interfaccia dunque altro non è che un “contenitore di istanze di oggetti”, un concetto che apparentemente sembra una cosa strana ma che è semplicemente spiegabile introducendo il pattern di programmazione della serializzazione di oggetti.
Interface Builder istanzia dunque oggetti Cocoa (ogni icona nella sua finestra che elenca il contenuto di un NIB è una istanza) mentre l’utente programatore “disegna” l’interfaccia; in seguito li “serializza” nel file NIB/XIB.
Il processo di deserializzazione avviene alla partenza della applicazione che si ritroverà dunque già create tutte le istanze degli oggetti introdotti da Interface Builder.
A ciascun oggetto creato dal NIB verrà inviato il messaggio (leggi “verrà eseguito il metodo”) initWithCoder: e awakeFromNib:.
Eventuali specializzazioni di oggetti (classi utente derivare da qualche classe del framework) potranno governare queste fasi implementando una loro versione di questi metodi.
Ogni icona in Interface Builder rappresenta una istanza di una certa classe.

La seguente icona
object
rappresenta ad esempio una istanza di una classe NSObject o di una versione personalizzata (in questo caso il tipo è da indicare esplicitamente nella finestra inspector di Interface Builder).

Questa è una possibilità di generare istanze di oggetti, anche utente, in maniera visuale e con nascita legata all’avvio dell’applicazione, e dunque esistenti “per costruzione”.

Allocazione di oggetti nel codice ObjC
Diversa la sorte di una istanza di oggetto creata da codice utente.
Tipicamente l’allocazione avviene per mezzo del messaggio statico alloc: inviato ad una classe, a cui segue il messaggio init: (semplice o in qualche variante con argomenti) inviato all’istanza ottenuta.

[[NSObject alloc] init];

Qualora le specializzazioni utente delle classi abbiano la necessità di istanziare oggetti a partire da un NIB e a partire da codice devono implementare entrambi i messaggi initWithCoder: che init: ed eventuali varianti.

Oggetti e messaggi
Gli oggetti Cocoa sono progettati per essere immersi in un circuito di invio reciproco di messaggi atti a costituire quella collaborazione tra oggetti che in object-oriented produce l’essenza di un programma.
E’ dunque importante conoscere, specie nell’ottica della specializzazione, significato e ordine di invio di ciascun messaggio.
Raramente sarà il nostro codice specializzato a inviare messaggi; più frequentemente dovremo analizzare e realizzare le specializzazioni in relazione alla catena di messaggi che “l’applicazione” (vista in una ottica di autonomia di funzionamento) invia alle sue componenti, anche in relazione ad interazione con l’utente (eventi).

Continua

cocoa_cup

Introduzione a Cocoa – Parte II

Cocoa e pattern di programmazione
Cocoa è un framework complesso. Nella sua progettazione ed implementazione sono utilizzate molteplici tecniche di progettazione ad oggetti.
Tra queste ricordiamo l’adozione dei seguenti pattern di programmazione:

  • Modello-Vista-Controller (MVC)
  • Delega
  • Delega di data source
  • Observer – KVC, KVO & binding
  • Notifica

L’adozione di tali modelli di programmazione è praticamente obbligatoria: nella adozione più semplice (senza coinvolgere il progetto di nuove classi) si esplicita nell’individuare specifici oggetti (anche realizzati da utente) capaci di essere attori per uno di questi ruoli.
Cerchiamo di specificare brevemente.

Delega
Il processo di delega è un modo che una istanza di oggetto ha per “traslare” l’implementazione di un suo comportamento (metodo) con il metodo omonimo appartenente ad un altro oggetto (anche di tipo diverso).
Il processo di delega è una forma di composizione (composizione dei comportamenti) tra oggetti.
delega

Una classe che prevede processo di delega per i suoi oggetti avrà un attributo indicante l’oggetto delegato (delegate). Per classi gestite con Interface Builder è sufficiente collegare l’attributo delegato di un oggetto ad un altro oggetto per stabilire la possibilità che il processo di delega abbia luogo.
Nel processo di delega, uno stesso oggetto delegato può essere delegato ad implementare più di un compito (metodo).
Vediamo come esempio quali metodi vengono delegati dalla classe NSTableView ad un suo oggetto delegato:

  • tableView:willDisplayCell:forTableColumn:row:
  • tableView:shouldSelectRow:
  • tableView:shouldSelectTableColumn:
  • selectionShouldChangeInTableView:
  • tableView:shouldEditTableColumn:row:

Modello-Vista-Controllo (MVC – Model/View/Controller)
Pattern di programmazione storico introdotto da SmallTalk e comune a tutti gli approcci ad oggetti alle interfacce grafiche (Java compreso).
Si fonda sulla separazione logica ed implementativa della rappresentazione dei dati dell’applicazione (modello), della componente visuale di interfaccia utente (vista) e della componente che coordina i due e costituisca tutto quanto è eventualmente procedurale nella applicazione (controller).
Una applicazione Cocoa è fortemente conforme al pattern MVC.
Non a caso il framework introduce classi e derivazioni da queste per rappresentare una implementazione delle componenti conforme al modello, con forme e comportamenti prestabiliti a governare le più disparate esigenze.
Per Vista e Controllo abbiamo:

  • NSView
  • NSController

Per il modello si possono usare tutte le classi conforme al modello “container” o “collection” quali:

  • NSArray
  • NSSet

oppure la classe NSManagedObject con l’adozione del framework Core Data (>= 10.4).
mvc

Composizione
La composizione è una tecnica di ADT (Abstract Type Definition) per la composizione delle informazioni degli oggetti. In progettazione object-oriented viene indicata come avente maggiori vantaggi dell’ereditarietà (si prefersce in sostanza descrivere i problemi con relazione ha-un invece che è-un, per ridurre la complessità della gerarchia di derivazione o eventuali effetti collaterali).
Questa filosofia è adottata anche da Cocoa.
Alcune istanze di classe dell’ApplicationKit esistono solo come elementi parte di classi più grandi come “aiuto all’implementazione”.
Un esempio classico è la NSTextFieldCell, che incapsulata in unica istanza in una NSTableView costituisce l’elemento che “disegna” tutte le celle della tabella, riutilizzata più volte dall’algoritmo di disegno della classe NSTableView.
In questo caso si ha anche una scelta architetturale particolare per cui non si incapsulano n istanze di celle per quante sono gli elementi della tabella (aumento di complessità), ma una sola istanza che non è un vero oggetto grafico (non deriva da NSView), ma è uno strumento che implementa il comportamento grafico del “disegnatore”.

Protocolli
Un protocollo è un modo alternativo alla derivazione per rendere possibile l’intoperabilità di classi molto differenti che devono rispondere ai medesimi messaggi.
L’insieme dei messaggi in comune, invece di essere una interfaccia, diviene un protocollo.
Un protocollo diviene così una interfaccia che qualsiasi classe può adottare in maniera indipendente dal progetto.
Esistono due tipi di protocolli in objc: formali ed informali.
Un protocollo informale è sostanzialmente una categoria di NSObject. Contrariamente ad una interfaccia, un protocollo informale non è da implementarsi obbligatoriamente. Prima che sia invocato un metodo, l’oggetto chiamante controlla se l’oggetto destinatario del messaggio implementa il metodo. Dato che i metodi opzionali di un protocollo sono stati introdotti solo nel Objective-C 2.0, i protocolli informali sono stati essenziali nel modo in cui le classi di Foundation e AppKit hanno implementato la delega..

NSObject protocols:

  • autorelease
  • class
  • conformsToProtocol:
  • description
  • hash
  • isEqual:
  • isKindOfClass:
  • isMemberOfClass:
  • isProxy
  • performSelector:
  • performSelector:withObject:
  • performSelector:withObject:withObject:
  • release
  • respondsToSelector:
  • retain
  • retainCount
  • self
  • superclass
  • zone

Un protocollo formale dichiara una lista di metodi che ci si aspetta utilizzata da una classe utente. Hanno una propria sintassi per dichiarazione, adozione e controllo di tipo.

protocollo_formale

Esempio particolare di protocollo: NSTableViewDataSource
Il metodo setDataSource: di una NSTableView vuole un oggetto conforme ad protocollo informale NSTableVewDataSource:

- (void)setDataSource:(id<NSTableViewDataSource>)anObject

L’oggetto che viene impostato come “sorgente dati” sarà in realtà un oggetto sottoposto a processo di delega che risponde al protocollo informale richiesto.

In particolare delega i seguenti compiti (che sono dettati dal protocollo):

Delegate Message Significato
numberOfRowsInTableView: Lettura valori
tableView:objectValueForTableColumn:row: Lettura valori
tableView:setObjectValue:forTableColumn:row: Impostazione valori
tableView:acceptDrop:row:dropOperation: Dragging
tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes: Dragging
tableView:validateDrop:proposedRow:proposedDropOperation: Dragging
tableView:writeRowsWithIndexes:toPasteboard: Dragging
tableView:sortDescriptorsDidChange: Ordinamento

Osservatori e Notifiche
L’Observer pattern è un design pattern utilizzato per tenere sotto controllo lo stato di diversi oggetti.

Observer
Un observer definisce una dipendenza uno-a-molti tra oggetti al fine di notificare e aggiornare automaticamente tutti gli oggetti dipendenti al cambiamento dello stato dell’oggetto osservato. Il pattern Observer è essenzialmente un modello “pubblica e sottoscrivi” in cui un soggetto e i suoi osservatori sono non strettamente accoppiati. La comunicazione può avvenire tra gli osservatori e gli osservati senza che l’uno sappia molto dell’altro.

Notifications
Il meccanismo di notifica in Cocoa implementa un sistema diffusione messaggi uno-a-molti in accoro con il Observer pattern. Gli oggetti di un programma aggiungono se stessi o altri oggetti ad una lista di osservatori per una o più notifiche, ciascuna delle quali viene identificata da una stringa (nome della notifica).
notification
Gli oggetti delegati vengono automaticamente registrati per ricevere messaggi corrispondenti alle notifiche.

Esempio: delegato di una NSTableView. Questi messaggi informano il delegato quando la selezione è stata modificata o quando una colonna viene mossa o ridimensinoata:

Delegate Message Notification
tableViewColumnDidMove: NSTableViewColumnDidMoveNotification
tableViewColumnDidResize: NSTableViewColumnDidResizeNotification
tableViewSelectionDidChange: NSTableViewSelectionDidChangeNotification
tableViewSelectionIsChanging: NSTableViewSelectionIsChangingNotification

 

Continua

EALogoBlack

Archeologia informatica: Interchange File Format

In questo mondo moderno in cui il numero dei formati file proprietari si moltiplica, fa tenerezza pensare ad un lontano passato (era il 1985) quando un colosso dell’informatica ludica (Electronic Arts) definiva uno standard per un formato file universale, tentando così di frenare quello che già all’epoca era una continua corsa alla Babele dei formati.

L’IFFInterchange File Format

L’intento era quello di definire un modello di file capace di contenere diverse componenti multimediali per i suoi software: immagini, suono, animazioni, test, ecc.

Questo standard non è stato più mantenuto e portato avanti, ma molte sono state le influenze e le similitudini che da questo sono state derivate; infatti pur non avendo avuto un futuro autonomo, lo standard è ancora enormemente diffuso (nei suoi eredi e direttamente).

Strutturalmente il file è costruito sul concetto di chunk, ossia un corpo dati identificato da un tipo (una parola a 32 bit di caratteri alfanumerici) e una dimensione (una seconda parola a 32 bit). Se la prima parola è da coniderarsi come sequenza di 4 caratteri alfanumerici interpretabili umanamente con un simbolo, la seconda parola sono un valore a 32 bit in formato big-endian.

Nel mondo Macintosh quei quattro caratteri sono noti come OSType, una parola a 32 bit che ricorre in molte strutture del sistema operativo e in qualche modo ispirate agli stessi principi del’IFF (difficile dire chi ha ispirato chi, data la quasi contemporaneità della nascita del Macintosh con IFF); nel mondo Windows gli sviluppatori chiamano FourCC questi stessi caratteri alfanumerici distintivi di una informazione.

L’uso dell’ordinamento di bit big-endian deriva dalla costruzione di questo standard su architetture Motorola 68000, il cuore dei sistemi Commodore Amiga e Apple Macintosh per i quali Electonics Arts stava sviluppando diverso software di successo (oltre a giochi, programmi di grafica come Deluxe Paint, e di musica come Deluxe Music Construction Set, ecc).

DMCS fu il primo programma capace di sfruttare la capacità di playback a 4 voci con campionamento 8-bit 22Khz dei sistemi Amiga, portando la tecnologia dei campionamenti alla portata di tutti in un epoca in cui una tecnologia analoga era appannaggio di un dispositivo professionale da circa $1700 (vedi Ensoniq Mirage).

La logica di costruzione del formato assomiglia alla codifica TLV (Type/Lenght/Value) dei protocolli di comunicazione:

  • semplici funzioni di analisi per ricercare componenti
  • elementi facilmente trascurabili durante la lettura
  • ordine di apparizione degli elementi nel file non importante
  • formato binario delle informazioni

La definizione del formato ha una certa ricorsività basata su tre strutture basilari: il ‘FORM’, il ‘LIST’ e il ‘CAT ‘. Tre identificatori di altrettanti chunk contenitori che rappresentano un formato specifico (identificato a sua volta da un altra parola 32-bit sempre simbolica), una lista di formati coordinati e un gruppo di formati non meglio correlati.

Lo standard prevedeva in qualche modo un pubblico registro degli identificatori dei chunk dati al fine di evitare collisioni (Commodore ne tenne uno di tutti i formati introdotti da software per le sue macchine); il registro doveva tener conto anche delle specifiche sul dato, al fine di rendere effettiva l’interoperabilità dei sistemi che adottassero il formato.
I vari chunk “registrati” in realtà spesso incorporavano dati destinati ad architetture specifiche e/o comunque molto legati a software o a sistemi operativi (è il caso di immagini HAM – Hold & Modify, una specifica tecnologia per schede grafiche Amiga): questo è certamente contradditorio rispetto alla possibilità di interscambio, ma di contro la definizione pubblica dei chunk poteva comunque consentire l’implementazione di una qualche forma di traduzione.

In ogni caso interpretare un formato file non significa poter gestire i dati: questo è vero anche nel caso dell’XML, pertanto IFF non si trova da solo in questo !

Una particolare implementazione di IFF che è sopravvissuta al tempo è il formato AIFF (Audio Interchange File Format) di Apple.
Un altro erede diretto del IFF è certamente il RIFF (Resource Interchange File Format) di Microsoft e IBM: RIFF è esattamente un IFF  che usa una endianess little-endian (per processori Intel). E’ il formato dei file AVI e WAV.

Tra le similitudini possiamo invece annoverare:

  • il resource fork dei sistemi Macintosh Classic ha una analoga struttura a chunk e identificazione tramite ID alfanumerici a 32 bit (ma anche alcune differenze)
  • il formato SMF (Standard Midi File, definito dalla MIDI Manufacturers Association) è un IFF mancato; quello che manca è il container FORM. Per il resto rispetta esattamente la stessa filosofia, tant’é che Microsoft ha introdotto il chunk RMID per incapsulare un SMF in un formato RIFF.

Molte idee dell’IFF rimangono ancora oggi in formati come XML (ad esempio la possibilità di saltare intere componenti di informazioni quando non se ne conosce il significato o non si ha modo di interpretarli), così come l’idea di marcare sezioni di informazioni con identificatori di 4 caratteri alfanumerici e lunghezza (vedi estensione ID3v2 al formato MP3).

Benché la sua definizione legata ad architetture a 32 bit ne impedisca l’utilizzo per dimensioni di dati/file superiori ai circa 4GB, la sua meccanica di funzionamento lo colloca ancora ai primi posti nella semplicità d’uso e implementazione.
Il concorrente diretto per gli stesi scopi è certamente l’XML (almeno questa è la vulgata): questo ha il vantaggio della portabilità (come IFF), ma l’implementazione di un buon parser XML non è meno onerosa (anzi!) di un semplice e completo parser IFF. E IFF è più rapidamente interpretabile e compatto nelle dimensioni.

Mi domando perché le buone idee spesso vengono abbandonate a favore di presunte novità, mode, interessi diversi ! Strane domande mi faccio !

cocoa_cup

Introduzione a Cocoa – Parte I

Con questo post vogliamo iniziare una serie di articoli più astratti sulla programmazione Cocoa, quasi un tutorial.

La programmazione in ambito nativo MacOSX si fonda su due pilastri: Objective-C e Cocoa Framework. Questo documento non si prefigge lo scopo di coprire lo studio particolareggiato di queste tecnologie (non potrebbe), ma di fornire un rapido accesso allo sviluppo software MacOSX.
Per poter governare la programmazione in ambito MacOSX occorre conoscere inoltre gli strumenti XCode, ivi compresa la componente Interface Builder per lo sviluppo delle interfacce grafiche (prima di Xcode 4 era una applicazione esterna).
La programmazione Objective-C è una programmazione multi-paradigma: l’Objective-C (da adesso objc) si fonda sul linguaggio C (programmazione procedurale) e costruisce su questo una sovrastruttura per il paradigma Object-Oriented (con l’introduzione di specifiche sintassi).
Per l’implementazione del paradigma ad oggetti, l’objc introduce un runtime environment che costituisce un “motore” (utilizzato da ogni applicazione costruita in objective-c) atto a realizzare la metafora dei messaggi e la componente dinamica del modello ad oggetti del linguaggio (polimorfismo, dynamic binding, ecc).

L’adozione del modello ad oggetti viene supportata da alcuni framework: primo tra tutti Foundation (Origina da NextStep Foundation library), responsabile della costruzione organica del modello a partire dalla classe NSObject.

  • Tutte le classi di questo framework hanno nomi con prefisso “NS”, che intende ricordare l’origine in NextStep.

Nel contesto grafico MacOSX il principale framework adottato è Cocoa: questo estende Foundation con l’adozione di tutto un insieme di framework tematici. Anche in questo caso le classi hanno nomi con prefisso “NS”.
Il corollario di framework che costituiscono Cocoa si posizionano su diversi livelli di una gerarchia di astrazione delle componenti del S.O. e altro: ogni framework è dato per semplificare e riutilizzare un progetto relativo a situazioni specifiche (Core Audio, Core Graphics, Core Animation, Core Data, ecc).

  • Le classi appartenenti a questi framework possono essere riconosciute sempre attraverso la convenzione dei prefissi, ma non in senso rigoroso (es. quelle Core Data hanno esempio prefisso “NS”, mentre Core Graphics ha “CG”).
  • La forma dei prefissi è legata a fattori storici e gerarchici.

Tutti questi framework sono da riferirsi ad uno SDK relativo alla versione software del sistema operativo: un SDK ha un numero di versione pari al numero di versione del S.O. (es. SDK per Tiger è 10.4, per Leopard è 10.5, per Snow Leopard 10.6, per Lion 10.7, Mountain Lion 10.8, Mavericks 10.9).

API Compliance
Un’applicazione può essere compilata solo per uno specifico SDK (selezione che avviene nella configurazione del “target” in XCode); si può adottare una programmazione parametrica (ovvero con direttive condizionali di preprocessing) al fine di modellare il codice per applicare modifiche atte a compilare per specifico SDK e modulare le eventuali differenze rispetto ad un altro.

#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
// codice per Leopard e oltre
#else
// codice pre-Leopard
#endif

ABI Compliance
Un’applicazione può essere invero compilata per architetture differenti (PPC vs Intel): questo è l’adozione dei concetti di Universal Binary.

  • In questo caso Lion ha creato uno spartiacque: la soppressione di Rosetta e l’impossibilità di sviluppare codice per PPC ha impedito lo sviluppo in un unico progetto per più piattaforme.

Cocoa e GNUStep
GNUStep è una versione “open” di NextStep, e per questo ha molte similitudini con Cocoa.
Un codice realizzato in Cocoa è “riutilizzabile” in contesto GNUStep. Gli strumenti di sviluppo di GNUStep sono però gli originali Project Builder e Interface Builder di NextStep e divergono dunque per alcuni aspetti dalle novità introdotte da MacOSX e da XCode.
Riutilizzare il codice è possibile ma occorre riportarlo all’interno di un progetto GNUStep.
Il vincolo maggiore è il livello di compatibilità rispetto ai vari SDK disponibili nel mondo MacOSX: attualmente GNUStep è paragonabile a Panther, ossia 10.3. Un software calibrato per 10.4 oppure adottante framework particolari come Core Data, Core Audio o Core Animation, difficilmente saranno “portabili” nel mondo GNUStep.
Lo saranno solo se soluzioni a quelle differenze vengano prodotte dal progetto GNUStep stesso oppure in modo autonomo da chi sviluppa il software.

Cocoa runtime environment
Possiamo parlare di un vero e proprio runtime environment anche nel caso del framework Cocoa.
Il concetto di “applicazione” è proprio del framework, il quale offre un suo modello e una implementazione. In particolare il progetto riguardante il concetto di applicazione è parte dello specifico framework ApplicationKit.
Possiamo così leggittimamente parlare di “applicazioni Cocoa” contro “applicazioni BSD“, benchè in entrambi i casi si possa adottare objc e Foundation.
Cocoa suddivide il concetto di “applicazione” in due possibili modelli alternativi:

  • applicazione
  • applicazione basate su documento

Applicazione
Una applicazione incentrata su un ciclo di eventi che pongono in relazione gli elementi grafici presenti (finestre, menu, ecc).

Applicazione basata su document
Una applicazione il cui fulcro è un “gestore di documenti”; ciascun documento ha una propria finestra di interfaccia e una propria base dati di riferimento. Ciò che viene implementato in riferimento ad un documento astratto viene replicato (nei comportamenti) per ciascun docuento “aperto”.

Entry point di un programma Cocoa
Proprio in ragione della presa in carico da parte del framework del progetto “applicazione”, non esiste un “corpo principale del programma” come in altri ambienti di sviluppi.
Per tutte le applicazioni Cocoa si ha un unico “punti di inizio del programm” che è l’esecuzione della funzione NSApplicationMain(). Questo viene precostruito dal progetto modello in XCode.

int main(int argc, char *argv[])
{
return NSApplicationMain(argc,  (const char **) argv);
}
  • Ovviamente è data la possibilità di interferire con le caratteristiche base dell’applicazione (intesa come flusso principale di programma) attraverso alcune classi di ApplicationKit (utilizzando metodi e attributi di classe specifici) e implementando una versione ad-hoc della funzione NSApplicationMain().

Classi che riguardano l’applicazione sono: NSApplication, NSBundle, NSApp.
La complessità dell’intera architettura è tale che non può essere ripercorsa da questo documento introduttivo.

Continua.

articolo_itunes

Esportare una playlist iTunes su chiavetta USB

Per tutti coloro che utilizzano al meglio (e felicemente) iTunes e la sua libreria organizzata e consolidata, è difficile pensare ad una organizzazione del materiale audio senza iTunes.

Molti però tendono ancora a gestire i file multimediali in maniera autonoma, specie chi di questa gestione automatizzata, comoda ed integrata non ha mai sentito parlare (non è nel loro DNA, diciamo). Va bé! ci sono forme alternative di pensiero che dicono che noi utenti Apple invero siamo indottrinati; fate come volete. Tant’é. Noi viviamo sereni.

Però il problema nasce quando occorre integrarsi con il mondo a “gestione manuale”.
Se qualcuno ci chiede una copia della nostra playlist (per ascoltare le nostre canzoni autoprodotte rigorosamente con il nostro GarageBand !!! ovviamente !!! ;-), potrebbe essere difficile raccogliere in giro per la cartella “iTunes Music” tutti i file e copiarli diciamo su una chiavetta USB.

Ecco che ci viene incontro un piccolo frammento di codice AppleScript da me creato in pochi minuti (potenza del linguaggio):

tell application "iTunes"
    repeat with aTrack in (tracks of user playlist "Prova")
        set theAlias to (get location of aTrack)
        set theAlbum to (get album of aTrack).
        tell application "Finder"
        try
                duplicate theAlias to folder "Macintosh HD:Users:andrea:Desktop:Prova"
        on error number errn
            if errn = -15267 then
                make new folder at folder "Macintosh HD:Users:andrea:Desktop:Prova" with properties {name:theAlbum}
                duplicate theAlias to folder ("Macintosh HD:Users:andrea:Desktop:Prova:" & theAlbum)
            end if
        end try
        end tell
    end repeat
end tell

Rozzamente (non abbiamo prodotto una interfaccia per richiedere sorgente, ossia playlist, e destinazione, ossia un folder) ma semplicemente questo script cercherà per noi tutti gli originali indicati da una playlist e li copierà in un folder da noi indicato: qualora il file dovesse collidere con uno già presente (ricordiamo che i nomi di file gestiti con libreria organizzata da iTunes sono fatti del numero di traccia e del titolo, quindi possono sussistere casi di omonimia in assenza di album) allora crea un subfolder con il titolo dell’album.

Potrete modificarlo a piacimento, magari creando direttamente la gerarchia di folder che più vi aggrada, introducendo altre meta informazioni, se necessario.

Buon ascolto!