Archivi tag: OSX

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.

cocoa_cup

OSX e le risorse dinamiche

Molte applicazioni di piccole dimensioni nascono e muoiono con il solo contributo delle risorse definite nel nib/xib MainMenu contenuto nel bundle dell’applicazione.
Ma la disponibilità di risorse nella programmazione macOS è molto più articolata e al crescere della dimensione e funzionalità di una applicazione questa disponibilità verrà prima o poi a convergere nella nostra applicazione.

Vediamo di dare una guida ragionata all’uso di questa disponibilità di risorse.

Le diverse forme di disponibilità vanno scelte in ragione della tipologia di risorsa che intendiamo dinamizzare ed in ragione di quante istanze di questa abbiamo bisogno.

Una ulteriore scelta potrebbe essere fatta in ragione del budle che contiene le risorse che ci interessa utilizzare: potremmo infatti considerare l’uso di risorse collocate diversamente dal bundle dell’applicazione; ma per ora tralasciamo questa possibilità.

Iniziano con il distinguere in base alla dimensione della astrazione che intendiamo recuperare dinamicamente: una intera finestra o una vista custom.

All’interno di questa distinzione possiamo trovare una ulteriore dicotomia in relazione al numero di controller collegati alle viste: un solo controller o più controller.

Pur potendo il nib/xib contenere istanze di oggetti controller, quello che a noi interessa è l’istanza (o istanze) di controller che richiedano il caricamento dinamico di risorsa, e non quindi controller che subiscano essi stessi il meccanismo di caricamento dinamico stesso.

Dal punto di vista del nib/xib esiste un riferimento speciale ad un oggetto che è inteso sempre disponibile per le viste ivi contenute perché responsabile del caricamento dell’intero nib/xib: questo riferimento è il File’s Owner. Si può considerarlo spesso un oggetto di tipo controller o comunque un proxy di qualche genere a modello o azioni (a cui collegare le viste).

In virtù di questo il framework AppKit provvede a fornirci due implementazioni di controller con cui governare (prima delle altre cose accessorie in grado di fare) il caricamento dinamico di una intera finestra o di una specifica vista; sono: NSWindowController e NSViewController. L’implementazione di base è spesso sufficiente allo scopo del caricamento dinamico, comunque non è infrequente che si possa necessitare di una loro estensione per compiti poi specifici.
Perché le viste all’interno del nib/xib abbiano accesso ad un controller o model validi è sufficiente fare affidamento al File’s Owner, definendolo nel nib/xib come di classe scelta per il controllo del caricamento della risorsa rappresentata nel nib/xib (quindi NSWindowController o NSViewController, oppure loro derivazioni).
A questo punto una istanza di NSWindowController potrà tramite -(id)initWithWindowNibName:(NSString *)windowNibName
consentire il recupero di una intera finestra da un nib/xib (indicato per nome) con tutte le azioni, outlet o binding collegate a File’s Owner (e quindi NSWindowController) o suoi elementi.
La finestra stessa è ottenibile dall’istanza di NSWindowController mediante metodo -window, così come sono ottenibili altri strumenti propri del controller.

Esempio

NSWindowController * aWindowController = [[NSWindowController alloc] initWithWindowNibName:@"myWindow"];

[aWindowController showWindow: self];

Allo stesso modo un NSViewController potrà farsi carico del caricamento di un nib/xib che definisce una vista attraverso il metodo:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

L’idea di questo controller (contrariamente al precedente, orientata alla finestra/documento) è maggiormente sviluppata al supporto per le viste contenute nel nib/xib nella definizione di legami con il modello; a tale scopo fornisce una proprietà generica representedObject per consentire un facile collegamento tra il nib e un oggetto (modello) che ancora non è noto in fase di caricamento del nib/xib.

Esempio

NSViewController *viewCtlr = [[[NSViewController alloc] initWithNibName:@"myView" bundle:nil] autorelease];

[[window contentView] addSubview: [viewCtlr view]];

Questi due controller risolvono il problema di una vista più o meno complessa governata da un proprio controller. Ma come poter gestire istanze multiple di viste custom afferenti al medesimo controller?

Una derivazione da NSView può implementare la seguente:

- (BOOL)loadFromNib:(NSString *)nibName bundle:(NSBundle *)bundle controller:(id)controller
{
/*
 * Codice liberamente tratto da "Resource Programming Guide" di Apple
 *
 */

    NSNib *		aNib = [[NSNib alloc] initWithNibNamed:@"ReusableView" bundle:bundle];

    NSArray *	topLevelObjs;

    if (![aNib instantiateNibWithOwner:controller topLevelObjects:&topLevelObjs]) {

     NSLog(@"Warning! Could not load nib file.\n");

     return NO;

    }

    // Ricerca delle viste

   for(id obj in topLevelObjs) {

     if ( [obj isKindOfClass: [NSView class]] )

        [self addSubview: obj];

}

   // Release the raw nib data.

    [aNib release];

    // Release the top-level objects so that they are just owned by the array.

    [topLevelObjs makeObjectsPerformSelector:@selector(release)];

   // Do not autorelease topLevelObjs.

    return YES;

}

Esempio

ReusableView * reusable1  = [[ReusableView alloc] initWithFrame:NSMakeRect(0, 0, 100, 95)];

[reusable1 loadFromNib:@"ReusableView" bundle:nil controller: controller];

[reusable2 loadFromNib:@"ReusableView" bundle:nil controller: controller];

Un limite di questa implementazione è l’assenza di un meccanismo automatico per definire la catena dei risponditori sommando il contributo di ciascuna vista (e poter passare da una a l’altra, che detengono così catene indipendenti).
Per semplificare rapporto con model implementare una proprietà representedObject similmente a NSViewController.

Va da se che applicare questa forma di caricamento quando si collegano poi alle viste istanze di controller differenti è una inutile ripetizione della implementazione già in essere con NSViewController.
Con questa ultima osservazione chiudiamo la nostra guida al caricamento dinamico delle risorse in macOS.

Alla prossima.

osx-vb-cpuid

OSX+VirtualBox+cpuid

Il mondo degli smanettoni è a volte omissivo, a volte ignorante. L’omissivo lo ammetto quando serve ad impedire a giovani sprovveduti di replicare tecniche pericolose o illegali; l’ignoranza è altra cosa. Ma nel chiacchiericcio internet, persa la fonte originale (forse solo omissiva), dopo molti “copia e incolla” molti post diventano un crogiolo di ignoranza.

Non so se questo sia il caso, ma non si trova molto in giro che spieghi le relazioni profonde tra le cose oggetto di questo post.

Cpuid è una istruzione assembly Intel che consente di determinare tipo e caratteristiche della cpu (ovviamente solo per architetture Intel/AMD).

L’insieme di informazioni risultanti è usato da sistemi operativi e software applicativi per determinare certi comportamenti.

Nel caso di  Oracle VirtualBox vengono utilizzate per determinare se il sistema host può eseguire il codice del sistema guest; ma VirtualBox usa una versione “edulcorata” di queste informazioni anche per mostrare al guest la CPU virtuale che intende fornire. Tipicamente fa questo clonando le informazioni cpuid della CPU reale modificando alcune caratteristiche (per ragioni di isolamento e protezione).

VirtualBox fornisce uno strumento per interrogare la CPU host rispetto al cpuid:

VBoxManage list hostcpuids

Consente anche un certo grado di intervento nell’immagine virtuale di queste informazioni che fornirà al sistema guest:

VBoxManage modifyvm --cpuidset

ma attenzione: potranno essere arbitrariamente alterate da VirtualBox per le ragioni di isolamento e protezione di cui sopra.

Come si relaziona tutto questo con OSX?

Parliamoci chiaro: su internet la maggior parte dei post che trattano questi argomenti lo fanno spiegando di installazioni OSX su hardware e OS “non Apple“: questo non è consentito dalle licenze d’uso.

Nessuno tratta argomenti più “Apple” come la sperimentazione di nuove versioni di OSX prima in virtuale (tipico approccio conservativo per chi personalizza pesantemente il sistema e deve sapere se tutto funzionerà correttamente, se software particolari e magari costosi funzioneranno ancora, ecc). Nessuno parla mai dello sviluppo “legacy”, quindi della necessità di avere una vecchia versione di OSX su cui sperimentare il software.

VirtualBox per OSX è lo strumento ideale per tutte queste cose.

Grazie alla manipolazione delle informazioni cpuid Potremo testare anche per hardware differente (sempre Apple, ma meno recente).

Ma OSX non ha uno strumento nativo per leggere il cpuid; quindi se non vogliamo installare VirtualBox su tutte le macchine da cui estrarre le informazioni cpuid  ci serve uno strumento alternativo.

È quello che propongo con questa mia semplice implementazione in C:

http://www.github.com/andrea-tassotti/cpuid

Il codice deriva da un sorgente open source di Apple (con licenza non GPL), e produce un output come quello di VBoxManage.

Il vantaggio di questo codice è che potrà essere eseguito all’interno del sistema guest per verificare che le informazioni impostate in configurazione della VM siano come previste (salvo modifiche applicate da VirtualBox).

Spero possa esserVi di qualche aiuto per comprendere certi meccanismi.

Alla prossima

IMG_3426.PNG

Big-G consuma risorse sul vostro Mac? 

Era un po’ di tempo che avevo notato che il mio Mac, quando non online, faticava a completare l’avvio della sessione utente.
Mi sono risposto troppe volte che forse avevo esagerato con software e servizi installati.

Troppe volte.

Cosè ieri sera ho deciso di fare il sistemista (a casa si evita sempre di farlo, chissà perchè) e ho guardato il log di sistema appena avviato ed entrato in sessione utente: sorpresa!

Scoperto il colpevole.

Non tutti sanno (e anche io lo ignoravo) ma dovrebbero sapere che quando si installa una qualche applicazione Google (Chrome, Earth, ecc) viene installato in sordina un altro software: GoogleSoftwareUpdate. E io avevo installato Chrome !!!

Questo software si pone come demone all’avvio di sessione e contatta i server Google per cercare nuove versioni dei software. Nulla di male se non per due cose:

  • non è chiaramente visibile all’utente
  • non contempla il non essere online

Proprio questo ultimo punto causa il maggior fastidio.

Infatti l’aggressività del software in configurazione base è tale per cui il fallimento nel contattare i server Google si riverbera nel sistema con forti rallentamenti dello stesso per continua ed intensa attività su disco. Troverete in rete molte fonti citare strani rallentamenti causati da tale software.

Ora una semplice alea di rete può rallentare temporaneamente, ma la non connessione permanente causa molto secondi di rallentamento ogni volta che il processo di controllo aggiornamenti viene schedulato (e questo avviene alla partenza e ogni 5 ore).
Questo è quanto ho rilevato nel log:

19/07/16 22.07.22	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:07:21.899 GoogleSoftwareUpdateAgent[240/0xa0cce540] [lvl=2] -[KSAgentApp setupLoggerOutput] Agent settings: <KSAgentSettings:0x20d280 bundleID=com.google.Keystone.Agent lastCheck=2016-06-15 23:04:48 +0200 checkInterval=18000.000000 uiDisplayInterval=604800.000000 sleepInterval=1800.000000 jitterInterval=900 maxRunInterval=0.000000 isConsoleUser=1 ticketStorePath=/Users/andrea/Library/Google/GoogleSoftwareUpdate/TicketStore/Keystone.ticketstore runMode=3 daemonUpdateEngineBrokerServiceName=com.google.Keystone.Daemon.UpdateEngine daemonAdministrationServiceName=com.google.Keystone.Daemon.Administration logEverything=0 logBufferSize=2048 alwaysPromptForUpdates=0 productIDToUpdate=(null) lastUIDisplayed=(null) alwaysShowStatusItem=0 updateCheckTag=(null) printResults=NO userInitiated=NO>

...

19/07/16 22.08.06	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:08:06.993 GoogleSoftwareUpdateAgent[240/0xb0207000] [lvl=3] -[KSOutOfProcessFetcher(PrivateMethods) helperDidTerminate:] KSOutOfProcessFetcher helper tool failure. [com.google.UpdateEngine.CommonErrorDomain:1501 - 'KSOutOfProcessFetcher.m:783']

...

19/07/16 22.08.09	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:08:09.938 GoogleSoftwareUpdateAgent[240/0xa0cce540] [lvl=2] -[KSAgentApp checkForUpdates] Finished update check.

47 secondi di attivitˆà per un singolo processo quando l’intero sistema esegue bootstrap in meno tempo?

Intollerabile.

Come risolvere ?
Si può pensare di modificare la configurazione di schedulazione, oppure (e questo ho fatto io) rimuovere dall’avvio in sessione. Ci si può facilmente spingere oltre rimuovendo l’intero software.

Consigurazioni e cache le troviamo in:

  • ~/Library/LaunchAgents/com.google.keystone.agent.plist
  • ~/Library/Preferences/com.google.keystone.Agent.plist
  • ~/Library/Caches/com.google.keystone.Agent

Il vero e proprio software in:

  • ~/Library/Google/GoogleSoftwareUpdate [Directory]


Rimuovere
com.google.keystone.agent.plist
è sufficiente per fermare l’avvio del demone.

Se volete spingervi oltre (rimuovere), liberi di farlo: basta buttare tutto nel cestino; non accade nulla di strano: semplicemente non avrete più aggiornamenti automatici.

Ma attenti!

Se installerete nuovamente qualche altro software Google, la storia potrebbe ripetersi.

Alla prossima

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_2826.JPG

Cocoa & Perl: come ti faccio diventare uno script una applicazione Cocoa

Torniamo a parlare di programmazione Cocoa.

Questa volta parliamo di come integrare una interfaccia Cocoa ad una preesistente applicazione a linea di comando, e perché no, persino un’applicazione interpretata.

Avete preferenze?

Io una si.

Ho scelto di integrare uno script perl che ho sempre utilizzato in Linux e che volevo anche su Mac, ovviamente in una veste più “consona”, passatemi il termine.

Si tratta di cdlabelgen (http://www.aczoom.com/tools/cdinsert/), un utilissimo strumento per generare rapidamente copertine e buste per CDROM e DVD.

Scaricare e provare lo script è stata la prima cosa, anche per verificare quali potevano essere i requisiti per il suo funzionamento su OSX. Tutto tranquillo: funziona senza ulteriori installazioni.
La veste più consona è ovviamente la veste grafica: scartati Automator e AppleScript quali potenziali strumenti, la soluzione è ovviamente una applicazione Cocoa. Come in molti porting di software open source l’applicazione essenzialmente avrà il compito di generare la lista argomenti conseguente alle scelte utente (frontend) e attivare il programma di backend (nel nostro caso cdlabelgen): per non essere dipendente da fattori esterni l’applicazione conterrà lo script nel proprio bundle.

Volendo essere precisi, trattandosi di uno script interpretato, l’applicazione attivata sarà l’interprete perl del sistema a cui verrà passato il nome dello script con i suoi argomenti.
Il risultato mi è sembrato soddisfacente, quindi ve lo propongo. 
Vediamo come si realizza.
Per prima cosa creiamo un nuovo progetto Cocoa Application nel nostro XCode e copiamo lo script cdlabelgen nel gruppo Resources del nostro progetto; parimenti facciamo anche della cartella “postscript” fornita assieme allo script (ci sono i modelli grafici di riferimento).
Poi in un metodo di delega (applicationDidFinishLaunching) dell’applicazione Cocoa leggiamo il percorso alle risorse del bundle: ci serviranno per avviare lo script, in particolare per fornire il percorso delle risorse come program working directory a cdlabelgen in cui cercherà la cartella postScript.

NSString * programWorkingDirectory = [[[NSBundle mainBundle] resourcePath] retain];

NSString * commandPath = [[programWorkingDirectory stringByAppendingPathComponent: @"cdlabelgen"] retain];

(Ovviamente creiamole come variabili dell’istanza di NSObject <NSApplicationDelegate> di cui scegliamo un metodo in cui inserire il precedente codice).
Creiamo quindi una interfaccia per raccogliere le opzioni per la linea di comando ed un pulsante “Vai”, “Disegna”, “Go”, “Salva” o qualsiasi cosa gradiate. Colleghiamo il selettore di questo pulsante al metodo – (IBAction)save:(id)sender che avrete creato nel delegato dell’applicazione.

In questo metodo eseguiremo quanto necessario all’esecuzione di cdlabelgen e alla raccolta eventuale dei suoi frutti.

Un piccolo accenno su come costruire rapidamente una interfaccia per gli argomenti.
Create una istanza di oggetto NSUserDefaultController nello XIB; otterrete un istanza “Shared”; collegate i value dei widget al controller key “value” dell’istanza condivisa di User Defaults: non solo avrete un oggetto per rendere “attive” e visibili al programma le scelte effettuate, ma avrete anche una persistenza delle ultime configurazioni scelte, tutto in uno!

Solo per i tipi derivati da NSPopupMenu c’é una piccola eccezione: occorre in questo caso collegare il “Selected Index”: il value contenuto è per noi solo una label.
Per la costruzione della linea di comando, comprensiva delle opzioni, sarà ora sufficiente decodificare tutte le chiavi negli userDefaults elencandole in un array (è quanto vuole l’API di esecuzione di programmi esterni); ad esempio in questo modo:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];

if ( [[[NSUserDefaults standardUserDefaults] valueForKey:@"noTrayPlaque"] boolValue] == YES )

[args addObject:@"--no-tray-plaque"];

Forse non è elegante, ma questo metodo è il più rapido da realizzare senza dover progettare un complesso modello che tenga conto delle varie necessità o scomodare Core Data. Ricorsarsi di applicare prime il metodo synchronize sui defaults prima di iniziare:

[[NSUserDefaults standardUserDefaults] synchronize];

Per la parte operativa utilizziamo una istanza di NSTask per eseguire il comando esterno.
Per configurare l’esecuzione occorre il percorso del programma interprete.
Su Terminale quindi eseguiamo questo comando:

iMac:~ andrea$ which perl

/usr/bin/perl

Portiamo questo a conoscenza di una istanza NSTask:

NSTask * task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/perl";

task.currentDirectoryPath = programWorkingDirectory;

oppure (variante senza property):

[task setLaunchPath: @"/usr/bin/perl"];

[task setCurrentDirectoryPath: programWorkingDirectory];

così come per gli argomenti:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];
[args addObject: @"-o"];
[args addObject: filename];

Per ovviare i problemi di bufferizzazione di NSPipe, invece di “leggere” lo standard output di cdlabelgen gli passiamo sempre l’opzione “–output-file” con un nome di file precedentemente fornito dall’utente con un NSSavePanel.
Non rimane che lanciare il nostro programma:

task.arguments = args;
[task launch];

avremo il nostro file generato nel percorso e nome dati.
È opportuno dare un riscontro all’utente con una finestra di dialogo.

Per questo è sufficiente porre un risponditore alla notifica NSTaskDidTerminateNotification che possa presentare un semplice NSAlert. La notifica è lanciata da NSTask al termine del processo, e questo completa l’interfaccia utente.
Potrete aggiungere a piacimento tutto quello che ritenete utile ad una applicazione compiuta, ivi compresa una icona accattivante.

Ovviamente , se decidete di realizzare questo progetto e lo distribuite (anche tra amici), ricordatevi di inserire le credenziale degli sviluppatori di cdlabelgen in qualche parte dell’interfaccia, ossia Avinash Chopde <avinash@aczoom.com> www.aczoom.com e B. W. Fitzpatrick <fitz@red-bean.com>

Al prossimo esperimento.

IMG_2710.JPG

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

Continuiamo l’analisi di iTunes per raggiungere il nostro obiettivo di condivisione.

Ripartiamo con alcune caratteristiche “fisiche” della libreria iTunes.

La libreria è composta di un file “.itl” e un insieme ordinato di cartelle.
Lo schema della cartella della libreria è il seguente:

  • Album Artwork
  • iTunes Library.xml
  • iTunes Library Extras.itdb
  • iTunes Library Genius.itdb
  • iTunes Library.itl
  • iTunes Media
  • Previous iTunes Libraries

In particolare la cartella iTunes Media contiene le seguenti cartelle (molte create dinamicamente all’aggiunta in libreria dei media per cui sono predisposte, così come verranno rimosse quando dalla libreria verrà eliminata l’ultima istanza di in certo media):

  • Automatically Add to iTunes.localized
  • Books
  • Music
  • Home Videos
  • iTunes U
  • Mobile Applications
  • Podcasts

Se la vostra libreria è vecchia come la mia, ha probabilmente ereditato la struttura gerarchica precedente: potete liberamente trasferire le cartelle nella posizione corretta (troverete probabilmente Books, Podcast e Home Videos in una cartella iTunes Music).

Mentre le finalità di ciascuna cartella sono abbastanza ovvie, meno noto è l’uso della cartella “Automatically Add to iTunes.localized”.

La cartella “Automatically Add to iTunes.localized” serve ad aggiungere alla libreria (all’avvio di iTunes) gli oggetti ivi eventualmente contenuti: ha due difetti: accetta solo file di media del tipo gestito  (no alias, no playlist in formato xml, testo, m3u, ecc) e copia l’oggetto nel ramo di iTunes Media anche se opzione consolidamento non è attiva. Questo impedisce a questa cartella di essere usata come luogo di scambio (o migrazione) tra librerie da sincronizzare.

ITunes consente di esportare la libreria in formato xml: è una operazione da menu, e si chiama appunto “Esporta Libreria”. Come sottolineato dalla documentazione Apple questa esportazione non è completa; aggiungiamo noi quanto non detto: il file non contempla ad esempio le app mobile, ma per noi è sufficiente riguardi la playlist Musica e le playlist utente derivanti.export

L’importazione sarebbe un mistero se non si tenesse in considerazione quanto abbiamo sottolineato in precedenza e quanto è osservabile interrogando iTunes via AppleScript: la Musica nella libreria è una playlist (particolare, ma pur sempre una playlist). Quindi la funzione “Importa Playlist” è la nostra funzione di importazione della libreria di musica. Per quanto accennato la funzione importerà anche tutte le playlist utente esportate col precedente metodo.import

Non considerate questa esportazione/importazione un sistema di backup completo per la vostra libreria: abbiamo già detto che non contiene tutte le informazioni.

Eppure Apple stessa la considera utile per un disaster recovery della vostra libreria: perché ?

La nascita stessa di una libreria in iTunes fonda le sue ragioni nella necessità di preservare quelle informazioni che non possono risiedere nel formato file del media (è ancora vero per alcuni formati). Tutto quanto è parte del formato file è recuperabile aggiungendo nuovamente il file alla libreria, ma il resto delle informazioni aggiunte grazie alla capacità della libreria non lo è: queste informazioni sono oggetto di esportazione nel formato xml della funzione Esporta Libreria, ecco perché è utile ancora oggi a recuperare una libreria corrotta.

Noi sfrutteremo questa funzione con ben altro scopo.


Quindi, prima di iniziare, affidatevi ad una copia aggiornata della vostra libreria in Time Machine, o (se proprio non potete) almeno affidatevi ad una copia su altro percorso del file Library.itl.

Ci leggiamo quando siete pronti !

IMG_2710.JPG

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

Torniamo a parlare di iTunes.

Uno degli aspetti più trascurati in molti software è la possibilità di condividere il proprio archivio tra gli utenti del sistema quando questo archivio abbia una naturale vocazione alla condivisione, anche e non solo per la necessità di tenere sotto controllo il peso degli oggetti di archivio che possono ripetersi tra più utenti.
Forse questa tendenza a trascurare la multi-utenza è dovuta alla ridotta necessità di questa in un mercato dominato da computer portatili utilizzati da un solo proprietario (personal computer, dunque). Il predominare poi dei dispositivi mobile (per loro natura “personal”) ha ulteriormente allontanato la multi-utenza tra le priorità degli sviluppatori software.

Peccano di mancanza di strumenti per la multi-utenza iTunes come gli altri software della famiglia i* (tutto il pacchetto iLife, per intenderci).

Ma stiamo parlando di OSX, un sistema che a differenza del MacOS nasce multiutente, e questa caratteristica ha una naturale collocazione nell’uso domestico e familiare di un iMac, ove ciascun membro può avere un profilo proprio ma che (per naturale appartenenza al medesimo gruppo umano) condividerà molto con gli altri utenti (pensiamo solo ai video o le foto delle gite familiari).

Eppure tutti questi programmi ignorano la loro possibile collocazione in ambito multiutente, lasciando a noi l’onere di individuare una soluzione.

Certo una qualche soluzione a volte è fornita dal supporto Apple (vedi https://support.apple.com/it-it/HT204488), ma come si può leggere non tutto è agevole, e nemmeno tutto è esplicitato.
Focalizzandoci su iTunes (dovremmo affrontare analoghe questioni anche per iPhoto o iMovie) vedremo di trovare alcune soluzioni più “complete”.
iTunes nasce in epoca MacOS ed era dedicato alla sola riproduzione audio. Si è evoluto gradualmente: video, podcast e app mobile sono arrivare col tempo e ben dopo la migrazione a OSX. Librerie ereditate da un’epoca pre iOS (come nel caso della mia) contengono i file multimediali in una cartella iTunes Music. Abbastanza significativo, no?
Gli aggiornamenti dell’applicativo hanno modificato la base dati, ma non la struttura di directory della mia libreria.
Ma il tempo è passato, e la struttura prevista dalle nuove versioni prevede ora tutta un’altra gerarchia, come si può osservare in una libreria creata ex-novo con una nuova versione (vedi procedura https://support.apple.com/it-it/HT201596).

In questa serie di articoli affronteremo principalmente il problema della condivisione della libreria iTunes a vari livelli, ma forniremo anche alcune esperienze sul significato di alcune componenti e sulle possibili configurazioni della libreria, nonché qualche suggerimento sparso. Sia ben chiaro: la “Condivisione in famiglia” non ha nulla a che vedere con il problema affrontato: questa condivisione è via rete locale, quindi l’iPod di vostra moglie non potrà essere sincronizzato con i brani che voi avete acquistato sullo store, pertanto non potrà sentirli durante la sua sessione di palestra (a meno che non abbiate la palestra in casa).
Purtroppo quello che vorremmo raggiungere, ossia la a piena automazione di tutte le procedure, non è ottenibile (come evidente dal documento del supporto Apple https://support.apple.com/it-it/HT204488) anche e non solo in quanto iTunes (come altri software) può aprire librerie differenti solo al suo avvio (vedi procedura https://support.apple.com/it-it/HT201596) e non è capace di fare questo nemmeno via Apple Script (esistono delle possibilità alterando un file di configurazione attraverso il comando defaults, ma non è una scelta condivisibile: troppo suscettibile di variazioni con gli aggiornamenti software). Comunque e una possibilità che completerebbe l’automazione cercata.
Cercheremo comunque di avvicinarci offrendo delle soluzioni di automazione che almeno supportino i processi eventualmente necessari a raggiungere lo scopo principale di realizzare una condivisione multiutente.

Chiariamo subito: una totale condivisione della libreria è possibile in un modo semplice ed immediato ma non utile. Si può infatti completamente condividere una libreria ponendola in area condivisa (Shared folder.localized) tra più utenti e dando i giusti permessi (fatto questo abbastanza ovvio).
Questa però non è (dicevamo) una soluzione utile, in quanto la libreria è contenitore di tutti i media gestibili con iTunes e ciascun utente vorrebbe una qualche autonomia di configurazione, lasciando che solo quanto è realmente condivisibile lo sia mentre il resto possa restare autonomo e privato. Se consideriamo che il file “.itl” gestisce il parco musica, così come le app mobile, libri e playlist e altro ancora è di tutta evidenza come ciascun utente voglia poter mantenere una certa autonomia di alcuni di questi media.

Cercheremo di mostrare come consentire una condivisione completa di musica e al contempo una piena indipendenza di app o quanto altro.
Come si evince da documenti Apple, si possono avere due librerie distinte che condividono gli stessi media, ma non si ha un modo per sincronizzarle.

Questo in quanto le due librerie hanno file “.itl” indipendenti: in questi è presente l’insieme delle informazioni che costituiscono la libreria, e non esistono strumenti di esportazione o importazione parziali.

Cercheremo di superare questo ostacolo. Inoltre daremo una soluzione alla individualità di gestione di altre risorse come le app mobile (la frattura tra versioni di iOS e dispositivi di vecchia generazione impone di avere un parco software non aggiornatissimo in una libreria indipendente diciamo “legacy”).

Iniziamo dunque con una panoramica degli strumenti disponibili, ma questo lo faremo in un altro articolo: ci siamo dilungati troppo.

cilindro2

crypt-à-porter

Tutti usano SSL nella vita digitale quotidiana: nell’utilizzo della posta elettronica, nell’accesso ai siti web, nei propri certificati digitali e, perfino a nostra insaputa, in mille altre appliances del nascente campo “internet-of-things”, quindi facilmente recuperabile onboard sui diversi sistemi embedded (O_o).

Se hai bisogno di crittografare velocemente un file on-the-fly in maniera simmetrica, non è necessario ricorrere a complesse architetture o a programmi addizionali, o impazzire con le migliaia di salse in cui è possibile interagire con openssl. Basta digitare da semplice riga di comando:

$ openssl enc -in <fileA> -out <fileB> -e -aes256 -k <mypass>

e, altrettanto semplicemente, decrittografare con:

$ openssl enc -in <fileB> -out <fileA> -d -aes256 -k <mypass>

Ovviamente, funziona anche sul tuo Mac  😉

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