Tutti gli articoli di Andrea Tassotti

core_midi_devel

Core MIDI e il tempo

Probabilmente Core Midi, parte dell’infrastruttura software Core Audio per OSX e iOS, è il software meno documentato da Apple e dal web in generale.
Volendo costruire un mini player MIDI integrato (all’interno di un programma più complesso) ho dovuto accontentarmi di un paio di esempi nella documentazione ADC (PlaySequence e PlaySoftMIDI) e i references dell’API, quasi tutta basata su strutture opache.

Costruire un modello funzionante non è stato molto difficile seguendo il prototipo PlaySenquence, apportando anche qualche modifica (ad esempio rimuovendo l’utilizzo delle classi C++ PublicUtility incluse nel progetto a favore di un paio di funzioni per la gestione del tempo di sistema).

Il problema è nato nel momento in cui ho voluto porre nell’interfaccia l’informazione del tempo corrente del brano in esecuzione con MusicPlayer (elemento dell’API).

Interrompiamo il racconto per una breve digressione sul tema temporizzazioni in ambito musicale/MIDI.

In musica il tempo si esprime tenendo presenti due parametri: la misura (ingl. bar) e la velocità.
Il tempo della misura (ingl. time signature) è un concetto più notazionale che pratico, ma che in sostanza caratterizza la collocazione reciproca delle note sull’asse tempo (e la loro durata) nel metro di riferimento adottato, ossia la misura): è un riferimento relativo capace di esprimere la struttura metrica del brano. La divisione viene indicata con una frazione che esprime quante note di quarto (ingl. quarter note; è l’unità di misura della divisione) compongono una misura (ingl. measure).

La velocità indica quanto veloci si susseguono le note. Nella notazione classica sono i riferimenti a quel “Piano”, “Pianissimo”, “Vivace”,con brio“, ecc. Con riferimento al metronomo e nella musica digitale si utilizza invece come parametro la velocità con cui si susseguono le note di quarto nel tempo, ossia quante note di quarto sono presenti in un minuto (beat per minute = BPM): è una misura assoluta e determina la velocità di esecuzione di un brano, qualsiasi metro si adotti.

Nel file MIDI (SMF = Standard Midi File) questi concetti sono espressi in vari modi. Il tempo della misura ad esempio è collocato con meta eventi nel flusso dei dati, così come la velocità di esecuzione, mentre posizione e durata delle note è espressa come differenziale tra due momenti espressi di una unità di misura estranea alla notazione tradizionale ma che funziona egregiamente come ponte tra questa e le necessità dell’informatica: lo step (o tick o, come lo definisce Core Midi, subbeat).
Lo step è una suddivisione della nota di quarto espressa a partire dal concetto di risoluzione, un concetto estraneo alla notazione classica, ma necessario a rendere la relazione tra il tempo assoluto (in milli o microsencondi, dipendentemente dal timer adottato dal software) e la nota di quarto; più propriamente uno step è la più piccola unità riconoscibile all’interno di una nota di quarto a partire da una risoluzione. Una risoluzione di 960 (quella adottata per esempio da Garage Band) indica che nella nota di quarto sono riconoscibili 960 frazioni di tempo (480 per la nota di ottavo, 240 per la nota di sedicesimo, ecc).
Quindi la risoluzione caratterizza la precisione con cui un software può esprimere le posizioni e le durate delle note (andando ben oltre la notazione tradizionale e consentendo così di esprimere quei discostamenti dalla divisione che caratterizza l’esecuzione umana di un brano). Vedi concetto di quantizzazione/umanizzazione.

E veniamo al problema in Core Midi.

Il discorso che segue fa riferimento all’API a partire da OSX 10.5 (e quindi anche iOS della stessa epoca).
L’API mette a disposizione (per la lettura di un SMF, ma anche per la generazione ex-novo) una struttura opaca MusicSequence che contiene una lista di strutture opache MusicTrack, più un accessorio di strutture e API di gestione (come il MusicEventIterator), il tutto a favore di un altro elemento opaco (MusicPlayer) capace di eseguire un brano espresso mediante un MusicSequence.
La funzione MusicSequenceFileLoad (MusicSequence References) costruisce una sequenza a partire da un SMF; nella MusicSequence le informazioni di cui abbiamo parlato in precedenza (e di cui certamente il file SMF ne ha una rappresentazione) vengono distribuite in un modo particolare e scarsamente documentato. L’unico modo di accedere alle informazioni dovrebbe essere attraverso l’API associata (strutture opache, ricordate?) ma tutto non è ben cordinato, specialmente nella documentazione.

La velocità di esecuzione del brano (ad esempio) è collocata in un dizionario leggibile mediante la funzione MusicSequenceGetInfoDictionary per la chiave “tempo”; le sue variazioni però devono essere rilevate (lo fa ovviamente anche MusicPlayer quando esegue il brano) nella “traccia tempo“, una traccia speciale in cui sono collocati (che lo siano stai nel file SMF o no) tutti i meta eventi che caratterizzazo il tempo.
La traccia tempo è ottenibile mediante la funzione MusicSequenceGetTempoTrack e iterabile mediante MusicEventIterator per leggerne e decodificarne ogni evento (se ad esempio il nostro software deve mostrare il contenuto di una MusicSequence invece di darla in pasto a MusicPlayer). Volendo usare MusicPlayer non saremo costretti a farlo.

Ma tornando al nostro problema di visualizzazione del tempo durante l’esecuzione con MusicPlayer, quello che dovremmo utilizzare è la funzione:

OSStatus MusicSequenceBeatsToBarBeatTime(
MusicSequence inSequence,
MusicTimeStamp inBeats,
UInt32 inSubbeatDivisor,
CABarBeatTime *outBarBeatTime
);

Questa dovrebbe essere intesa per mostrare il tempo (internamente espresso come numero float di note di quarto) nella canonica forma:

Misura:Battuta:Frammento di tempo

Per quanto riguarda il parametro inBeats, questo è facilmente ottenibile mediante MusicPlayerGetTime().
Per quanto riguarda la struttura CABarBeatTime del parametro di uscita outBarBeatTime non abbiamo documentazione; ma basta cercare in AudioToolbox/CoreAudioClock.h (erroneamente chiamato ancora nella documentazione AudioToolbox/CAClock.h ) per ottenere

struct CABarBeatTime {
SInt32 bar;
UInt16 beat;
UInt16 subbeat;
UInt16 subbeatDivisor;
UInt16 reserved;
};
typedef struct CABarBeatTime CABarBeatTime;

Ma per quanto riguarda il parametro inSubbeatDivisor non esiste documentazione.
Dopo varia sperimentazione intuisco che il parametro è in qualche modo associato al concetto di risoluzione. So che il file SMF pone questo valore nell’ultima parola a 16 bit del chunk THdr; ma non trovo nell’API di MusicSequence nulla che mi possa far accedere a questa informazione.
Solo una ricerca nei file AudioToolbox/CoreAudioClock.h mi conferma che il parametro subbeatDivisor è legato alla risoluzione: questo mi convince a cercare informazioni nell’API con una ricerca brutale del termine “risoluzione”.
Rinvengo una costante kSequenceTrackProperty_TimeResolution legata all’API di MusicTrack, e per la precizione alla funzione MusicTrackGetProperty e vengo a scoprire dalla documentazione che la sola traccia tempo detiene il dato di risoluzione del brano, esattamente quanto riportato in THrd.

Bingo ! Questo è il parametro necessario.

Vista la difficoltà nel reperire una informazione così vitale implemento una estensione all’API di MusicSequence (mi pare più appropriato) con il doppio fine di documentare e di semplificare l’utilizzo di questa informazione.

Ecco l’implementazione:

/**
* MusicSequenceGetResolution
*
* @author Andrea Tassotti
*
*/
OSStatus MusicSequenceGetResolution(MusicSequence inSequence, SInt16 *outResolution)
{
MusicTrack tempoTrack;

OSStatus status = MusicSequenceGetTempoTrack(inSequence, &tempoTrack);
if (status != noErr) return status;

UInt32 ioLength = sizeof(SInt16);
return MusicTrackGetProperty (
tempoTrack,
kSequenceTrackProperty_TimeResolution,
outResolution,
&ioLength
);
}

che sarà semplicemente utilizzabile nel seguente modo:

MusicTimeStamp time;
if ( MusicPlayerGetTime (player, &time) != noErr )
[NSException raise:@"getPrerollTime" format:@"Can't get time for player"];
if (time >= sequenceLength)
time = sequenceLength;

CABarBeatTime outBarBeatTime;
SInt16 resolution;

if ( MusicSequenceGetResolution(sequence, &resolution) != noErr )
resolution = 240;

if ( MusicSequenceBeatsToBarBeatTime(sequence, time, (UInt32)resolution, &outBarBeatTime) == noErr )
{
// Get the string representation of the current time (Garage Band style)
[timeTextField setStringValue: [NSString stringWithFormat:@"%04i.%2i.%2i.%03i",
outBarBeatTime.bar,
outBarBeatTime.beat,
outBarBeatTime.subbeat / (resolution/4) + 1, // 16-th
outBarBeatTime.subbeat ]];
}

Ovviamente in un caso reale (come nel mio software), la risoluzione verrà estratta una sola volta dalla traccia tempo.

Spero anche in questo caso di essere stato utile a qualcuno.

Il vice-versa, ovvero l’impostazione di una risoluzione per un file SMF si ottiene con la funzione che crea il file (questo è documentato):

OSStatus MusicSequenceFileCreate (
MusicSequence inSequence,
CFURLRef inFileRef,
MusicSequenceFileTypeID inFileType,
MusicSequenceFileFlags inFlags,
SInt inResolution);
);

Ma questa è una altra storia.

BROCADE_fnl_TM

FabricOS e configurazioni

Nella gestione di una fabbrica fiber channel realizzata con switch Brocade (o assimilabili) si ha a che fare con il sistema FabricOS (ne abbiamo già parlato in un precedente articolo). Questo ha tutta una serie di comandi per creare e modificare la sua configurazione.

A differenza del Cisco IOS, nel FabricOS la sintassi di configurazione differisce dalla sintassi nella visualizzazione della configurazione stessa. Pertanto chi è abituato a Cisco IOS trova difficoltoso “copiare” una parte della configurazione da uno switch ad un altro osservandone semplicemente la configurazione tramite i comandi alishow, zoneshow e cfgshow.

È la classica situazione in cui si trova il sistemista che deve risolvere il conflitto di due fabbriche segmentate su una connessione ISL. La soluzione passa per la clonazione della configurazione su tutti gli switch interessati.

Il comando configupload, salvando la configurazione dello switch in un formato ASCII su un server FTP o via SCP, potrebbe tornarci utile (come in molti consigliano). Ma l’utilità si ferma al poter individuare  le differenze, e soltanto a patto di riordinare le righe della configurazione prima di eseguire tale ricerca.

Applicare le opportune modifiche non è cosa diretta.

Nel file (pur potendo scegliere tra configurazione intera e riduzione al  solo chassis o al solo switch) sono presenti informazioni non replicabili su tutti gli switch.

La replica della configurazione al fine di sanare una segmentazione deve considerare solo  alias, zone e configurazioni, non parametri dello chassis o dello switch che sono parametri univoci (e devono rimanere tali), come nome, indirizzo, id di dominio, ecc. Pertanto un file generato da uno switch non potrà essere caricato su di un altro, se non al fine di un disaster recovery.

Inoltre una zona (ad esempio) in questi file è espressa con la seguente:

zone.Prod1_C_CXPROD_SPA0:Prod1_Adapter_C;CX4_PROD_SPA0

che è cosa diversa dal comando per istruirla:

zonecreate "Prod1_C_CXPROD_SPA0", "Prod1_Adapter_C;CX4_PROD_SPA0"

La cosa ottimale sarebbe poter avere una traduzione del formato del file di configurazione scaricato dallo switch reputato “modello” e poterla replicare sugli altri dopo averli ripuliti con il comando cfgclear (come molti tutorial insegnano).

Pertanto ci sarà molto utile il seguente script, progettato proprio a questo scopo:

#!/bin/bash
#
# usage: create_FOS_config.sh
#
#
awk -F. '/^alias/{ print "alicreate \""substr($2,1, index($2,":") - 1)"\", \"" substr($2, index($2,":") + 1)"\"" }' $1 | sort -k2
awk -F. '/^zone/ { print "zonecreate \""substr($2,1, index($2,":") - 1)"\", \"" substr($2, index($2,":") + 1)"\"" }' $1 | sort -k2
awk -F. '/^cfg/ { print "cfgcreate \""substr($2,1, index($2,":") - 1)"\", \"" substr($2, index($2,":") + 1)"\"" }' $1

L’ordinamento impostato nell’elenco degli alias e delle zone potrà essere utile per un esame manuale del contenuto di questa parte della configurazione una volta caricata.

Spero possa essere utile.

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!

tux+ibm

Linux e appliances

Mi è recentemente capitato di configurare degli switch FC IBM, modello 2498_24.
Nel verificare alcuni parametri di versione per la configurazione che mi accingevo a fare mi è andato l’occhio su un numero alquanto sospetto:

SAN:root> version
Kernel:     2.6.14.2
Fabric OS:  v6.4.2a
Made on:    Mon Jul 18 22:27:42 2011
Flash:        Tue Jan 3 17:18:42 2012
BootProm:   1.0.9

2.6.14 … interessante, mi dico. E mentre la parola Linux mi sovviene alla mente, mi ritornano certe polemiche mosse da certi “guru” sulla presunta pericolosità di adottare sistemi Linux nei comparti del networking e della sicurezza, ed è più sicuro affidarsi ad appliance da migliaia di euro (senza considerare licenze d’uso, upgrade, assistenze, ecc).
Ed eccomene una sotto mano, di queste appliance, e per di più costruita da “mamma IBM”, e chi ti trovo? Un bel sistema Linux con kernel 2.6: certo, customizzato, certo con tantissimi comandi ad hoc (vedi cosiddetto FOS) per il ruolo di questo dispositivo. Ma in finale sempre un kernel Linux.

Pertanto rido di quanti credano che comprare è meglio che realizzare in economia a partire da un semplice Linux box, specialmente per quei servizi che sono già completi e “di serie” quali un netfilter o altri elementi di livello kernel. Il resto sono pezzi di ferro da aggiungere tutto intorno.

Tanto per la cronaca; ecco altre dimostrazioni che un IBM 2498_24 è a tutti gli effetti un Linux box, customizzato e ridotto al minimo necessario.

SAN:root> uname -r
2.6.14.2
SAN:root> uname -a
Linux SAN 2.6.14.2 #1 Sat Jul 16 10:19:20 PDT 2011 ppc unknown
SAN:root> ls -l /
total 96
drwxr-xr-x   2 root     sys          4096 Jan  3  2012 bin/
drwxr-xr-x   2 root     sys          4096 Jan  3  2012 boot/
drwxr-xr-x   2 root     root         4096 Jan  3  2012 config/
drwxrwxrwx  41 root     sys          4096 Jan  3  2012 core_files/
drwxr-xr-x   3 root     sys          4096 Nov 13 12:28 dev/
lrwxrwxrwx   1 root     root           10 Nov 13 12:28 diag -> /proc/diag/
drwxr-xr-x  17 root     sys          4096 Jan 21 13:02 etc/
drwxr-xr-x   2 root     sys          4096 Jul 18  2011 export/
drwxr-xr-x  18 root     sys          4096 Nov 13 12:28 fabos/
drwxr-xr-x   2 root     sys          4096 Jul 18  2011 import/
drwxr-xr-x   2 root     sys          4096 Jul 18  2011 initrd/
drwxr-xr-x   6 root     sys          4096 Jan  3  2012 lib/
lrwxrwxrwx   1 root     sys            11 Jan  3  2012 libexec -> usr/libexec/
drwx------   2 root     root        16384 Jan  3  2012 lost+found/
drwxr-xr-x  24 root     root         4096 Jan  3  2012 mnt/
dr-xr-xr-x 214 root     root            0 Jan  1  1970 proc/
drwxr-x---   4 root     sys          4096 Jan  3  2012 root/
drwxr-xr-x   2 root     sys          4096 Jan  3  2012 sbin/
lrwxrwxrwx   1 root     sys             9 Jan  3  2012 share -> usr/share/
drwxr-xr-x   2 root     root         4096 Jan  3  2012 standby_sbin/
drwxrwxrwx   3 root     sys          4096 Jan  3  2012 support_files/
drwxr-xr-x   2 root     sys          4096 Jul 18  2011 tftpboot/
drwxrwxrwt   5 root     root            0 Jan 23 14:26 tmp/
drwxr-xr-x   2 root     sys          4096 Jul 18  2011 users/
drwxr-xr-x  11 root     sys          4096 Jan  3  2012 usr/
drwxr-xr-x  11 root     sys          4096 Jan  3  2012 var/

E vogliamo parlare di un

tail /var/log/nslog.txt
?

Va bene. Ogni altra considerazione è puramente superflua.

Il seguente articolo è anche disponibile nel seguente Blog

oracle-ecommerce-integration

Oracle e LOB

di Andrea Tassotti

Non me ne vorranno i colleghi di questo blog che per lavoro o diletto praticano il mondo della programmazione Java, ma nello storico contenzioso tra sistemista e programmatore (contenzioso su risorse e sicurezza) si osserva una (dico) “perversa” tendenza tutta (ahimé) dei programmatori Java nel prediligere l’uso di campi LOB di tipo BLOB o CLOB (raramente NCLOB) per risolvere questioni di memorizzazione di informazioni non strettamente primitive, con particolare propensione alla serializzazione di oggetti o immagazzinamento di file di ogni sorta (e orrore, anche xml, trascurando il tipo nativo Oracle) [a difesa dei colleghi del blog…molte volte si ereditano scelte progettuali che sarebbero devastanti da cambiare] in questo tipo di campi, con buona pace o la connivenza [close your eyes] di tanti DBA.
Continua a leggere

tux-banner

Linux & SAN

Premesso che lasceremo al lettore l’onere di trovare il software citato nel post nel sistema di pacchetti della distribuzione su cui opererà, diamo alcuni suggerimenti utili per la configurazione di connessioni a SAN per sistemi Linux.

Esistono molti modi per determinare le informazioni su indirizzi dell’HBA e mappature dei dischi, e in giro si trovano molti tutorial al riguardo.

Il punto finale può essere sempre considerato il seguente comando:

sg_map -x

che ci fornisce i nomi dei dispositivi a blocchi mappati su una qualche LUN.

Partiamo da qui per determinare altra informazione, in genere molto utile in sistemi complessi per orientarsi tra i vari dischi e storage: il nome dello storage e le WWN delle LUN.Ipotizziamo che la nuova LUN sia stata mappata sul dispositivo /dev/sda, quindi interroghiamo questo per determinare le informazioni richieste:

  • Informazioni sullo storage (produttorel,modello,S/N)
/lib/udev/scsi_id --page=0x80 --whitelisted \
--device=<b>/dev/sda</b> | awk '{ print substr($0,2)}'
  • Informazioni sulla LUN (WWN)
/lib/udev/scsi_id --page=0x83 --whitelisted \
--device=<b>/dev/sda</b> | awk '{ print substr($0,2)}'

Consigliamo di trascrivere queste due linee di codice in due distinti file, denominati scsi_storage_info e scsi_lun, dotarli di diritti di esecuzione e porli nel percorso /sbin (tutto quanto detto in questo post presuppone che siate amministratori delle macchine su cui operate). Avrete così altri comandi utili nel processo di configurazione di una connessione a SAN in un host Linux.

ios

iOS, Pages e WebDAV locale

Chi possiede un dispositivo mobile Apple sa che una delle politiche Apple prevede che per usare alcune funzionalità dai suoi dispositivi o applicativi occorre avere una connessione Internet attiva.

Questo impedisce l’uso offline di alcune funzioni, e questo ci può anche stare se queste sono legate strettamente alla Rete; non lo sono altrettanto se queste funzionalità (vedi l’accesso ad un servizio webDAV) possono essere rese disponibili da una rete locale wifi, isolata da internet anche e non solo per ragioni di sicurezza (come in alcune realtà imprenditoriali o istituzionali).

Continua a leggere

oracle-ecommerce-integration

Velenosissimi problemi di startup

Oracle quando ci si mette fa diventare pazzi di rabbia; ho provato ad avviare una istanza che sapevo configurata (ometto per il momento alcuni passaggi per non rovinarvi la sorpresa):

SQL> startup
ORA-27103: internal error
Linux-x86_64 Error: 2: No such file or directory
Additional information: 101

Ovviamente cerco dettagli su alert.log e altre possibili fonti, compreso il documento ufficiale per gli errori (b14219.pdf, che per errore 27103 mi suggerisce di chiamare l’assistenza!!!!).

Internet neanche a parlarne: tanti errori e (im)probabili soluzioni, ma neanche una traccia di questo errore.

Non vi dico le peripezie e il percorso con cui sono venuto a soluzione. Anche il caso ci ha messo del suo.

In ogni modo devo prima svelarvi il contesto omesso precedentemente: l’istanza risiede in un virtual environment di un container OpenVZ (tecnologia di zoning per Linux).

L’ambiente è stato creato clonando una macchina fisica (su cui l’istanza girava correttamente): la clonazione è avvenuta con la semplice copia del filesystem con un TAR, omettendo quei percorsi legati al kernel sorgente che avrebbero potuto collidere con il kernel della macchina virtualizzatrice (/proc, /dev, /sys, ecc). Tecnica sperimentata in precedenza decine di volte con successo, anche con istanza Oracle presenti.

Ed è stato proprio in /dev il problema: nella ricostruzione di tutti gli i-node standard presenti in questo percorso mi sono dimenticato (e poi fortunosamente ricordato cercando di usarlo nel contesto di un comando che stavo eseguendo per altre configurazioni) del file speciale /dev/zero.

In assenza di questo file Oracle va in pappa e genera l’errore indicato all’inizio, senza altra informazione.

Buon Oracle a tutti