Visualizzazione post con etichetta utilità. Mostra tutti i post
Visualizzazione post con etichetta utilità. Mostra tutti i post

giovedì 9 giugno 2016

JMusicMan versione 5.0

Dopo il primo post su JMusicMan, adesso ho migliorato il programma sotto diversi aspetti. 

La funzione principale rimane invariata, ma adesso grazie al supporto di un'altra libreria, il programma gestisce diversi file audio.

Ecco le migliorie principali:

  • La lettura dei tag non presenta più bug; anzi è prevista la funzione "Reset tag" per creare un nuovo tag e non entrare in contrasto con tag creati precedentemente da programmi diversi (la cui compatibilità purtroppo, viene a cadere)
  • Eliminazione delle cartelle vuote e notifica nel caso ci siano cartelle contenenti esclusivamente materiale non-audio
  • Gestione delle ambiguità: se due o più file hanno lo stesso tag si presentano due casi:
    • I file hanno la stessa durata quindi probabilmente sono uguale; si chiede all'utente se eliminare i doppi o crearne una copia rinominata
    • I file hanno durata diversa: alla fine della procedura di aggiornamento viene chiesto di modificare i tag dei file audio, pena l'esclusione della libreria. Il messaggio si ripresenterà al successivo aggiornamento.
Tra le caratteristiche che tuttora sono in via di sviluppo (anzi sono già state implementate, ma la versione è beta) figurano:
  • Ricerca di canzoni nella libreria
  • Pannello laterale per visualizzare alcune informazioni
  • Vari automatismi fra cui numerazione automatica, riconoscimento traccia e riconoscimento tracce album tramite il servizio musicbrainz
  • Modifica titoli massivamente (in sviluppo)
Questo è uno screenshot della versione beta:

Panoramica

All'avvio il programma cercherà automaticamente file audio nella cartella "musica" del computer e comincia la catalogazione:

Se si sono verificati errori (ambiguità di tag o altro) allora una finestra di dialogo mostrerà le tracce "problematica" e chiederà all'utente la modifica dei tag:


L'utente può modificare le informazioni direttamente dentro il form. E' possibile selezionare anche più tracce. Titolo e numero traccia si memorizzano dinamicamente scrivendo nei relativi campi, Artista e Album invece si memorizzando premendo i tasti coi tre puntini sulla destra.

È anche possibile correggere le tracce anche quando sono taggate, sia singolarmente che massivamente (in quest'ultimo caso è possibile modificare solo Artista, Album e Immagine):

La sincronizzazione dei dispositivi

È una funzione che non ho curato molto ultimamente, ma comunque ricordo funzionava a dovere. Dopo aver impostato il dispositivo contenente il file .is_audio_player, il programma sincronizza la libreria del PC con quella del dispositivo trovando le tracce da copiare e quelle da eliminare semplicemente comparando due file XML che sono quelli che si trovano nella cartella Musica ("JMusicManLibrary.xml")

domenica 17 aprile 2016

Flacreader, lettore di commenti (tag metadata) dei file flac

Come un surplus per JMusicMan, il progetto che avevo iniziato in java per la catalogazione della musica sul mio PC e che avevo abbandonato per l'impossibilità di gestire i file .flac, ho sviluppato questa classe, ancora da migliorare, per leggere e modificare i commenti (si chiamano così, più precisamente: vorbis comments) di questi file lossless. 

Però è una classe scritta in C#, dovrei convertirla di nuovo in java, o creare un binding. 

Da questa pagina ho consultato come è formato l'header dei file flac, dunque con delle operazioni sui file binari bit a bit sono riuscito a creare questa classe che attualmente legge bene i file, modifica bene anche i file che hanno già una sezione vorbis comment, ma ancora non ho testato la modifica dei file che non hanno la suddetta sezione, e quindi sarà la caratteristica che implementerò al prossimo commit su git. 

La pagina git è questa: https://github.com/standuptall/Flacreader, con due branch, uno quello principale (la classe è Flacreader.cs) e un'altro branch (grafica) che include una piccola interfaccia grafica. Anche se con git devo prenderci ancora la mano. 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace it.albe
{
    public class FlacReader
    {
        public static string vendor_string;
        public static Int32 user_comment_list_length;
        private string filepath;
        private bool data_written = false;
        private class _metadata {
            public struct _streaminfo
            {
                public bool presente;
                public const int code = 0x0;
                public bool ultimo;
            }
            public struct _padding
            {
                public bool presente;
                public const int code = 0x1;
                public bool ultimo;
            }
            public struct _application
            {
                public bool presente;
                public const int code = 0x2;
                public bool ultimo;
            }
            public struct _seektable
            {
                public bool presente;
                public const int code = 0x3;
                public bool ultimo;
            }
            public struct _vorbis_comment
            {
                public bool presente;
                public const int code = 0x4;
                public bool ultimo;
            }
            public struct _cuesheet
            {
                public bool presente;
                public const int code = 0x5;
                public bool ultimo;
            }
            public struct _picture
            {
                public bool presente;
                public const int code = 0x6;
                public bool ultimo;
            }

            public _streaminfo streaminfo;
            public _padding padding;
            public _application application;
            public _seektable seektable;
            public _vorbis_comment vorbis_comment;
            public _cuesheet cuesheet;
            public _picture picture;
            public const int ending_metadata_code = 0xF0;
            const int NUM_METADATA = 7;
            public _metadata()
            {
                streaminfo.presente = false;
                streaminfo.ultimo = false;
                padding.presente = false;
                padding.ultimo = false;
                application.presente = false;
                application.ultimo = false;
                seektable.presente = false;
                seektable.ultimo = false;
                vorbis_comment.presente = false;
                vorbis_comment.ultimo = false;
                cuesheet.presente = false;
                cuesheet.ultimo = false;
                picture.presente = false;
                picture.ultimo = false;
            }
            public void setMetadata(int code)
            {
                switch ((code<<1)>>1)  //tolgo il bit più significativo
                {
                    case _streaminfo.code: streaminfo.presente = true; break;
                    case _padding.code: padding.presente = true; break;
                    case _application.code: application.presente = true; break;
                    case _seektable.code: seektable.presente = true; break;
                    case _vorbis_comment.code: vorbis_comment.presente = true; break;
                    case _cuesheet.code: cuesheet.presente = true; break;
                    case _picture.code: picture.presente = true; break;
                }
            }
            public int getFlag(int flag)  //devo controllare se è l'ultimo, così setto il bit più significativo a 1
            {
                flag = ((flag << 1) >> 1); //tolgo il bit più significtivo
                if (picture.presente)
                    picture.ultimo = true;
                else if (cuesheet.presente)
                    cuesheet.ultimo = true;
                else if (vorbis_comment.presente)
                    vorbis_comment.ultimo = true;
                else if (seektable.presente)
                    seektable.ultimo = true;
                else if (application.presente)
                    application.ultimo = true;
                else if (padding.presente)
                    padding.ultimo = true;
                else if (streaminfo.presente)
                    streaminfo.ultimo = true;
                switch (flag)
                {
                    case _streaminfo.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _padding.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _application.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _seektable.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _vorbis_comment.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _cuesheet.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                    case _picture.code: if (streaminfo.presente) flag += ending_metadata_code; break;
                }
                return flag;
            }
        } ;
        _metadata metadata;
        private int metadata_comments_length;
        public Dictionary comments;
        public FlacReader(string filepath)
        {
            metadata_comments_length = 0;
            metadata = new _metadata();
            comments = new Dictionary();
            this.filepath = filepath;
            user_comment_list_length = 0;
            FileStream stream;
            stream = File.OpenRead(filepath);
            if (stream == null)
                throw new FileNotFoundException("Il file non esiste");
            byte[] array = {0,0,0,0};
            stream.Read(array,0,4);
            if (!((array[0]==0x66)&&(array[1]==0x4C)&&(array[2]==0x61)&&(array[3]==0x43)))   //fLaC
                throw new Exception("Il file non è un file flac!");
            byte flag;
            do 
            {
                stream.Read(array,0,1);  //mi sposto di 1 byte
                flag = array[0];
                metadata.setMetadata(flag);
                stream.Read(array, 0, 3);  //leggo tre byte per la lunghezza del metadata
                Int32 metadata_length = array[0] * 65536 + array[1] * 256 + array[2];
                if (flag == 4 || flag == 132)   //se è un vorbis comment cioè 00000100 oppure 10000100
                {
                    metadata_comments_length = metadata_length;
                    caricaCommenti(stream);
                }
                else
                    stream.Seek(metadata_length, SeekOrigin.Current); //mi sposto della lunghezza del metadata
            } while (!((flag>>7)==0x1)); //se il bit più significativo è uguale a uno vuol dire ch enon ci sono più metadata
            stream.Close();
        }
        public void setVendor(string vendorName)
        {
            int vendor_string_length_old = vendor_string.Length;  
            metadata_comments_length -= vendor_string_length_old;//tolgo la lunghezza iniziale
            vendor_string = vendorName;
            metadata_comments_length += vendor_string.Length;  //e metto la nuova lunghezza del vendor string
        }
        public string getVendor()
        {
            return vendor_string;
        }
        public void addComment(string field, string value)
        {
            try
            {
                if (comments[field] != null)   //se il commento già esiste
                {
                    string val = comments[field];
                    metadata_comments_length -= (field.Length + val.Length + 1);   //tolgo la lunghezza originaria
                    metadata_comments_length -= 4;
                }
            }
            catch(KeyNotFoundException e)
            {
                comments[field] = value;
                user_comment_list_length++;
                metadata_comments_length += 4; //aggiungo 4 byte per memorizzare la lunghezza del comment sul file
                metadata_comments_length += field.Length + value.Length + 1;  //aggiungo la lunghezza del commento più il simbolo uguale
            }
            
            
        }
        public void writeAll()  //file esistente quindi riverso il contenuto su un file _temp
        {
            FileStream stream,streamWrite;
            stream = File.OpenRead(filepath);
            streamWrite = File.OpenWrite(filepath + "_temp");
            byte[] array = { 0, 0, 0, 0 };
            stream.Read(array, 0, 4);
            streamWrite.Write(array, 0, 4);
            byte flag;
            do
            {
                stream.Read(array, 0, 1);  //mi sposto di 1 byte
                streamWrite.Write(array, 0, 1);  //mi sposto di 1 byte
                flag = array[0];
                stream.Read(array, 0, 3);  //leggo tre byte per la lunghezza del metadata
                Int32 metadata_length = array[0] * 65536 + array[1] * 256 + array[2];
                if (flag == 4 || flag == 132)  //se è un vorbis comment cioè 00000100 oppure 10000100
                {
                    stream.Seek(metadata_length, SeekOrigin.Current); //mi sposto sul lettore della lunghezza del metadata
                    array[0] = (byte) (metadata_comments_length >> 16);
                    array[1] = (byte) (metadata_comments_length >> 8);
                    array[2] = (byte)(metadata_comments_length);
                    streamWrite.Write(array, 0, 3);  //scrivo la nuova lunghezza del metadata
                    scriviCommenti(streamWrite);
                }
                else
                {
                    streamWrite.Write(array, 0, 3);  //scrivo la lunghezza del metadata
                    int i = 0;
                    for (i = 0; (i+4)< metadata_length; i+=4)
                    {
                        stream.Read(array, 0, 4);
                        streamWrite.Write(array, 0, 4);
                    }
                    /* scrivo i byte rimanenti */
                    stream.Read(array, 0, metadata_length-i);
                    streamWrite.Write(array, 0, metadata_length -i);
                }
            } while (!((flag >> 7) == 0x1)); //se il bit più significativo è uguale a uno vuol dire ch enon ci sono più metadata
            /* scrivo tutto il rimanente */
            byte[] chunk = new byte[10240];
            
               
            int num = 0;
            while (true)
            {
                num = stream.Read(chunk, 0, 10240);
                streamWrite.Write(chunk, 0, num);
                if (num < 10240)
                {
                    stream.Close();
                    streamWrite.Close();
                    return;
                }
            }
            
        }
        public void writeAll(String filename)  //nuovo file
        {
        }
        private void caricaCommenti(Stream stream)
        {
            byte[] array = { 0, 0, 0, 0 };
            stream.Read(array, 0, 4);
            uint vendor_length = (uint)((array[0]));  //non capisco perché ma conta solo il primo byte
            byte[] stringa = new byte[1024];
            stream.Read(stringa, 0, (int)vendor_length);
            vendor_string = System.Text.Encoding.UTF8.GetString(stringa,0,(int)vendor_length);
            stream.Read(array, 0, 4); //leggo il numero dei commenti
            uint number_of_comments = (uint)array[0];
            for (uint i = 0; i < number_of_comments; i++)
            {
                stream.Read(array, 0, 4); //leggo il numero di caratteri da leggere
                uint comment_length = (uint)array[0];
                stream.Read(stringa, 0, (int)comment_length);
                String comment = System.Text.Encoding.UTF8.GetString(stringa, 0,(int) comment_length);
                comments[comment.Split('=')[0]] = comment.Split('=')[1];
                Array.Clear(stringa, 0, stringa.Length);
            }
        }
        private void scriviCommenti(Stream stream)
        {
            Int32 length = vendor_string.Length;
            byte[] array = { 0, 0, 0, 0 };
            array[0] = (byte)(length);
            array[1] = 0;
            array[2] = 0;
            array[3] = 0;
            stream.Write(array, 0, 4);
            byte[] arrayName = System.Text.Encoding.UTF8.GetBytes(vendor_string.ToCharArray());
            stream.Write(arrayName, 0, length);
            byte numcommenti = (byte)comments.Count;
            array[0] = (byte)(numcommenti);
            stream.Write(array, 0, 4);
            /* scrivo i commenti */
            foreach (KeyValuePair entry in comments)
            {
                string comment = entry.Key + "=" + entry.Value;
                length = comment.Length;
                arrayName = System.Text.Encoding.UTF8.GetBytes(comment.ToCharArray());
                array[0] = (byte)length;
                array[1] = 0;
                array[2] = 0;
                array[3] = 0;
                stream.Write(array, 0, 4);
                stream.Write(arrayName, 0, length);
            }
            data_written = true;
        }
    }
}

giovedì 28 gennaio 2016

Come collegare il gamepad della prima console xbox sul PC

Avevo in cantina una vecchia xbox, la prima serie. Essendo obsoleta, ho pensato di recuperarne almeno il sempre valido joystick, o gamepad che dir si voglia. 
Ha 2 analogici, 2 triggers, 1 d-pad e 8 tasti: il gamepad della xbox 360 (nativamente supportato su windows, e con presa USB) ha solo un tasto in più.
Il modello in questione è il controller s, il secondo gamepad usato per questa console

Questo gamepad ha una presa proprietaria della Microsoft e credo che non appartenga a nessun standard, almeno stando alle informazioni che sono riuscito a racimolare sul web. 
Il vantaggio è che questa presa è assolutamente analoga a quella USB: in poche parole, non basta essere un elettrotecnico per trasformare questa presa in una USB e quindi, collegarla al PC.
Infatti sia la presa USB che la presa del gamepad hanno lo stesso numero di fili aventi lo stesso colore  (4 fili, in verità il gamepad ne ha uno in
La presa del gamepad
più, il giallo, che non viene utilizzato però). In fin dei conti, basta prendere un vecchio cavo USB e recidere in due sia quest'ultimo che il filo del gamepad, quindi collegare i fili; se si è bravi si può saldare ogni singolo filo e isolare singolarmente i quattro fili. 

Fatto questo, collegare il gamepad al PC e Windows riconoscerà una nuova periferica, ma fallirà nel trovare i driver appropriati. Allora ecco che un nerd della rete è venuto in aiuto: ha sviluppato i driver necessari per far funzionare e riconoscere il gamepad al PC come una
I fili collegati
periferica di gioco. Se avete un sistema a 32 bit potete scaricare da qui l'installer (scegliete l'ultima versione) che installerà tutto automaticamente, riavviate e il gioco è fatto. 
Se invece avete un sistema a 64bit questi driver, compilati per 32bit, non funzioneranno. Ma ecco che un altro bravo nerd della rete è venuto di nuovo in aiuto e ha compilato i driver anche per i sistemi a 64bit. Vi illustro in alcuni passaggi quello che ho fatto:
  1. Da questa pagina ho copiato sugli appunti i caratteri strani che stanno tra "begin" e "end" (compresi), dunque ho aperto Notepad++ (dovrebbe andar bene anche il blocco note, ma se usate ancora quest'ultimo siete sfigati), incollato il tutto e salvato come file avente estensione .7z
  2. Ho scaricato un encoder/decoder per decodificare il tutto da qui ed estratto il file UUDECODE.EXE sulla stessa cartella del file .7z
  3. Ho aperto il prompt (dal menù Start, scrivere cmd e premere invio), mi sono spostato sulla cartella (cd "percorso cartella) e scritto UUDECODE <nomefile.7z>  (con il nome del file .7z che avete salvato al punto 1) a premuto invio
  4. Poi ho aperto il file xbcd_108.7z che si è creato (l'ho aperto con 7zip, ma va bene anche winrar) ed estratto il contenuto in una cartella.
  5. Sono andato nel Pannello di Controllo->Sistema->Gestione dispositivi e individuato la periferica non riconosciuta (contrassegnata da un punto esclamativo su sfondo giallo), dunque cliccato con tasto destro->Proprietà, nella finestra sono andato nella sezione "Driver" e cliccato su "Aggiorna driver..." e poi "Cerca il software del driver nel computer", dunque ho scelto la sottocartella "x64" della cartella dove ho scompattato xbcd_108.7z
  6. Dopo avere atteso un po', Windows ci chiede se siamo sicuri di voler installare un software senza firma; scegliamo si. 
I sistemi Windows a 64bit controllano a ogni avvio se ci sono driver senza firma digitale, in tal caso li blocca e ne impedisce l'esecuzione. In poche parole,allo stato attuale, il gamepad ancora non vi funziona. Se voi avete un modo di mettere la firma digitale bene, altrimenti dovrete eludere il succitato controllo. All'avvio di Windows, premete continuamente F8 e scegliere l'ultima opzione, quella relativa al controllo dei driver non firmati. Questo farà si che all'avvio non verranno controllati i driver e finalmente il nostro gamepad funzionerà. Andate nel pannello di controllo e cercate "gioco", dovreste trovare "Configura dispositivi di gioco USB". Da lì dovreste vedere se il gamepad funziona bene. 

In aggiuntiva, dovrete installare il software che gestisce le periferiche di gioco, cioè un software che sfrutta al meglio le potenzialità di questo gamepad con cui possiamo associare le funzioni di gioco a ogni tasto del gamepad; all'inizio io ho usato il molto valido Pinnacle Game Profiler, ma vista la sua natura shareware (ormai mi è scaduto, comunque il prezzo è molto basso) sono passato a una valida alternativa freeware: Xpadder. Potrete seguire le istruzioni di questo video per la configurazione.

lunedì 31 agosto 2015

Un programma per aggiungere lo "staccato" agli spartiti in MusicXML

Prima
Dopo
Ne approfitto per rispolverare questo blog su cui non scrivo da molto.

Ho sviluppato un programma in java che in principio doveva essere un plugin interno a MuseScore (un software libero di notazione musicale), ma viste alcune complicazioni, ho deciso di creare un programma in java con cui si può fare tutto.

In pratica esso agisce su un file MusicXML (file standard per la notazione musicale che MuseScore può esportare facilmente), e aggiunge lo "staccato" alle note seguite da una pausa avente lo stesso valore. Rende lo spartito più leggibile.

L'ho sviluppato per mettere fine una volta per tutte alla modifica manuale degli spartiti. 

Il prossimo passo sarà quello di aggiungere il punto a quelle note legate a una stessa nota ma con la metà del loro valore.

Per sviluppare il programma (sviluppato e lasciato in linea di comando) mi sono servito della libreria JDOM per manipolare i file XML.

Da qui potete scaricare sorgenti e compilati del programma. 

Per il funzionamento è necessario estrarre in una cartella AddStaccato.jar e le directory DTD e lib.
Per lanciare il programma devi avere java sul tuo pc e le variabili d'ambiente impostate.
Quindi lanci il comando:

java -jar <percorso_del_programma_addstaccato> <opzioni> <valori> nomefile.xml

le opzioni sono:

-r seguito da un numero, specifica il numero della parte (=rigo musicale) da modificare
-i seguito da due numeri che rappresentato la prima e l'ultima battuta (incluse) da modificare

Esempio:


java -jar <percorso_del_programma_addstaccato> -r 4 -i 15 74 nomefile.xml

modifica le battute comprese tra 15 e 74 (inclusi) del rigo numero 4 di nomefile.xml

bye


martedì 26 novembre 2013

Gnome Launcher Creator, piccola utility per creare lanciatori per GNOME

Ho scritto in giornata questa piccola utility che permette di creare dei lanciatori per GNOME in maniera facile, appoggiandosi all'utility per GNOME gnome-desktop-item-edit.

Potete scaricare il sorgente da QUI oppure (e cio è molto triste perché ricorda windows) scaricare direttamente l'eseguibile da QUI.  Se lo dovete compilare assicuratevi di avere installato il pacchetto libgtk-3-dev necessario per le librerie GTK+,  e se avete configurato anche pkg-config, vi basta eseguire da terminale il comando

make 

sulla cartella contenente il file Makefile per compilare direttamente e creare l'eseguibile GLC.

lunedì 18 novembre 2013

Calcolare il determinante di una matrice di qualsiasi ordine in maniera ricorsiva - Software grafico GNU/Linux

Come avevo promesso, ecco la versione GNU/Linux del programma grafico per calcolare il determinante che ho presentato nel precedente post (a dir la verità ci ho fatto qualche lieve modifica)


A differenza della versione per Windows, su Linux con molta probabilità avrete già le librerie installate sul sistema, quindi ecco a voi solo l'eseguibile (per versioni di GTK > 3.0):


martedì 12 novembre 2013

Calcolare il determinante di una matrice di qualsiasi ordine in maniera ricorsiva - Software grafico Windows

Tempo fa ho scritto un post che descriveva il metodo per calcolare il determinante di una matrice in maniera ricorsiva (metodo di Laplace) e mostravo la funzione creata per C++ per calcolare il determinante data una matrice quadrata di un qualsiasi ordine. 

Ho creato un'interfaccia grafica scritta con GTK+ dove si può comodamente inserire la matrice di cui si vuole calcolare il determinante. Per ora il programma è compilato con windows perché in questo momento non ho un linux a portata di mano, quindi l'eseguibile che potrete scaricare è per windows. 

Il programma inizia con un prompt, chiedendo all'utente di inserire l'ordine della matrice:
subito dopo, inserendo un ordine di 3 per esempio, avremo la schermata:

in cui potremmo inserire i valori e calcolare il determinante:

Il programma non è limitato a un certo ordine, teoricamente si può inserire qualsiasi ordine, salvo tempi che si allungano per via dell'elaborazione prima della parte grafica, poi (più veloce) la parte computazionale. I problemi derivati dall'uso di GTK+ su windows è che queste librerie sono nate in ambiente linux, e il porting per windows è complesso e macchinoso rispetto altre librerie grafiche. Ma io sono affezionato a Ubuntu e Gnome :)

Lo potete scaricare da qui (windows):
Istruzioni:
estraete i files in una cartella e lanciate "Determinante.bat", il quale lancerà a sua volta "Determinante.exe" contenuto nella cartella "lib". Ho dovuto fare questo trucchetto perché il programma per funzionare ha bisogno delle librerie GTK+.

A breve anche la versione per linux.

mercoledì 30 ottobre 2013

Ideone, il sito definitivo per i programmatori

L'ho scoperto, non ci credevo quasi! Fantastico tool online per programmatori che permette di condividere spezzoni di codice in tutti i linguaggi più famosi e più usati, con tanto di output e stdin. 
Per esempio, classico codice per i neofiti:


#include 
int main(void) {
 int a,b;
 printf ("Inserisci il primo numero: ");
 scanf("%d",&a);
 printf ("Inserisci il secondo numero: ");
 scanf("%d",&b);
 printf("La loro somma è %d",a+b);
 return 0;
}

riportato su questo sito crea un link temporaneo (o perlomeno, ho visto codici vecchi sei mesi, non so quando viene cancellato, se viene cancellato) con il codice memorizzato e lo stdin:

http://ideone.com/aVxwyp

Credo che lo userò molto da ora in poi.

giovedì 29 marzo 2012

Convertitore UNIX Timestamp - Data in Java

Bene, per una mia applicazione Java avevo sempre questo problema del UNIX timestamp (ovvero il numero di secondi trascorsi dal 1 Gennaio 1970 alle ore 00:00:00), ovvero convertire una data in formato anno, mese, giorno, ore minuti e secondi in UNIX timestamp. Forse non ho cercato bene nella libreria di Java o forse non ho googlato per bene, ma una funzione che facesse ciò non l'ho trovata, solo timestamp dell'ora corrente (che non mi interessava).

Avevo urgentemente bisogno dell'algoritmo per calcolarlo che per pigrizia non ho mai scritto; adesso con il cambiamento all'ora legale è seguito un nuovo malfunzionamento del mio programma e quindi ho deciso di scriverlo una volta per tutte e condividerlo con chi ne avesse bisogno. 

L'algoritmo si compone di due parti: data una data (scusate il gioco di parole), la prima parte calcola il numero di secondi trascorsi fino al primo gennaio dell'anno corrente alle 00:00:00, la seconda parte aggiunge il "resto", ovvero il numero di secondi mancante. 

Specularmente, ho riciclato il suddetto algoritmo per fare il procedimento inverso, e ne è uscita fuori una bella classe che contiene i due metodi statici. 
Innanzitutto ho creato una classe ausiliaria Data, invece di utilizzare la java.util.Date.

package it.albe;

/**
 *
 * @author Alberto
 * 29/03/2012
 */
public class Data {
    public Data(int a,int m,int g, int o, int min, int sec){
        anno = a;
        mese = m;
        giorno = g;
        ore = o;
        minuti = min;
        secondi = sec;
    }
    public String toString(){
        String ms;
        switch(mese){
            case 1: ms = "Gennaio";break;
            case 2: ms = "Febbraio";break;
            case 3: ms = "Marzo";break;
            case 4: ms = "Aprile";break;
            case 5: ms = "Maggio";break;
            case 6: ms = "Giugno";break;
            case 7: ms = "Luglio";break;
            case 8: ms = "Agosto";break;
            case 9: ms = "Settembre";break;
            case 10: ms = "Ottobre";break;
            case 11: ms = "Novembre";break;
            case 12: ms = "Dicembre";break;
            default: ms="";
        }
        return String.valueOf(giorno)+" "+ms+" "+
                String.valueOf(anno)+" "+
                String.valueOf(ore)+":"+
                String.valueOf(minuti)+":"+
                String.valueOf(secondi);
    }
    public int anno;
    public int mese;
    public int giorno;
    public int ore;
    public int minuti;
    public int secondi;
}
Fatto ciò, ecco allora la classe Timestamp:

package it.albe;

/**
 *
 * @author Alberto
 * 29/03/2012
 */
public class Timestamp {
    public static long toUNIXtimestamp(Data data){
        long timestamp = 0;     
        for (int a=1970;a < data.anno;a++){ //anno
            if (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0)){  //bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            for (g=1;g <= 31;g++)
                               timestamp+= 86400;
                        }break;
                        case 2: {
                            int g;
                            for (g=1;g <= 29;g++)
                               timestamp+= 86400;
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            for (g=1;g <= 30;g++)
                               timestamp+= 86400;
                        }break;
                    } //switch                
            } //if
            else {      //non bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            for (g=1;g <= 31;g++)
                               timestamp+= 86400;
                        }break;
                        case 2:{
                            int g;
                            for (g=1;g <= 28;g++)
                               timestamp+= 86400;
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            for (g=1;g <= 30;g++)
                               timestamp+= 86400;
                        }break;
                    } //switch
            } //else
        }    //for anno
        /***********************************************************/
        /*                   AGGIUNGO RESIDUO                      */
        /***********************************************************/
        if (data.anno % 4 == 0 && (data.anno % 100 != 0 || data.anno % 400 == 0)){
            int m;
            for (m=1;m < data.mese;m++)
                switch(m){
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12: {
                        int g;
                        for (g=1;g <= 31;g++)
                           timestamp+= 86400;
                    }break;
                    case 2: {
                        int g;
                        for (g=1;g <= 29;g++)
                           timestamp+= 86400;
                    }break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:{
                        int g;
                        for (g=1;g <= 30;g++)
                           timestamp+= 86400;
                    }break;
               } //switch  
           int g;
           for (g=1;g < data.giorno;g++)
               timestamp+= 86400;
           timestamp += data.secondi+data.minuti*60+data.ore*3600;             
        }
        else {
             int m;
            for (m=1;m < data.mese;m++)
                switch(m){
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12: {
                        int g;
                        for (g=1;g <= 31;g++)
                           timestamp+= 86400;
                    }break;
                    case 2: {
                        int g;
                        for (g=1;g <= 28;g++)
                           timestamp+= 86400;
                    }break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:{
                        int g;
                        for (g=1;g <= 30;g++)
                           timestamp+= 86400;
                    }break;
               } //switch  
           int g;
           for (g=1;g < data.giorno;g++)
               timestamp+= 86400;
           timestamp += data.secondi+data.minuti*60+data.ore*3600;  
        }
        return timestamp;
    }
    public static Data toDate(long Ptimestamp){
        int a;
        long timestamp = 0;
        for (a=1970;a < 2038;a++){ //anno
            if (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0)){  //bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 31;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            
                                            }
                                        }
                        }break;
                        case 2: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 29;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 30;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                    } //switch                
            } //if
            else {      //non bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 31;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 2:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 28;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 30;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                    } //switch
            } //else
        }    //for anno 
        return new Data(0,0,0,0,0,0);
    }
    
}
In pratica il metodo toUNIXtimestamp() nella prima parte fa un ciclo dove anno per anno controlla innanzitutto se è bisestile, e mese per mese controlla se è un 31 o un 28 o un 30, e poi giorno per giorno aumenta il timestamp di 86400 (numero di secondi in un giorno), mentre la seconda parte procede in maniera analoga tenendo conto della data di cui si vuol calcolare il timestamp.

L'altro metodo è analogo e non c'è nemmeno bisogno che lo spieghi.

Ed ecco un utilizzo esemplare di questa classe:


        Data data = new Data(2012,3,29,21,48,45); //29 marzo 2012 alle 21:48 e 45 secondi
        long ts = toUNIXtimestamp(data);
        System.out.print(ts+"\n");
        data = toDate(ts);
        System.out.print(data);

giovedì 15 dicembre 2011

Calcolare il determinante di una matrice di qualsiasi ordine in maniera ricorsiva

Nota: in data 12/11/2013 ho creato un programma con interfaccia grafica per windows: http://newbufferedwriter.blogspot.com/2013/11/calcolare-il-determinante-di-una.html
e per GNU/linux:
http://newbufferedwriter.blogspot.com/2013/11/calcolare-il-determinante-di-una_18.html

Era da tempo che mi volevo cimentare in un programma del genere. Per il corso di elettrotecnica avevo sviluppato un programma in C# per calcolare il determinante di una matrice 3x3, che mi è servito per calcolare la soluzione ai sistemi di equazioni per i circuiti; ma comunque era limitato solamente a matrici 3x3.

Ho pensato pochi giorni fa di implementare un programma del genere con la ricorsione. Dalla matematica "pratica" sappiamo che calcolare il determinante per matrici di grosse dimensioni è complesso, specialmente se si usa la definizione fondamentale del determinante. Io in questo programma ho usato lo sviluppo di Laplace avete questa forma:


che come si può notare, la formula stessa è ricorsiva, ovvero il determinante compare nell'espressione stessa. det Mij    è il determinante della matrice estrapolata dalla matrice originale escludendo la riga i e la colonna j, quindi in definitiva l'ordine diminuisce di 1 ad ogni ricorsione.  Quando si "ferma" la ricorsione? Semplice: quando la matrice raggiunge l'ordine 2, che, essendo una matrice elementare, è possibile con un'espressione elementare (non più ricorsiva) calcolare il suo determinante.
Facciamo un esempio. Consideriamo la seguente matrice di ordine 3:

| 2   8   6 |
| 4   5   2 |
| 1   7   4 |
Il suo determinante si può calcolare in questo modo:
1)Scegliamo una riga (per esempio la riga 1: {2,8,6}). Di solito nella pratica si sceglie una riga con molti zeri, ma in questo caso non ci sono zeri; e comunque ci dobbiamo mettere nei panni del calcolatore, che prende sempre e comunque la riga 1 per questo programma che ho creato.
2)Moltiplichiamo ogni elemento della riga per il suo complemento algebrico (ovvero il determinante della matrice ottenuta eliminando la riga 1 e la colonna del singolo elemento)
3)Addizioniamo i 3 risultati trovati, avendo cura di preporre ad ogni singolo elemento il segno, negativo o positivo che sia (Delta di Kronecker, in questo caso poitivo se la colonna è dispari, negativo se è pari).

           | 2   8   6 |
 det     | 4   5   2 |    =  2 * det | 5  2|     -  8 * det | 4   2|    +  6 * det | 4  | 5 |  = 2 * (5*4-2*7) - 
           | 1   7   4 |                    |7   4|                     | 1   4|                    | 1  | 7 |

- 8*(4*4-2*1)  + 6 *(4*7-5*1) = 2*6 - 8*14 + 6*23 = 38

Il seguente programma scirtto in C++ fa la stessa cosa per matrici di qualsiasi dimensione. Ho definito la funzione det che accetta come parametri la matrice (ovvero un doppio puntatore a interi) e il suo ordine. Nel metodo main ho inserito per esempio la matrice che abbiamo analizzato, ma potete crearne voi una qualsiasi.

#include <iostream>

using namespace std;

int det(int**,int);

int main(){
    int** m;
    m = new int*[3];        //ordine 3, alloco il primo vettore
    for (int i=0;i<3;i++)   //alloco gli altri vettori
        m[i] = new int[3];
    m[0][0]=2;m[0][1]=8;m[0][2]=6;
    m[1][0]=4;m[1][1]=5;m[1][2]=2;
    m[2][0]=1;m[2][1]=7;m[2][2]=4;
    int deter = det(m,3);
    cout << "Il determinante e': " << deter;
}

int det(int** matrice,int ordine){
    if (ordine==2)    
       return (matrice[0][0]*matrice[1][1] - matrice[0][1]*matrice[1][0]);
    else {
       int provv=0;  //risultato "provvisorio", ovvero diventa definitivo quando la ricorsione termina
       bool col=false; //variabile utile per creare la sottomatrice
       bool segnoNegativo = false;  //variabile che tiene conto del simbolo di Kronecker
       int elem;       
       for (int i=0;i<ordine;i++){     //colonna
          int** matrix;
          /*ALLOCO MEMORIA PER LA NUOVA MATRICE*/
          matrix = new int*[ordine-1];
          for (int c=0;c<ordine-1;c++)
              matrix[c] = new int[ordine-1];
           /*---------------FINE----------------*/
          for (int r=1;r<ordine;r++){ //inizio a creare la nuova matrice
              for (int c=0;c<ordine;c++){
                  if (c==i) {        
                      col = true; 
                      c++;         //salta questa colonna e vai avanti
                      if (c==ordine)     //controllo per non fare eccedere i limiti
                         break;
                  }
                  if (col)
                      matrix[r-1][c-1] = matrice[r][c];
                  else
                      matrix[r-1][c] = matrice[r][c];
               }
               col=false;
          } //Fine r
          if (segnoNegativo)
             elem = -matrice[0][i];
          else
             elem = matrice[0][i];
          provv += elem * det(matrix,ordine-1);
          segnoNegativo = !(segnoNegativo);
       }  //fine i
    return provv;
    }   //fine else
}

In sintesi, cosa fa il programma, in particolare la funzione int det(int** matrice,int ordine);  ?
1)Per prima cosa controlla l'ordine. Se è due, restituisce immediatamente il risultato, altrimenti continua.
2)Crea una matrice di ordine ordine-1 e la inizializza con la sottomatrice (col  è una variabile boolean utile per sapere qual'è la colonna a cui appartiene l'elemento).
3)Procede per aggiungere alla variabile provv (inizializzata a zero) il prodotto del singolo elemento per il suo complemento algebrico (ovvero una nuova chiamata alla stessa funzione det(); qui sta la ricorsione), tenendo conto del segno tramite la variabile boolean segnoNegativo.
4)Finito il ciclo della variabile temporanea i, restituisce il risultato (provv).


Analizziamo cosa succede in memoria. Il caso di ordine 3 è abbastanza semplice perché necessita di 3 cicli ed una sola ricorsione. La memoria della matrice di ordine 3-1 = 2 viene liberata alla fine di ogni ciclo ma comunque ad ogni ricorsione si accumula, quindi in definitiva per questo caso la massima memoria utilizzata contemporaneamente è quella per memorizzare una matrice 3x3 e una 2x2, ovvero rispettivamente 48 e 24 byte, quindi 48+24 = 72byte. Per una matrice di ordine 4 la massima memoria utilizzata contemporaneamente è quella per memorizzare una matrice 4x4, una 3x3 e una 2x2, ovvero rispettivamente 80 + 48 + 24 = 152. L'espressione della memoria occupata da una matrice di ordine n è:
   4 * (n^2 + n)
in questo modo ho stilato una tabella che ci indica quanta memoria viene utilizzata dal programma al crescere dell'ordine n:

n | Max memoria(byte)
--|---------------------
2|                          24
3|                          72
4|                        152
5|                        278
6|                        440
7|                        664
8|                        952
9|                      1312
10|                         1752

Tutto ciò era solamente per uno scopo goliardico...comunque sia la memoria utilizzata non è poi tanta...no?
Alberto