JavascriptProva

giovedì 31 marzo 2016

La mia vecchia libreria jacSpeech: la classe per il riconoscimento vocale.

Ed ecco un bel codice che ho creato un paio di anni fa in VB.NET e che devo ristudiare...

Imports System.Threading
Public Class jacRecognizer
    Dim WithEvents motore As New SpeechRecognitionEngine(New Globalization.CultureInfo("it-IT"))
    Dim costruttore As GrammarBuilder
    Dim grammatica As Grammar

    Public Event riconoscimento(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)
    Public Event noise(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs)
    Public lista As New ArrayList
    Public matrice() As String

    Sub New()
        motore.SetInputToDefaultAudioDevice()
        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")
        Dim scelte As New Choices("u")
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
    Sub New(ByVal nomeFile As String)
        motore.SetInputToDefaultAudioDevice()

        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")

        Dim str As String = ""


        FileOpen(1, nomeFile, OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            lista.Add(str)
        Loop
        FileClose(1)

        ReDim matrice(lista.Count - 1)
        lista.CopyTo(matrice)

        Dim scelte As New Choices(matrice)
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
    Public Sub recognizing(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
        RaiseEvent riconoscimento(sender, e)
    End Sub
    Public Sub rumore(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs) Handles motore.SpeechDetected
        RaiseEvent noise(sender, e)
    End Sub
End Class



Public Class jacSpeaker
    Public sintetizzatore As New SpeechSynthesizer
    Sub New()
        sintetizzatore.SetOutputToDefaultAudioDevice()
        sintetizzatore.SelectVoice("Microsoft Server Speech Text to Speech Voice (it-IT, Lucia)")
        sintetizzatore.Volume = 100
    End Sub
    Sub parla(ByVal parola As String)
        sintetizzatore.Speak(parola)
    End Sub
End Class


Public Class jacEngRecognizer
    Dim WithEvents motore As New SpeechRecognitionEngine(New Globalization.CultureInfo("en-US"))
    Dim costruttore As GrammarBuilder
    Dim grammatica As Grammar

    Public Event riconoscimento(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)

    Public lista As New ArrayList
    Public matrice() As String


    Sub New(ByVal nomeFile As String)
        motore.SetInputToDefaultAudioDevice()

        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("en-US")

        Dim str As String = ""


        FileOpen(1, nomeFile, OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            lista.Add(str)
        Loop
        FileClose(1)

        ReDim matrice(lista.Count - 1)
        lista.CopyTo(matrice)

        Dim scelte As New Choices(matrice)
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
    Public Sub recognizing(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
        RaiseEvent riconoscimento(sender, e)
    End Sub

End Class


Public Class jacRecorder
    Declare Function mciSendString Lib "winmm" Alias "mciSendStringA" (ByVal messaggio As String, ByVal retmessage As String, ByVal valore As Integer, ByVal handle As Integer) As Integer

    Public path As String
    Sub record(ByVal tempo As Integer)
        mciSendString("open new type waveaudio alias suono", 0, 0, 0)
        mciSendString("record suono", 0, 0, 0)
        Dim sw As New Stopwatch
        sw.Start()
        Thread.Sleep(tempo)
        sw.Stop()
        mciSendString("save suono " + path, 0, 0, 0)
        mciSendString("close suono", "", 0, 0)
    End Sub
    Sub play()
        mciSendString("open " + path + " alias suono", 0, 0, 0)
        mciSendString("play suono wait", 0, 0, 0)
        mciSendString("close suono", 0, 0, 0)

    End Sub
End Class

Bene. Ci sono due classi. Comincio a studiarmi la prima...
Public Class jacRecognizer
    Dim WithEvents motore As New SpeechRecognitionEngine(New Globalization.CultureInfo("it-IT"))
    Dim costruttore As GrammarBuilder
    Dim grammatica As Grammar

    Public Event riconoscimento(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)
    Public Event noise(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs)
    Public lista As New ArrayList
    Public matrice() As String

    Sub New()
        motore.SetInputToDefaultAudioDevice()
        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")
        Dim scelte As New Choices("u")
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
    Sub New(ByVal nomeFile As String)
        motore.SetInputToDefaultAudioDevice()

        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")

        Dim str As String = ""


        FileOpen(1, nomeFile, OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            lista.Add(str)
        Loop
        FileClose(1)

        ReDim matrice(lista.Count - 1)
        lista.CopyTo(matrice)

        Dim scelte As New Choices(matrice)
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
    Public Sub recognizing(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
        RaiseEvent riconoscimento(sender, e)
    End Sub
    Public Sub rumore(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs) Handles motore.SpeechDetected
        RaiseEvent noise(sender, e)
    End Sub
End Class
Dunque... innanzitutto vengono dichiarate delle variabili:
  • SpeechRecognitionEngine
  • GrammarBuilder
  • Grammar
E poi altre variabili di tipo Event eccetera... che vedrò dopo...

Intanto, nel costruttore di JacRecognizer si eseguono alcune istruzioni:
Viene chiamato il metodo
motore.SetInputToDefaultAudioDevice()
che probabilmente setta l'input per il riconoscitore al microfono del dispositivo.

Quindi viene fatto "qualcosa" a carico del GrammarBuilder (che io con una scelta infelice ho chiamato "costruttore" generando facile confusione con il costruttore della classe).
        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")
        Dim scelte As New Choices("u")
        costruttore.Append(scelte)
Per prima cosa viene istanziato l'oggetto della classe GrammarBuilder
costruttore = New GrammarBuilder


Viene attribuita la lingua a questo oggetto GrammarBuilder chiamato "costruttore":
costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")


Vengono create delle "scelte" (oggetti Choices) e "appese" al costruttore di tipo GrammarBuilder:
Dim scelte As New Choices("u")
        costruttore.Append(scelte)
Il significato di quella "u" mi sfugge completamente.

Ah, ecco! In pratica, costruiamo l'oggetto costruttore (GrammarBuilder) e gli attribuiamo due cose:
  • la proprietà Culture, ossia la lingua;
  • Le scelte, ossia le parole che comprenderà la "grammatica".
La grammatica verrà costruita semplicemente ponendo il costruttore come parametro nel suo costruttore (che brutta denominazione che ho scelto! Confusiva al massimo!)
        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")
        Dim scelte As New Choices("u")
        costruttore.Append(scelte)
e poi:
grammatica = New Grammar(costruttore)

...quindi il motore si "carica" la grammatica:
motore.LoadGrammar(grammatica)
Resta da studiare l'ultimo metodo del motore, RecognizeAsync...

Ecco: sarebbe mettere il motore in condizioni di eseguire il riconoscimento.
motore.RecognizeAsync(RecognizeMode.Multiple)
Il "Multiple" starebbe per "multiple operazioni di riconoscimento.


Nel mio jacRecognizer ho overloadato il costruttore, però, avendone anche un altro con un parametro:
    Sub New(ByVal nomeFile As String)
        motore.SetInputToDefaultAudioDevice()

        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")

        Dim str As String = ""


        FileOpen(1, nomeFile, OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            lista.Add(str)
        Loop
        FileClose(1)

        ReDim matrice(lista.Count - 1)
        lista.CopyTo(matrice)

        Dim scelte As New Choices(matrice)
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub
Ecco: il parametro fornito a questo costruttore è una stringa contenente il nome di un file.
Questo file viene aperto e le righe vengono lette e caricate in una ArrayList (quella che viene dichiarata e istanziata inizialmente nella dichiarazione della classe); quindi dalla ArrayList vengono poste in una matrice.
    Sub New(ByVal nomeFile As String)
        motore.SetInputToDefaultAudioDevice()

        costruttore = New GrammarBuilder
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("it-IT")

        Dim str As String = ""


        FileOpen(1, nomeFile, OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            lista.Add(str)
        Loop
        FileClose(1)

        ReDim matrice(lista.Count - 1)
        lista.CopyTo(matrice)
(in celeste, le operazioni di lettura del file il cui indirizzo è stato passato come parametro, in rosso la copiatura dell'ArrayList nella matrice)

Quindi questa matrice viene usata per creare delle scelte (Choices) e "appesa" al costruttore, col quale verrà costruita la grammatica, che sarà quindi caricata dal motore, come prima, quindi il motore si mette in ascolto:
        Dim scelte As New Choices(matrice)
        costruttore.Append(scelte)
        grammatica = New Grammar(costruttore)
        motore.LoadGrammar(grammatica)
        motore.RecognizeAsync(RecognizeMode.Multiple)
    End Sub

Restano le subroutines di elevazione degli eventi:
    Public Sub recognizing(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
        RaiseEvent riconoscimento(sender, e)
    End Sub
    Public Sub rumore(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs) Handles motore.SpeechDetected
        RaiseEvent noise(sender, e)
    End Sub
End Class
Questi eventi erano stati dichiarati inizialmente:
    Public Event riconoscimento(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)
    Public Event noise(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs)
E' importante, nelle subroutines che evocano gli eventi, la parte Handles:
       Public Sub recognizing(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
        RaiseEvent riconoscimento(sender, e)
    End Sub
    Public Sub rumore(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs) Handles motore.SpeechDetected
        RaiseEvent noise(sender, e)
    End Sub

Bene.
Adesso apro il programma che usa questo oggetto jacRecognizer...

E ho trovato la via spianata per la costruzione di tutto quello che voglio! Verso l'infinito e oltre!!!

martedì 29 marzo 2016

Promemoria: codice per il "pinch zoom" di un layout.

Ho sistemato il problema ponendo un minimo e un massimo:
public class MainActivity extends Activity {
 
  RelativeLayout mainLayout;
  LinearLayout ll;
  ScaleGestureDetector detector;
  float scala=1.f;
  int bmpWidth, bmpHeight;
  TextView textView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mainLayout=(RelativeLayout) findViewById(R.id.mainLayout);
        ll=new LinearLayout(this);
        RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
        params.width=300;
        params.height=200;
        ll.setLayoutParams(params);
        ll.setBackgroundColor(Color.GREEN);
        mainLayout.addView(ll);
        
        detector=new ScaleGestureDetector(this,new listener());
        bmpWidth=300;
        bmpHeight=200;
        textView=(TextView)findViewById(R.id.textView1);
        
        drawMatrix();
        
    }
    
    public void drawMatrix(){
     bmpWidth=(int) (Math.round(bmpWidth*scala));
     bmpHeight=(int) (Math.round(bmpHeight*scala));
     LayoutParams p=(LayoutParams)ll.getLayoutParams();
     p.width=bmpWidth;
     if(bmpWidth>=400){
      bmpWidth=400;
      bmpHeight=(int)(400/1.5);
     }
     if(bmpWidth<=220){
      bmpWidth=220;
      bmpHeight=(int)(220/1.5);
     }
     p.height=bmpHeight;
     ll.setLayoutParams(p);
    
     
     textView.setText(bmpWidth+" "+bmpHeight);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event){
     detector.onTouchEvent(event);
  return true;
     
    }
    
    class listener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
     
     @Override
     public boolean onScale(ScaleGestureDetector detector){
      scala=detector.getScaleFactor();
      drawMatrix();
   return true;
      
     }
    }
    
}

Annotazione di codice per il pinch zoom

Ecco un codice che sembra funzionare, per il pinch zoom di una bitmap:
public class MainActivity extends Activity {
 
  RelativeLayout mainLayout;
  ImageView immagine;
  Bitmap bitmap;
  ScaleGestureDetector detector;
  float scala=1.f;
  int bmpWidth, bmpHeight;
  TextView textView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mainLayout=(RelativeLayout) findViewById(R.id.mainLayout);
        
        
        detector=new ScaleGestureDetector(this,new listener());
        immagine=(ImageView)findViewById(R.id.imageView1);
        textView=(TextView)findViewById(R.id.textView1);
        
        
        
        bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.calcestruzzo);
        bmpWidth=bitmap.getWidth();
        bmpHeight=bitmap.getHeight();
        drawMatrix();
        
    }
    
    public void drawMatrix(){
     bmpWidth=(int) (bmpWidth*scala);
     bmpHeight=(int) (bmpWidth*scala);
     Bitmap resizedBitmap=Bitmap.createScaledBitmap(bitmap, bmpWidth, bmpHeight, false);
     immagine.setImageBitmap(resizedBitmap);
     textView.setText(bmpWidth+" - "+bmpHeight);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event){
     detector.onTouchEvent(event);
  return true;
     
    }
    
    class listener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
     
     @Override
     public boolean onScale(ScaleGestureDetector detector){
      scala=detector.getScaleFactor();
      
      drawMatrix();
   return true;
      
     }
    }
    
}

Me lo annoto prima di giocherellarci sopra...

sabato 26 marzo 2016

Zoomare l'immagine con due dita (ScaleGestureDetector e SimpleOnScaleGestureListener)

Ridimensionare un'immagine con due dita.

Ci sto combattendo un po'...

Credo di essere arrivato a qualche conclusione.
Esiste una classe che si chiama ScaleGestureDetector.
Questa classe viene creata con un costruttore che assume come parametri context e un listener, per cui va creato anche il listener.

Dopo aver scritto un codice funzionante modificando pedissequamente il codice trovato in rete, come al solito rompo il Mandala e lo ricostruisco daccapo a titolo di esercitazione...

public class MainActivity extends Activity {
 
 //dichiarazioni
 ScaleGestureDetector scaleGestureDetector;
 
 RelativeLayout mainLayout;
 ImageView imageView;
 Bitmap bitmap;
 int bitmapHeight,bitmapWidth;
 float scala=1.f;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  scaleGestureDetector=new ScaleGestureDetector(this,new simpleOnScaleGestureListener());
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  imageView=new ImageView(this);
  mainLayout.addView(imageView);
  
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.calcestruzzo);
  bitmapHeight=bitmap.getHeight();
  bitmapWidth=bitmap.getWidth();  
  drawMatrix();
   
 }
 private void drawMatrix(){
  bitmapWidth*=scala;
  bitmapHeight*=scala;
  Bitmap resizedBitmap=Bitmap.createScaledBitmap(bitmap, bitmapWidth, bitmapHeight, false);
  imageView.setImageBitmap(resizedBitmap);
 }
 
 @Override
 public boolean onTouchEvent(MotionEvent event){
  scaleGestureDetector.onTouchEvent(event);
  return true;
  
 }
 private class simpleOnScaleGestureListener extends SimpleOnScaleGestureListener{
  
  @Override
  public boolean onScale(ScaleGestureDetector detector){
   scala=detector.getScaleFactor();
   drawMatrix();
   return true;
   
  }
 } 

}
Ecco: solo che aumentando e diminuendo le dimensioni un po' di volte mi allunga l'immagine...
Ma per il momento c'è da essere soddisfatti!

lunedì 21 marzo 2016

FileInputStream

Ho messo un file wav nella memoria dell'emulatore: si tratta di un file che ho preso da qualche parte, contenente una canzoncina comica, tanto per mantenere l'allegria, e voglio leggere il file in un buffer.
Ecco il codice:
public class MainActivity extends Activity {

// dichiarazioni di variabili oggetto dell'activity
 Button button;
 Button button2;
 Button button3;
 Button button4;

//il percorso del file
 String filePath = Environment.getExternalStorageDirectory()
   .getAbsolutePath() + "/vaffanculo.wav";

// registratore e riproduttore (dichiarazioni)
 AudioRecord audioRecord;
 AudioTrack audioTrack;

//impostazioni audio
 int audioSource=AudioSource.MIC;
 int sampleRate=44100;
 int channelConfig=AudioFormat.CHANNEL_IN_STEREO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int minBufferSize=AudioRecord.getMinBufferSize(sampleRate, 
             channelConfig, 
             audioFormat);
 
// dichiarazione del buffer
 byte[] buffer= new byte[minBufferSize];
 
 
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  
//istanziazione dei bottoni
  button = (Button) findViewById(R.id.button1);
  button2 = (Button) findViewById(R.id.button2);
  button3 = (Button) findViewById(R.id.button3);
  button4 = (Button) findViewById(R.id.button4);

//listeners
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    try {
     
     FileInputStream fis=new FileInputStream(filePath);
     fis.read(buffer,0,minBufferSize);
     for(int i=0; i < minBufferSize;i++){
      Log.v(i+"",buffer[i]+"");
     }
    } catch (FileNotFoundException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
    
    
   }
  });
    

  button2.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    
   }
  });
  button3.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    
   }
  });
  
  button4.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    
   }
  });
  
// termine listeners
 }

}
La parte notevole è questa:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    try {
     
     FileInputStream fis=new FileInputStream(filePath);
     fis.read(buffer,0,minBufferSize);
     for(int i=0; i < minBufferSize;i++){
      Log.v(i+"",buffer[i]+"");
     }
    } catch (FileNotFoundException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
    
    
   }
  });
Creo un FileInputStream con l'indirizzo del file, e quindi con la riga scritta in verde "leggo" i bytes del file nel buffer.
Quindi schiaccio il bottone, produco il LogCat e mi leggo pezzo per pezzo ogni byte, di cui riporto i primi 100:
03-21 08:22:49.520: V/0(19511): 74
03-21 08:22:49.520: V/1(19511): 1
03-21 08:22:49.520: V/2(19511): 74
03-21 08:22:49.520: V/3(19511): 1
03-21 08:22:49.520: V/4(19511): 50
03-21 08:22:49.520: V/5(19511): 1
03-21 08:22:49.520: V/6(19511): 50
03-21 08:22:49.520: V/7(19511): 1
03-21 08:22:49.520: V/8(19511): 22
03-21 08:22:49.520: V/9(19511): 1
03-21 08:22:49.520: V/10(19511): 22
03-21 08:22:49.520: V/11(19511): 1
03-21 08:22:49.520: V/12(19511): -9
03-21 08:22:49.520: V/13(19511): 0
03-21 08:22:49.520: V/14(19511): -9
03-21 08:22:49.520: V/15(19511): 0
03-21 08:22:49.520: V/16(19511): -42
03-21 08:22:49.520: V/17(19511): 0
03-21 08:22:49.520: V/18(19511): -42
03-21 08:22:49.520: V/19(19511): 0
03-21 08:22:49.520: V/20(19511): -77
03-21 08:22:49.520: V/21(19511): 0
03-21 08:22:49.520: V/22(19511): -77
03-21 08:22:49.520: V/23(19511): 0
03-21 08:22:49.520: V/24(19511): -112
03-21 08:22:49.520: V/25(19511): 0
03-21 08:22:49.520: V/26(19511): -112
03-21 08:22:49.520: V/27(19511): 0
03-21 08:22:49.520: V/28(19511): 110
03-21 08:22:49.520: V/29(19511): 0
03-21 08:22:49.520: V/30(19511): 110
03-21 08:22:49.520: V/31(19511): 0
03-21 08:22:49.520: V/32(19511): 79
03-21 08:22:49.520: V/33(19511): 0
03-21 08:22:49.520: V/34(19511): 79
03-21 08:22:49.520: V/35(19511): 0
03-21 08:22:49.520: V/36(19511): 51
03-21 08:22:49.520: V/37(19511): 0
03-21 08:22:49.520: V/38(19511): 51
03-21 08:22:49.520: V/39(19511): 0
03-21 08:22:49.520: V/40(19511): 27
03-21 08:22:49.520: V/41(19511): 0
03-21 08:22:49.520: V/42(19511): 27
03-21 08:22:49.520: V/43(19511): 0
03-21 08:22:49.520: V/44(19511): 8
03-21 08:22:49.520: V/45(19511): 0
03-21 08:22:49.520: V/46(19511): 8
03-21 08:22:49.520: V/47(19511): 0
03-21 08:22:49.520: V/48(19511): -8
03-21 08:22:49.520: V/49(19511): -1
03-21 08:22:49.520: V/50(19511): -8
03-21 08:22:49.520: V/51(19511): -1
03-21 08:22:49.520: V/52(19511): -22
03-21 08:22:49.520: V/53(19511): -1
03-21 08:22:49.520: V/54(19511): -22
03-21 08:22:49.520: V/55(19511): -1
03-21 08:22:49.520: V/56(19511): -35
03-21 08:22:49.520: V/57(19511): -1
03-21 08:22:49.520: V/58(19511): -35
03-21 08:22:49.520: V/59(19511): -1
03-21 08:22:49.520: V/60(19511): -48
03-21 08:22:49.520: V/61(19511): -1
03-21 08:22:49.520: V/62(19511): -48
03-21 08:22:49.520: V/63(19511): -1
03-21 08:22:49.520: V/64(19511): -62
03-21 08:22:49.520: V/65(19511): -1
03-21 08:22:49.520: V/66(19511): -62
03-21 08:22:49.520: V/67(19511): -1
03-21 08:22:49.520: V/68(19511): -78
03-21 08:22:49.520: V/69(19511): -1
03-21 08:22:49.520: V/70(19511): -78
03-21 08:22:49.520: V/71(19511): -1
03-21 08:22:49.520: V/72(19511): -96
03-21 08:22:49.520: V/73(19511): -1
03-21 08:22:49.520: V/74(19511): -96
03-21 08:22:49.520: V/75(19511): -1
03-21 08:22:49.520: V/76(19511): -114
03-21 08:22:49.520: V/77(19511): -1
03-21 08:22:49.520: V/78(19511): -114
03-21 08:22:49.520: V/79(19511): -1
03-21 08:22:49.520: V/80(19511): 125
03-21 08:22:49.520: V/81(19511): -1
03-21 08:22:49.520: V/82(19511): 125
03-21 08:22:49.520: V/83(19511): -1
03-21 08:22:49.520: V/84(19511): 109
03-21 08:22:49.520: V/85(19511): -1
03-21 08:22:49.520: V/86(19511): 109
03-21 08:22:49.520: V/87(19511): -1
03-21 08:22:49.520: V/88(19511): 96
03-21 08:22:49.520: V/89(19511): -1
03-21 08:22:49.520: V/90(19511): 96
03-21 08:22:49.520: V/91(19511): -1
03-21 08:22:49.520: V/92(19511): 87
03-21 08:22:49.520: V/93(19511): -1
03-21 08:22:49.520: V/94(19511): 87
03-21 08:22:49.520: V/95(19511): -1
03-21 08:22:49.520: V/96(19511): 82
03-21 08:22:49.520: V/97(19511): -1
03-21 08:22:49.520: V/98(19511): 82
03-21 08:22:49.520: V/99(19511): -1
03-21 08:22:49.520: V/100(19511): 81
Sono 278 bytes in totale. Ma il buffer di che dimensione è?
Modifico il codice per ottenere minBufferSize in LogCat:

     FileInputStream fis=new FileInputStream(filePath);
     fis.read(buffer,0,minBufferSize);
     for(int i=0; i < minBufferSize;i++){
      Log.v("BUFFERSIZE",i+"",buffer[i]+"");
     }
.....   
Ottengo 640, che è pure il valore che ottengo come risultato della funzione fis.read(buffer,0,minBufferSize).

Ma adesso voglio trovare modi diversi di inserire i valori nel buffer.
     FileInputStream fis=new FileInputStream(filePath);
     for(int i=0; i < minBufferSize;i++){
      buffer[i]=(byte)fis.read();
     }
     for(int i=0; i < minBufferSize;i++){
      Log.v(i+"",buffer[i]+"");
     }
ed ecco il risultato:
03-21 08:59:47.970: V/0(26357): 74
03-21 08:59:47.970: V/1(26357): 1
03-21 08:59:47.970: V/2(26357): 74
03-21 08:59:47.970: V/3(26357): 1
03-21 08:59:47.970: V/4(26357): 50
03-21 08:59:47.970: V/5(26357): 1
03-21 08:59:47.970: V/6(26357): 50
03-21 08:59:47.970: V/7(26357): 1
03-21 08:59:47.970: V/8(26357): 22
03-21 08:59:47.970: V/9(26357): 1
03-21 08:59:47.970: V/10(26357): 22
03-21 08:59:47.970: V/11(26357): 1
03-21 08:59:47.970: V/12(26357): -9
03-21 08:59:47.970: V/13(26357): 0
03-21 08:59:47.970: V/14(26357): -9
03-21 08:59:47.970: V/15(26357): 0
03-21 08:59:47.970: V/16(26357): -42
03-21 08:59:47.970: V/17(26357): 0
03-21 08:59:47.970: V/18(26357): -42
03-21 08:59:47.970: V/19(26357): 0
03-21 08:59:47.970: V/20(26357): -77
03-21 08:59:47.970: V/21(26357): 0
03-21 08:59:47.970: V/22(26357): -77
03-21 08:59:47.970: V/23(26357): 0
03-21 08:59:47.970: V/24(26357): -112
03-21 08:59:47.970: V/25(26357): 0
03-21 08:59:47.970: V/26(26357): -112
03-21 08:59:47.970: V/27(26357): 0
03-21 08:59:47.970: V/28(26357): 110
03-21 08:59:47.970: V/29(26357): 0
03-21 08:59:47.970: V/30(26357): 110
03-21 08:59:47.970: V/31(26357): 0
03-21 08:59:47.970: V/32(26357): 79
03-21 08:59:47.970: V/33(26357): 0
03-21 08:59:47.970: V/34(26357): 79
03-21 08:59:47.970: V/35(26357): 0
03-21 08:59:47.970: V/36(26357): 51
03-21 08:59:47.970: V/37(26357): 0
03-21 08:59:47.970: V/38(26357): 51
03-21 08:59:47.970: V/39(26357): 0
03-21 08:59:47.970: V/40(26357): 27
03-21 08:59:47.970: V/41(26357): 0
03-21 08:59:47.970: V/42(26357): 27
03-21 08:59:47.970: V/43(26357): 0
03-21 08:59:47.970: V/44(26357): 8
03-21 08:59:47.970: V/45(26357): 0
03-21 08:59:47.970: V/46(26357): 8
03-21 08:59:47.970: V/47(26357): 0
03-21 08:59:47.970: V/48(26357): -8
03-21 08:59:47.970: V/49(26357): -1
03-21 08:59:47.970: V/50(26357): -8
03-21 08:59:47.970: V/51(26357): -1
03-21 08:59:47.970: V/52(26357): -22
03-21 08:59:47.970: V/53(26357): -1
03-21 08:59:47.970: V/54(26357): -22
03-21 08:59:47.970: V/55(26357): -1
03-21 08:59:47.970: V/56(26357): -35
03-21 08:59:47.970: V/57(26357): -1
03-21 08:59:47.970: V/58(26357): -35
03-21 08:59:47.970: V/59(26357): -1
03-21 08:59:47.970: V/60(26357): -48
03-21 08:59:47.970: V/61(26357): -1
03-21 08:59:47.970: V/62(26357): -48
03-21 08:59:47.970: V/63(26357): -1
03-21 08:59:47.970: V/64(26357): -62
03-21 08:59:47.970: V/65(26357): -1
03-21 08:59:47.970: V/66(26357): -62
03-21 08:59:47.970: V/67(26357): -1
03-21 08:59:47.970: V/68(26357): -78
03-21 08:59:47.970: V/69(26357): -1
03-21 08:59:47.970: V/70(26357): -78
03-21 08:59:47.970: V/71(26357): -1
03-21 08:59:47.970: V/72(26357): -96
03-21 08:59:47.970: V/73(26357): -1
03-21 08:59:47.970: V/74(26357): -96
03-21 08:59:47.970: V/75(26357): -1
03-21 08:59:47.970: V/76(26357): -114
03-21 08:59:47.970: V/77(26357): -1
03-21 08:59:47.970: V/78(26357): -114
03-21 08:59:47.970: V/79(26357): -1
03-21 08:59:47.970: V/80(26357): 125
03-21 08:59:47.970: V/81(26357): -1
03-21 08:59:47.970: V/82(26357): 125
03-21 08:59:47.970: V/83(26357): -1
03-21 08:59:47.970: V/84(26357): 109
03-21 08:59:47.970: V/85(26357): -1
03-21 08:59:47.970: V/86(26357): 109
03-21 08:59:47.970: V/87(26357): -1
03-21 08:59:47.970: V/88(26357): 96
03-21 08:59:47.970: V/89(26357): -1
03-21 08:59:47.970: V/90(26357): 96
03-21 08:59:47.970: V/91(26357): -1
03-21 08:59:47.970: V/92(26357): 87
03-21 08:59:47.970: V/93(26357): -1
03-21 08:59:47.970: V/94(26357): 87
03-21 08:59:47.970: V/95(26357): -1
03-21 08:59:47.970: V/96(26357): 82
03-21 08:59:47.970: V/97(26357): -1
03-21 08:59:47.970: V/98(26357): 82
03-21 08:59:47.970: V/99(26357): -1
03-21 08:59:47.970: V/100(26357): 81
Questi valori sono esattamente uguali a quelli ricavati prima.

domenica 20 marzo 2016

Leggere i bytes da un file

Ed ecco ora il FileInputStream per leggere i bytes di un file in un buffer:
public class MainActivity extends Activity {

 Button button;
 Button button2;
 Button button3;
 Button button4;

 String filePath = Environment.getExternalStorageDirectory()
   .getAbsolutePath() + "/ilmiofiledalnomelungolungo";

 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  button = (Button) findViewById(R.id.button1);
  button2 = (Button) findViewById(R.id.button2);
  button3 = (Button) findViewById(R.id.button3);
  button4 = (Button) findViewById(R.id.button4);

  button.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {

    try {
     FileOutputStream fos = new FileOutputStream(filePath);
     byte[] nome = { 0x41, 0x4e, 0x54, 0x4f, 0x4e, 0x45, 0x4c,
       0x4c, 0x4f };
     fos.write(nome);
    } catch (Exception e) {
     e.printStackTrace();
    }

   }
  });
  button2.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    try {
     FileInputStream fis=new FileInputStream(filePath);
     byte[] buffer=new byte[50];
     fis.read(buffer);
     for(int i=0;i<50;i++){
      Log.d(i+"",buffer[i]+"");
     }
    } catch (Exception e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }

   }
  });
 }

}
Ecco il risultato in LogCat:
03-19 17:03:03.740: D/0(15493): 65
03-19 17:03:03.740: D/1(15493): 78
03-19 17:03:03.740: D/2(15493): 84
03-19 17:03:03.740: D/3(15493): 79
03-19 17:03:03.740: D/4(15493): 78
03-19 17:03:03.740: D/5(15493): 69
03-19 17:03:03.740: D/6(15493): 76
03-19 17:03:03.740: D/7(15493): 76
03-19 17:03:03.740: D/8(15493): 79
03-19 17:03:03.740: D/9(15493): 0
03-19 17:03:03.740: D/10(15493): 0
03-19 17:03:03.740: D/11(15493): 0
...che sono i codici ASCII di "ANTONELLO" in notazione decimale.
65/16= 4 con resto di 1: 0x41
78/16= 4 con resto di 14 (E): 0x4e
84/16= 5 con resto di 4; 0x54
79/16= 4 con resto di 15 (F): 0x4f
...eccetera...

E' valso la pena, imparare la tabellina del 16, per fare i calcoli un po' più agevolmente! :D

Scrivere un byte e un array di bytes sul file creato nella memoria del cellulare

Inizio a scrivere sui files creati.
Ora metto l'iniziale del mio nome sul file:
   button.setOnClickListener(new View.OnClickListener() {
  
  @Override
  public void onClick(View v) {
   
   try {
    FileOutputStream fos=new FileOutputStream(filePath);
    fos.write(65);
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }
 });
Aprendo in Hex Editor, leggo 41 che viene tradotto in A come da tabella ASCII.
Perfetto!
Ora voglio scrivere un array di bytes... Scrivo il mio nome usando direttamente i numeri in formato esadecimale perché ricordo meglio i codici ASCII.
   button.setOnClickListener(new View.OnClickListener() {
  
  @Override
  public void onClick(View v) {
   
   try {
    FileOutputStream fos=new FileOutputStream(filePath);
    byte[] nome= {0x41,0x4e,0x54,0x4f,0x4e,0x45,0x4c,0x4c,0x4f};
    fos.write(nome);
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }
 });
Riuscito perfettamente: aggiungo l'estensione .txt al nome del file e lo apro con Blocco Note:
ANTONELLO

Procediamo per gradi: creazione di un file nella memoria del cellulare.

Qui dobbiamo andarci molto per gradi...

Sto affastellando molti concetti e alla fine mi ci perdo.
Con la mia proverbiale capacità di isolare i problemi e affrontarli uno per uno, devo prendere maggiore confidenza con gli STREAM!

Per prima cosa, creiamo un file vuoto nel cellulare, nella cartella storage/sdcard.
public class MainActivity extends Activity{
  
  Button button;
  Button button2;
  Button button3;
  Button button4;

  String filePath=
    Environment.getExternalStorageDirectory().getAbsolutePath()+
    "/ilmiofiledalnomelungolungo";
  
 
  
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   
   button=(Button)findViewById(R.id.button1);
   button2=(Button)findViewById(R.id.button2);
   button3=(Button)findViewById(R.id.button3);
   button4=(Button)findViewById(R.id.button4);
   
   button.setOnClickListener(new View.OnClickListener() {
  
  @Override
  public void onClick(View v) {
   try {
    FileOutputStream fos=new FileOutputStream(filePath);
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }
 });
}
Con la pressione sul primo dei quattro bottoni presenti sull'activity viene creato il file dal nome assurdo che gli ho dato per rendermelo facilmente riconoscibile.



Il file è di lunghezza zero, ovviamente.
Lo apro anche con l'Hex Editor... E' zero!

venerdì 18 marzo 2016

Codice sicuramente funzionante per l'AudioRecord

public class MainActivity extends Activity{
 
 Button button;
 Button button2;
 boolean isRecording;
 
 AudioRecord audioRecord;
 
 int audioSource=AudioSource.MIC;
 int sampleRate=44100;
 int channelConfig=AudioFormat.CHANNEL_IN_STEREO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int bufferSize=AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
 
 
 //AudioTrack audioTrack=new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channelConfig,audioFormat,bufferSize,AudioTrack.MODE_STATIC);

 short[] buffer=new short[(bufferSize/2)];
 
 
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Log.v("Buffer size",bufferSize+"");
  Log.v("Length", buffer.length+"");
  for(int i=0;i<bufferSize/2;i++){
   Log.v(i+"", buffer[i]+"");
  }
  
  
  button=(Button)findViewById(R.id.button1);
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    azione();
    
   }
  });
  
  button2=(Button)findViewById(R.id.button2);
  button2.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    isRecording=false;
    
    audioRecord.stop();
    audioRecord.release();
    audioRecord=null;

    
    Log.v("spento", "SPENTO");
    //ULTIMA MODIFICA--------------------------------
    for(int i=0;i<bufferSize/2;i++){
     Log.v(i+"", (short)buffer[i]+"");
    }
   }
  });
 }
 
 public void azione(){
  audioRecord=new AudioRecord(audioSource,sampleRate,channelConfig,audioFormat,bufferSize/2);
  audioRecord.startRecording();
  isRecording=true;
  Thread thread=new Thread(new Runnable(){

   @Override
   public void run() { 
    while(isRecording){
     int rd=audioRecord.read(buffer, 0, bufferSize/2);
     
    }
     
   }
   
   
  });
  thread.start();
 }
}

Rappresentazione di un'onda sonora?

Usando AudioRecord, ho ottenuto nel buffer questi numeri:
03-18 16:09:00.370: V/0(2345): 35
03-18 16:09:00.370: V/1(2345): 35
03-18 16:09:00.370: V/2(2345): 32
03-18 16:09:00.370: V/3(2345): 32
03-18 16:09:00.370: V/4(2345): 29
03-18 16:09:00.370: V/5(2345): 29
03-18 16:09:00.370: V/6(2345): 26
03-18 16:09:00.370: V/7(2345): 26
03-18 16:09:00.370: V/8(2345): 23
03-18 16:09:00.370: V/9(2345): 23
03-18 16:09:00.370: V/10(2345): 18
03-18 16:09:00.370: V/11(2345): 18
03-18 16:09:00.370: V/12(2345): 14
03-18 16:09:00.370: V/13(2345): 14
03-18 16:09:00.370: V/14(2345): 9
03-18 16:09:00.370: V/15(2345): 9
03-18 16:09:00.370: V/16(2345): 3
03-18 16:09:00.370: V/17(2345): 3
03-18 16:09:00.370: V/18(2345): -3
03-18 16:09:00.370: V/19(2345): -3
03-18 16:09:00.370: V/20(2345): -10
03-18 16:09:00.370: V/21(2345): -10
03-18 16:09:00.370: V/22(2345): -17
03-18 16:09:00.370: V/23(2345): -17
03-18 16:09:00.370: V/24(2345): -24
03-18 16:09:00.370: V/25(2345): -24
03-18 16:09:00.370: V/26(2345): -31
03-18 16:09:00.370: V/27(2345): -31
03-18 16:09:00.370: V/28(2345): -39
03-18 16:09:00.370: V/29(2345): -39
03-18 16:09:00.370: V/30(2345): -46
03-18 16:09:00.370: V/31(2345): -46
03-18 16:09:00.370: V/32(2345): -52
03-18 16:09:00.370: V/33(2345): -52
03-18 16:09:00.370: V/34(2345): -59
03-18 16:09:00.370: V/35(2345): -59
03-18 16:09:00.370: V/36(2345): -64
03-18 16:09:00.370: V/37(2345): -64
03-18 16:09:00.370: V/38(2345): -70
03-18 16:09:00.370: V/39(2345): -70
03-18 16:09:00.370: V/40(2345): -74
03-18 16:09:00.370: V/41(2345): -74
03-18 16:09:00.370: V/42(2345): -79
03-18 16:09:00.370: V/43(2345): -79
03-18 16:09:00.370: V/44(2345): -82
03-18 16:09:00.370: V/45(2345): -82
03-18 16:09:00.370: V/46(2345): -86
03-18 16:09:00.370: V/47(2345): -86
03-18 16:09:00.370: V/48(2345): -90
03-18 16:09:00.370: V/49(2345): -90
03-18 16:09:00.370: V/50(2345): -94
03-18 16:09:00.370: V/51(2345): -94
03-18 16:09:00.370: V/52(2345): -97
03-18 16:09:00.370: V/53(2345): -97
03-18 16:09:00.370: V/54(2345): -101
03-18 16:09:00.370: V/55(2345): -101
03-18 16:09:00.370: V/56(2345): -106
03-18 16:09:00.370: V/57(2345): -106
03-18 16:09:00.370: V/58(2345): -110
03-18 16:09:00.370: V/59(2345): -110
03-18 16:09:00.370: V/60(2345): -115
03-18 16:09:00.370: V/61(2345): -115
03-18 16:09:00.370: V/62(2345): -119
03-18 16:09:00.370: V/63(2345): -119
03-18 16:09:00.370: V/64(2345): -124
03-18 16:09:00.370: V/65(2345): -124
03-18 16:09:00.370: V/66(2345): -128
03-18 16:09:00.370: V/67(2345): -128
03-18 16:09:00.370: V/68(2345): -131
03-18 16:09:00.370: V/69(2345): -131
03-18 16:09:00.370: V/70(2345): -134
03-18 16:09:00.370: V/71(2345): -134
03-18 16:09:00.370: V/72(2345): -137
03-18 16:09:00.370: V/73(2345): -137
03-18 16:09:00.370: V/74(2345): -138
03-18 16:09:00.370: V/75(2345): -138
03-18 16:09:00.370: V/76(2345): -139
03-18 16:09:00.370: V/77(2345): -139
03-18 16:09:00.370: V/78(2345): -139
03-18 16:09:00.370: V/79(2345): -139
03-18 16:09:00.370: V/80(2345): -138
03-18 16:09:00.370: V/81(2345): -138
03-18 16:09:00.370: V/82(2345): -136
03-18 16:09:00.370: V/83(2345): -136
03-18 16:09:00.370: V/84(2345): -134
03-18 16:09:00.370: V/85(2345): -134
03-18 16:09:00.370: V/86(2345): -130
03-18 16:09:00.370: V/87(2345): -130
03-18 16:09:00.370: V/88(2345): -126
03-18 16:09:00.370: V/89(2345): -126
03-18 16:09:00.370: V/90(2345): -120
03-18 16:09:00.370: V/91(2345): -120
03-18 16:09:00.370: V/92(2345): -114
03-18 16:09:00.370: V/93(2345): -114
03-18 16:09:00.370: V/94(2345): -108
03-18 16:09:00.370: V/95(2345): -108
03-18 16:09:00.370: V/96(2345): -100
03-18 16:09:00.370: V/97(2345): -100
03-18 16:09:00.370: V/98(2345): -93
03-18 16:09:00.370: V/99(2345): -93
03-18 16:09:00.370: V/100(2345): -85
03-18 16:09:00.370: V/101(2345): -85
03-18 16:09:00.370: V/102(2345): -77
03-18 16:09:00.370: V/103(2345): -77
03-18 16:09:00.370: V/104(2345): -70
03-18 16:09:00.370: V/105(2345): -70
03-18 16:09:00.370: V/106(2345): -62
03-18 16:09:00.370: V/107(2345): -62
03-18 16:09:00.370: V/108(2345): -55
03-18 16:09:00.370: V/109(2345): -55
03-18 16:09:00.370: V/110(2345): -48
03-18 16:09:00.370: V/111(2345): -48
03-18 16:09:00.370: V/112(2345): -41
03-18 16:09:00.370: V/113(2345): -41
03-18 16:09:00.370: V/114(2345): -35
03-18 16:09:00.370: V/115(2345): -35
03-18 16:09:00.370: V/116(2345): -29
03-18 16:09:00.370: V/117(2345): -29
03-18 16:09:00.370: V/118(2345): -23
03-18 16:09:00.370: V/119(2345): -23
03-18 16:09:00.370: V/120(2345): -17
03-18 16:09:00.370: V/121(2345): -17
03-18 16:09:00.370: V/122(2345): -11
03-18 16:09:00.370: V/123(2345): -11
03-18 16:09:00.370: V/124(2345): -5
03-18 16:09:00.370: V/125(2345): -5
03-18 16:09:00.370: V/126(2345): 1
03-18 16:09:00.370: V/127(2345): 1
03-18 16:09:00.370: V/128(2345): 8
03-18 16:09:00.370: V/129(2345): 8
03-18 16:09:00.370: V/130(2345): 15
03-18 16:09:00.370: V/131(2345): 15
03-18 16:09:00.370: V/132(2345): 22
03-18 16:09:00.370: V/133(2345): 22
03-18 16:09:00.370: V/134(2345): 29
03-18 16:09:00.370: V/135(2345): 29
03-18 16:09:00.370: V/136(2345): 37
03-18 16:09:00.370: V/137(2345): 37
03-18 16:09:00.370: V/138(2345): 45
03-18 16:09:00.370: V/139(2345): 45
03-18 16:09:00.370: V/140(2345): 53
03-18 16:09:00.370: V/141(2345): 53
03-18 16:09:00.370: V/142(2345): 60
03-18 16:09:00.370: V/143(2345): 60
03-18 16:09:00.370: V/144(2345): 68
03-18 16:09:00.370: V/145(2345): 68
03-18 16:09:00.370: V/146(2345): 74
03-18 16:09:00.370: V/147(2345): 74
03-18 16:09:00.370: V/148(2345): 81
03-18 16:09:00.370: V/149(2345): 81
03-18 16:09:00.370: V/150(2345): 87
03-18 16:09:00.370: V/151(2345): 87
03-18 16:09:00.370: V/152(2345): 92
03-18 16:09:00.370: V/153(2345): 92
03-18 16:09:00.370: V/154(2345): 97
03-18 16:09:00.370: V/155(2345): 97
03-18 16:09:00.370: V/156(2345): 101
03-18 16:09:00.370: V/157(2345): 101
03-18 16:09:00.370: V/158(2345): 105
03-18 16:09:00.370: V/159(2345): 105
03-18 16:09:00.370: V/160(2345): 108
03-18 16:09:00.370: V/161(2345): 108
03-18 16:09:00.370: V/162(2345): 110
03-18 16:09:00.370: V/163(2345): 110
03-18 16:09:00.370: V/164(2345): 112
03-18 16:09:00.370: V/165(2345): 112
03-18 16:09:00.370: V/166(2345): 114
03-18 16:09:00.370: V/167(2345): 114
03-18 16:09:00.370: V/168(2345): 116
03-18 16:09:00.370: V/169(2345): 116
03-18 16:09:00.370: V/170(2345): 117
03-18 16:09:00.370: V/171(2345): 117
03-18 16:09:00.370: V/172(2345): 118
03-18 16:09:00.370: V/173(2345): 118
03-18 16:09:00.370: V/174(2345): 119
03-18 16:09:00.370: V/175(2345): 119
03-18 16:09:00.370: V/176(2345): 120
03-18 16:09:00.370: V/177(2345): 120
03-18 16:09:00.370: V/178(2345): 121
03-18 16:09:00.370: V/179(2345): 121
03-18 16:09:00.370: V/180(2345): 122
03-18 16:09:00.370: V/181(2345): 122
03-18 16:09:00.370: V/182(2345): 123
03-18 16:09:00.370: V/183(2345): 123
03-18 16:09:00.370: V/184(2345): 125
03-18 16:09:00.370: V/185(2345): 125
03-18 16:09:00.370: V/186(2345): 127
03-18 16:09:00.370: V/187(2345): 127
03-18 16:09:00.370: V/188(2345): 129
03-18 16:09:00.370: V/189(2345): 129
03-18 16:09:00.370: V/190(2345): 131
03-18 16:09:00.370: V/191(2345): 131
03-18 16:09:00.370: V/192(2345): 133
03-18 16:09:00.370: V/193(2345): 133
03-18 16:09:00.370: V/194(2345): 135
03-18 16:09:00.370: V/195(2345): 135
03-18 16:09:00.370: V/196(2345): 136
03-18 16:09:00.370: V/197(2345): 136
03-18 16:09:00.370: V/198(2345): 137
03-18 16:09:00.370: V/199(2345): 137
03-18 16:09:00.370: V/200(2345): 138
03-18 16:09:00.370: V/201(2345): 138
03-18 16:09:00.370: V/202(2345): 137
03-18 16:09:00.370: V/203(2345): 137
03-18 16:09:00.370: V/204(2345): 136
03-18 16:09:00.370: V/205(2345): 136
03-18 16:09:00.370: V/206(2345): 133
03-18 16:09:00.370: V/207(2345): 133
03-18 16:09:00.370: V/208(2345): 129
03-18 16:09:00.370: V/209(2345): 129
03-18 16:09:00.370: V/210(2345): 125
03-18 16:09:00.370: V/211(2345): 125
03-18 16:09:00.370: V/212(2345): 119
03-18 16:09:00.370: V/213(2345): 119
03-18 16:09:00.370: V/214(2345): 113
03-18 16:09:00.370: V/215(2345): 113
03-18 16:09:00.370: V/216(2345): 106
03-18 16:09:00.370: V/217(2345): 106
03-18 16:09:00.370: V/218(2345): 98
03-18 16:09:00.370: V/219(2345): 98
03-18 16:09:00.370: V/220(2345): 90
03-18 16:09:00.370: V/221(2345): 90
03-18 16:09:00.370: V/222(2345): 82
03-18 16:09:00.370: V/223(2345): 82
03-18 16:09:00.370: V/224(2345): 74
03-18 16:09:00.370: V/225(2345): 74
03-18 16:09:00.370: V/226(2345): 65
03-18 16:09:00.370: V/227(2345): 65
03-18 16:09:00.370: V/228(2345): 56
03-18 16:09:00.370: V/229(2345): 56
03-18 16:09:00.370: V/230(2345): 48
03-18 16:09:00.370: V/231(2345): 48
03-18 16:09:00.370: V/232(2345): 39
03-18 16:09:00.370: V/233(2345): 39
03-18 16:09:00.370: V/234(2345): 30
03-18 16:09:00.370: V/235(2345): 30
03-18 16:09:00.370: V/236(2345): 21
03-18 16:09:00.370: V/237(2345): 21
03-18 16:09:00.370: V/238(2345): 12
03-18 16:09:00.370: V/239(2345): 12
03-18 16:09:00.370: V/240(2345): 2
03-18 16:09:00.370: V/241(2345): 2
03-18 16:09:00.370: V/242(2345): -7
03-18 16:09:00.370: V/243(2345): -7
03-18 16:09:00.370: V/244(2345): -16
03-18 16:09:00.370: V/245(2345): -16
03-18 16:09:00.370: V/246(2345): -25
03-18 16:09:00.370: V/247(2345): -25
03-18 16:09:00.370: V/248(2345): -35
03-18 16:09:00.370: V/249(2345): -35
03-18 16:09:00.370: V/250(2345): -44
03-18 16:09:00.370: V/251(2345): -44
03-18 16:09:00.370: V/252(2345): -54
03-18 16:09:00.370: V/253(2345): -54
03-18 16:09:00.370: V/254(2345): -64
03-18 16:09:00.370: V/255(2345): -64
03-18 16:09:00.370: V/256(2345): -74
03-18 16:09:00.370: V/257(2345): -74
03-18 16:09:00.370: V/258(2345): -84
03-18 16:09:00.370: V/259(2345): -84
03-18 16:09:00.370: V/260(2345): -94
03-18 16:09:00.370: V/261(2345): -94
03-18 16:09:00.370: V/262(2345): -104
03-18 16:09:00.370: V/263(2345): -104
03-18 16:09:00.370: V/264(2345): -114
03-18 16:09:00.370: V/265(2345): -114
03-18 16:09:00.370: V/266(2345): -123
03-18 16:09:00.370: V/267(2345): -123
03-18 16:09:00.370: V/268(2345): -132
03-18 16:09:00.370: V/269(2345): -132
03-18 16:09:00.370: V/270(2345): -141
03-18 16:09:00.370: V/271(2345): -141
03-18 16:09:00.370: V/272(2345): -149
03-18 16:09:00.370: V/273(2345): -149
03-18 16:09:00.370: V/274(2345): -156
03-18 16:09:00.370: V/275(2345): -156
03-18 16:09:00.370: V/276(2345): -162
03-18 16:09:00.370: V/277(2345): -162
Un andamento sinusoidale? Sto VEDENDO LA RAPPRESENTAZIONE DI UN'ONDA SONORA????? Non ne sono sicuro, ma sarebbe pazzescamente FANTASTICO!!!

giovedì 17 marzo 2016

Riproduzione di un file wav con Android mediante AudioTrack

Ho riprodotto un file WAV con AudioTrack, ottenendo anche qualche informazione in più sul sample rate.
public class MainActivity extends Activity{
 
 AudioTrack audioTrack;
 Button bottone;
 int minBufferSize=AudioTrack.getMinBufferSize(10000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_8BIT);
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  bottone=(Button)findViewById(R.id.button1);
  audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 10000, AudioFormat.CHANNEL_IN_STEREO, 
                AudioFormat.ENCODING_PCM_8BIT, minBufferSize, AudioTrack.MODE_STREAM); 
 
  bottone.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    playSound();
    
   }
  }); 
 }
 
 public void playSound(){
  
  audioTrack.play(); 
  int i=0;
  int bufferSize=512;
  final byte[] buffer=new byte[bufferSize];
  
  final InputStream inputStream=getResources().openRawResource(R.raw.vaffanculo);
  
  
       try {
        
           while((i = inputStream.read(buffer)) > -1)
               audioTrack.write(buffer, 0, i);
       } catch (IOException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       try {
           inputStream.close();
       } catch (IOException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
    
   
   
  }
  

 
  
 
}

lunedì 14 marzo 2016

audioRecorder, primi rudimenti.

Mi preparo tutte le impostazioni relative al tipo di segnale digitale, e dichiaro la variabile per l'audioRecorder e per il Thread (è ovvio che la registrazione va su un thread separato altrimenti si impappa tutto),
Poi ho due metodi: startRecording, che istanzia un audioRecorder e lo avvia sul thread suo leggendo i dati che arrivano dal microfono (o altra fonte) nel buffer; e stopRecording che stoppa il loop della registrazione, ferma l'audioRecorder e me lo distrugge.

Ecco le dichiarazioni:
 private Thread recordingThread=null;
 boolean isRecording;
 Button button1;
 
 int audioSource=AudioSource.MIC;
 int sampleRate=8000;
 int channelConfig=AudioFormat.CHANNEL_IN_MONO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
 AudioRecord audioRecord;
 byte[] buffer=new byte[bufferSizeInBytes];
E il codice che attribuisce il listener al bottone che avvia e stoppa la registrazione:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  button1=(Button)findViewById(R.id.button1);
  button1.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if(isRecording){
     ((Button)v).setText("Stop recording");
     stopRecording();
    }
    else{
     ((Button)v).setText("Start recording");
     startRecording();
    }
    
    
   }
  });


Poi il codice che crea e quello che distrugge l'audioRecorder:
La prima funzione è startRecording:
 public void startRecording(){
  isRecording=true;
  audioRecord=new AudioRecord(audioSource,sampleRate,channelConfig,audioFormat,bufferSizeInBytes);
  recordingThread=new Thread(new Runnable(){

   @Override
   public void run() {
    while(isRecording){
     audioRecord.read(buffer,0,bufferSizeInBytes);
    }
    
   }
   
  });
  
 }


E l'altra è stopRecording:
 public void stopRecording(){
  isRecording=false;
  audioRecord.stop();
  audioRecord.release();
  audioRecord=null; 
 }
E pare che funzioni.
Ovviamente non lo posso testare perché mi manca ancora uno strumento con funzioni di player...
Comunque perlomeno non si blocca, ed è già qualcosa.

domenica 13 marzo 2016

AudioRecorder: primi passi.

Apro Eclipse.
Devo creare un audioRecord.
Per farlo, riprendo il codice che avevo messo sul blog...

Per creare un audioRecord dovevo definire alcuni parametri:
  1. Source (microfono);
  2. Sample Rate;
  3. Channel configuration (sarebbe STEREO o MONO);
  4. AudioFormat (sarebbe la quantità di bit di cui è fatto il segnale)
  5. la lunghezza del buffer, che si ricava dai tre parametri precedenti.
Ci provo: inizio scrivendo solo i nomi delle variabili, di tipo int:
public class MainActivity extends Activity{

 int audioSource;
 int sampleRate;
 int channelConfig;
 int audioFormat;
 int bufferSizeInBytes;
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  
 }
  
}
Adesso calcolo queste variabili con le rispettive funzioni.
 int audioSource=AudioSource.MIC;
 int sampleRate=8000;
 int channelConfig=AudioFormat.CHANNEL_IN_MONO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
Bene.
Adesso cancello tutto e ci vado a memoria, senza sbirciare...

 int audioSource=AudioSource.MIC;
 int sampleRate=8000;
 int channelConfig=AudioFormat.CHANNEL_IN_MONO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRate,channelConfig,audioFormat);
Perfetto!!!
Ora dovrei creare l'audioRecorder...

La sintassi sembra la solita, con tutti questi valori come parametri.
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  AudioRecord audioRecord = new AudioRecord(audioSource,sampleRate, channelConfig, audioFormat, bufferSizeInBytes);
Adesso cosa fare?

ho visto che bisognava anche definire un buffer, di tipo short.
La sintassi che ho trovato su un sito era questa:
short[] buffer = new short[bufferSizeInBytes];
Su un altro sito trovo questa sintassi:
buffer = new short[buffersizebytes]; 
buflen=buffersizebytes/2; 
Quel buflen sarebbe la lunghezza in short, essendo il tipo short di 16 bytes...
Quindi una volta creato il recorder bisogna vedere come scrive sul buffer.
Confronto le sintassi:
audioRecord.startRecording(); 
mSamplesRead = audioRecord.read(buffer, 0, buffersizebytes); 
audioRecord.stop(); 
Ecco, la funzione sarebbe read.

Perché sia racchiusa fra startRecording e stop, lo ignoro...
Vediamo...

Niente, la sintassi è questa.
Provo a metterla in atto.
public class MainActivity extends Activity{

 int audioSource=AudioSource.MIC;
 int sampleRate=8000;
 int channelConfig=AudioFormat.CHANNEL_IN_MONO;
 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
 int bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRate,channelConfig,audioFormat);
 
 short[] buffer=new short[bufferSizeInBytes];
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  AudioRecord audioRecord = new AudioRecord(audioSource,sampleRate, channelConfig, audioFormat, bufferSizeInBytes);
  audioRecord.startRecording();
  audioRecord.read(buffer, 0, bufferSizeInBytes);
    
    
 }
}
E fin qua dovremmo esserci... Non so che farci, con questo buffer, ammesso che si riempia, ma comunque dovremmo esserci.
Mi aspetto che se avvio il codice succederà una catastrofe...

Ripasso di elementi di audio digitale

Quanti parametri ha AudioRecord?
Il primo parametro è audiosource: cosa è?
Era come quella che avevo visto per MediaRecorder: la sorgente audio.
Potrei scegliere "Microfono"...
AudioRecord audioRecord=new AudioRecord(MediaRecorder.AudioSource.MIC,)

Il secondo parametro è sampleRateInHz: il nome spiega che sarebbe la frequenza di campionamento.
Il valore più usato sarebbe 44100.

Il terzo è channelConfig che sarebbe la differenza fra MONO, STEREO.

Il quarto è audioFormat che può essere 8 bit, 16 bit o float (quest'ultimo non l'ho mai sentito)

Il quinto e ultimo parametro è bufferSizeInBytes, che a quanto ho capito può essere determinato nel suo valore ottimale mediante un'altra funzione, che sarebbe getMinBufferSize la quale troverebbe il valore minimo del buffer necessario per lavorare con certi parametri.



E non vogliamo andare a vedercela subito, questa funzione getMinBufferSize?
Ecco qui un pezzo di codice preso da StackOverflow dove viene definita la lunghezza del buffer per mezzo di questa funzione:
int audioSource = AudioSource.MIC;
int sampleRateInHz = 8000;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
Questa funzione usa Sample Rate, Channel Config e Audio Format per derivare la minima dimensione necessaria per il buffer.
Cioè, per intendermi, usa cose piuttosto semplici da capire:
  • La frequenza di campionamento
  • il fatto che si voglia registrare un suono STEREO o MONO
  • il formato a 8 o 16 bit

E ora mi sento un po' più tranquillo sulla cosa...

sabato 12 marzo 2016

Codice MediaRecorder e MediaPlayer

Salvo qui il codice completo di MediaRecorder e MediaPlayer:
public class MainActivity extends Activity{

 private MediaPlayer mPlayer;
 private MediaRecorder mRecorder;
 private Button bttRecord; 
 private Button bttStopRecord;
 private Button bttPlay;
 private Button bttStopPlay;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  bttRecord=(Button)findViewById(R.id.button1);
  bttStopRecord=(Button)findViewById(R.id.button2);
  bttPlay=(Button)findViewById(R.id.button3);
  bttStopPlay=(Button)findViewById(R.id.button4);
  
  bttRecord.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    mRecorder =new MediaRecorder();
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/mioFile.3gp");
    try{
     mRecorder.prepare();
    }catch(Exception e){
     Log.d("errore", "Prepare failed!");
    }
    mRecorder.start();
   }
  });
  bttStopRecord.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mRecorder.stop();
    mRecorder.release();
    mRecorder=null;
    
   }
  });
  
  bttPlay.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mPlayer=new MediaPlayer();
    try{
     mPlayer.setDataSource(Environment.getExternalStorageDirectory().getAbsolutePath()+"/mioFile.3gp");
     mPlayer.prepare();
     mPlayer.start();
    }catch(Exception e)
    {
     Log.d("Exception", "Play failed");
    }
    
   }
  });
  
  bttStopPlay.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mPlayer.release();
    mPlayer=null;
    
   }
  });
  
 
 }

 
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.laboratorio.MainActivity" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="38dp"
        android:text="Record" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button1"
        android:layout_alignBottom="@+id/button1"
        android:layout_marginLeft="46dp"
        android:layout_toRightOf="@+id/button1"
        android:text="Stop record" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button1"
        android:layout_below="@+id/button1"
        android:layout_marginTop="55dp"
        android:text="Play" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button3"
        android:layout_alignBottom="@+id/button3"
        android:layout_alignLeft="@+id/button2"
        android:text="Stop Play" />

</RelativeLayout> 
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.laboratorio"
    android:versionCode="1"
    android:versionName="1.0" >
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-sdk
        android:minSdkVersion="18"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest> 
Per eventuale ripasso futuro.

Registrazione di suoni con Android

Recorder.
Quale è l'oggetto che dobbiamo istanziare per registrare la voce?

Ecco, dichiaro una variabile di tipo MediaRecorder e una di tipo MediaPlayer, con le relative "importazioni" richieste da Eclipse.

public class MainActivity extends Activity{

 private MediaPlayer mPlayer;
 private MediaRecorder mRecorder;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  
 
 }
Quindi devo istanziare le variabili.
Come si fa?

Ecco, predispongo un Button che istanzia l'oggetto:
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  button=(Button)findViewById(R.id.button1);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mRecorder =new MediaRecorder();
    
   }
  });
Ma adesso bisogna settare alcune cose di questo neonato MediaRecorder...

Lo faccio pedissequamente, senza pensarci troppo: capirò in seguito.
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  button=(Button)findViewById(R.id.button1);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mRecorder =new MediaRecorder();
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"mioFile.3gp");
    try{
     mRecorder.prepare();
     catch(Exception e){
      Log.d("prepare","failed");
     }
    }
   }
  });
E quindi inizia:
public class MainActivity extends Activity{

 private MediaPlayer mPlayer;
 private MediaRecorder mRecorder;
 private Button button;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  button=(Button)findViewById(R.id.button1);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    mRecorder =new MediaRecorder();
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"mioFile.3gp");
    try{
     mRecorder.prepare();
    }catch(Exception e){
     Log.d("errore", "Prepare failed!");
    }
    mRecorder.start();
   }
  });
 
 }

 
}
Ho avuto un po' di problemi.
Per prima cosa, leggo che nel Manifest vanno impostati dei permessi, di cui non mi ero accorto prima (anche se ci avevo pensato per poi scordarmene...):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.laboratorio"
    android:versionCode="1"
    android:versionName="1.0" >
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-sdk
        android:minSdkVersion="18"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest> 
...quindi sembra che abbia messo in sequenza sbagliata alcuni settaggi (la cui natura mi è ancora ignota): inizialmente, con questa sequenza...
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"mioFile.3gp");
...ho avuto un messaggio di errore:
03-12 14:56:52.930: E/MediaRecorder(16135): try to set the audio encoder without setting the audio source first
Allora ho cambiato:
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"mioFile.3gp");
e ho ottenuto:
03-12 15:13:32.585: E/MediaRecorder(18394): setAudioEncoder called in an invalid state(2)
Cambio ancora:
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"mioFile.3gp");
Ed è sempre lui:
03-12 15:17:28.156: E/MediaRecorder(18958): start called in an invalid state: 4

Ma ecco che sull'impagabile StackOverflow leggo "check if you have output file path set and if it really exists.", e penso che forse il path del file di destinazione non è scritto bene in quanto ci manga uno slash!
Ci metto lo slash:
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/mioFile.3gp");
...E finalmente funziona!!!

martedì 8 marzo 2016

Codice per risolvere il conflitto di nome per un nuovo foglio di Excel da rinominare.

Il problema è dover inserire un nuovo foglio di Excel e rinominarlo, con un nome che potrebbe entrare in conflitto con un eventuale foglio preesistente con lo stesso nome.
Ho seguito una logica un po' contorta, forse...

Il foglio, una volta creato, deve subire l'azione di due subroutines, chiamate EmptyRowDelete e DividiInterviste.
Se invece il foglio viene mantenuto, deve subire solo l'azione di DividiInterviste.
Ho seguito la logica di andare a ErrHandler: se c'è conflitto, di andare a fine: se non c'è conflitto.
In ErrHandler, se viene creato un file ex novo, si va ugualmente a fine: perché il destino è comune a quello che ci sarebbe in mancanza di errore, mentre se viene mantenuto il file originario si va a continua: per subire solo l'ultima delle azioni subite dal file negli altri due casi.
Sub CopiaFoglio()
    Dim newsheet As Worksheet
    Set newsheet = Sheets.Add
    
    On Error GoTo ErrHandler
    newsheet.Name = "Foglio elaborazione"
   
    GoTo fine



ErrHandler:
    risposta = MsgBox("Creare un nuovo Foglio Elaborazione?", vbYesNo)

        If risposta = vbNo Then
        Application.DisplayAlerts = False
        newsheet.Delete
        Application.DisplayAlerts = True
    GoTo continua

    Else
        Application.DisplayAlerts = False
        Sheets("Foglio elaborazione").Delete
        Application.DisplayAlerts = True
        newsheet.Name = "Foglio elaborazione"
    End If
fine:
    Sheets("Foglio Elaborazione").Move after:=Sheets("PANNELLO DI CONTROLLO")
    Sheets("riserva").Cells.Copy Destination:=Sheets("Foglio elaborazione").Cells
    EmptyRowDelete
continua:
     Sheets("Foglio elaborazione").Activate
     DividiInterviste
     
End Sub
Ho cercato di rendere con i colori le due vie e la via comune finale.

lunedì 7 marzo 2016

Creazione di un nuovo foglio con un dato nome in Excel.

Da un elenco di fogli "intoccabile" si copia il tutto su un nuovo foglio rinominato ad hoc.
Bisogna anche gestire l'ipotesi in cui il nuovo foglio già esista.


Sub Macro3()


    Set NewSheet = Sheets.Add
    On Error GoTo ErrHandler
    NewSheet.Name = "Foglio elaborazione"
    Sheets("riserva").Cells.Copy Destination:=NewSheet.Cells

    Exit Sub
ErrHandler:
    NewSheet.Delete
    
End Sub
Con questo codice ci siamo quasi.
Viene aggiunto un nuovo foglio, e viene rinominato, ma c'è una gestione dell'errore nel caso in cui un foglio con questo nome già esista: il foglio viene eliminato.
L'unico neo è che all'atto dell'eliminazione del foglio appare un avviso, come di default ogni qualvolta si elimini un foglio.
La cosa è disattivabile: devo solo ricordare come.

Ecco:
Sub Macro3()


    Set NewSheet = Sheets.Add
    On Error GoTo ErrHandler
    NewSheet.Name = "Foglio elaborazione"
    Sheets("riserva").Cells.Copy Destination:=NewSheet.Cells

    Exit Sub
ErrHandler:
    Application.DisplayAlerts = False
    NewSheet.Delete
    Application.DisplayAlerts = True
End Sub
In questo modo, il tentativo di rinominare un nuovo foglio aggiunto abortisce alla base, in quanto il nuovo foglio aggiunto viene prontamente eliminato, come se non fosse mai stato aggiunto.
Peraltro, provando manualmente a eliminare un foglio della cartella, gli avvisi appaiono come di norma perché l'impostazione Application.DisplayAlerts è settata a false solo per il momento necessario ad eliminare senza "traumi" il foglio appena aggiunto.
Però la cosa sarebbe più completa ed elegante se l'utente potesse avere la possibilità di scegliere se mantenere il vecchio foglio "Foglio Elaborazione", oppure crearne uno nuovo che sia la copia dell'originale, nel caso in cui il foglio sia stato per qualche motivo modificato.
Ci provo...

Ed ecco il risultato finale: si può scegliere se si vuole ricreare il foglio o mantenere quello preesistente.
Sub Macro3()

    
    Set NewSheet = Sheets.Add
    On Error GoTo ErrHandler
    NewSheet.Name = "Foglio elaborazione"
   
    GoTo fine

ErrHandler:
    risposta = MsgBox("Creare un nuovo Foglio Elaborazione?", vbYesNo)
    If risposta = vbYes Then
        Application.DisplayAlerts = False
        Sheets("Foglio elaborazione").Delete
        Application.DisplayAlerts = True
        NewSheet.Name = "Foglio elaborazione"
    Else
        Application.DisplayAlerts = False
        NewSheet.Delete
        Application.DisplayAlerts = True
    End If
    
fine:
     Sheets("riserva").Cells.Copy Destination:=Sheets("Foglio elaborazione").Cells
End Sub

domenica 6 marzo 2016

Android: ripasso su layout mobili nell'ambito di una ScrollView

Devo rimettere a posto i concetti relativi agli elementi spostabili nell'ambito di una ScrollView.

Ho un RelativeLayout posto all'interno di una ScrollView:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" 
    android:fillViewport="true"
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:id="@+id/mainLayout"
    android:background="#aef"
    tools:context="com.example.comunicatore.MainActivity" >


</RelativeLayout>
</ScrollView> 
Adesso voglio esercitarmi a riscrivere il codice secondo il quale vengono creati dei layout ognuno contenente un'immagine presa nelle risorse, spostabili nell'ambito della ScrollView.
Ossia la schermata può scorrere e nell'ambito della schermata si può eseguire il drag-drop di questi layout.

Cancello il codice che ho scritto e riscrivo.

Parto da questo:
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);


	}

	
}
Ho già un id attribuito al RelativeLayout e alla ScrollView che lo contiene, nel file xml.

Vado:
inizio con la dichiarazione della ScrollView e del RelativeLayout
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
(mi creo delle sezioni distinte nell'ambito del codice per poterlo rileggere più agevolmente quando diventerà complicato).

Ora inizializzo scrollView e mainLayout:
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
//INIZIALIZZAZIONI----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
		scrollView=(ScrollView)findViewById(R.id.scrollView);
		mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
//TERMINE INIZIALIZZAZIONI----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
E adesso procedo alla creazione a runtime di un layout:
per prima cosa lo dichiaro nella sezione DICHIARAZIONI (preferisco che il suo scope sia diffuso a tutte le parti del codice):
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Dichiaro la ImageView che figurerà all'interno del layout:
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Dopo aver creato il layout, creo una ImageView che vi andrà messa dentro:
//CODICE DI ONCREATE----CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
//TERMINE CODICE DI ONCREATE----CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
E ora devo prendere l'immagine dalle risorse e metterla nella ImageView.
Ricorro al codice che riduce le dimensioni delle immagini:
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
E quindi pongo la ImageView entro il Layout.
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
		layout.addView(imageView)


Ora imposto le proprietà del layout e lo aggiungo a mainLayout.
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
		layout.addView(imageView);
		RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
		mainLayout.addView(layout);


Ora lancio il tutto, scritto di getto, per verificare la mia abilità...



Perfetto! Funziona alla perfezione!

Giocherellando sulle dimensioni dell'immagine verifico che la scrollView funzioni anch'essa, e risulta funzionante.

Ora metto il codice per rendere spostabile il layout con l'immagine.
Creo una sezione //LISTENERS. Dichiaro un OnTouchListener nella sezione DICHIARAZIONI...
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
e vado a inizializzarlo nella sezione LISTENERS:
//LISTENERS----LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		
		return false;
	}
};
		
//TERMINE LISTENERS----LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
Quindi mi assicuro che all'evento Touch, sempre, venga rilevata la posizione del mouse (o meglio del dito)
Dichiaro le coordinate del mouse/dito nella sezione DICHIARAZIONI:
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
	int X,Y;
E rilevo nell'evento OnTouch le coordinate:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		return false;
	}
};

Ora bisogna calcolare la differenza fra la posizione del mouse/dito e quella del layout nel momento in cui va a toccare (evento ACTION_DOWN).
Dichiaro due altre variabili: DeltaX e DeltaY, in cui metterò le differenze delle coordinate dito-layout.
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
	int X,Y;
	int deltaX, deltaY;
e vado a dar loro il valore.
Per fare questo, nel codice del listener, devo desumere le coordinate del layout stesso, mediante i LayoutParameters:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
                        break;
		}
		return false;
	}
};
Ottenuta la differenza, a ogni movimento del mouse/dito devo porre le coordinate del layout a valori pari alle coordinate del dito meno le coordinate del layout.
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return false;
	}
};
(l'evento OnTouch si ripete sempre anche durante il movimento, per cui X e Y vengono continuamente rilevate).

Ora devo attribuire il Listener al layout, però! Ho lanciato l'applicazione dimenticando di farlo, e ovviamente non funzionava...

layout=new LinearLayout(this); imageView=new ImageView(this); BitmapFactory.Options opzioni=new BitmapFactory.Options(); opzioni.inJustDecodeBounds=true; BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni); int fattore=1; while(opzioni.outWidth/fattore>100 || opzioni.outHeight/fattore>100){ fattore*=2; } opzioni.inJustDecodeBounds=false; opzioni.inSampleSize=fattore; Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni); imageView.setImageBitmap(bmp); layout.addView(imageView); RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); layout.setOnTouchListener(layoutTouchListener); mainLayout.addView(layout); Non funziona ancora!
Provo a cambiare il false dell'evento OnTouch a true:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
E parte!!!
Probabilmente con il false l'evento "bubblava" al mainLayout.
Però insorge un nuovo problema! Finché non viene "sfottuta" la scrollView, il layout con l'immagine si sposta agevolmente, ma quando lo porto in giù oltrepassando il limite della "schermata", allora entra in azione la scrollView, che funziona, ma una volta azionata non riesco più a muovere il layout, che nel trascinamento col mouse/dito ha strani comportamenti.
Probabilmente ciò è dovuto al fatto che la scrollView viene manipolata quando tocco il layout. Così devo mettere in azione un blocco della scrollView nel momento in cui sto "draggando" il layout.
Ci provo con un magico metodo di nome "request..." e non ricordo più come, da porre nel codice dell'evento OnTouch.
Lo ritrovo con l'Intellisense...
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		scrollView.requestDisallowInterceptTouchEvent(false);
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
Eccolo!
Rilancio l'applicazione e vediamo ora come va...

Va male, esattamente come prima, perché quel metodo dovevo impostarlo a true!
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		scrollView.requestDisallowInterceptTouchEvent(true);
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
Ora è praticamente perfetto!!!

Una griglia di numeri in JavaScript

Vorrei creare una tastiera mediante la quale sia possibile prelevare cifre per poi deporle in uno spazio a creare numeri.

(nota: provato solo per Firefox)

Proverei con il JavaScript...

Comincio con il creare due DIV ognuno dei quali porta un numero da 1 a 3.
Cliccando sul DIV dovrebbe apparire al puntatore del mouse un "cartellino" contenente il numero rappresentato dal DIV.

Come si può creare un DIV a runtime? Rinfreschiamoci le idee...

<style>
.numero{
 font-size:60px;
 background-color:#AACCFF;
 width:100px;
 height:100px;
}

</style>


<script>
function creaDiv(){
 var mioDiv=document.createElement('div');
 mioDiv.className="numero";
 mioDiv.innerHTML=1;
 document.getElementsByTagName("body")[0].appendChild(mioDiv); 
}
</script>

<body onLoad="creaDiv()">


</body> 
Adesso vediamo di creare dei DIV affiancati, in file di tre.
Ricordo un classico schema per creare delle griglie di elementi...


Ecco, l'ho messo in pratica, ho rinunciato ad andare per passettini creando solo 1, 2 e 3, e ho fatto di getto tutto, ed ecco la funzione grid(left, top) che mi crea una griglia di elementi che ha la posizione specificata dai parametri:
<style>
.numero{
 font-size:100px;
 background-color:#AAEEDD;
 width:100px;
 height:100px;
 border: 2px solid green;
 line-height:100%;
 text-align:center;
 vertical-align:text-middle;
}

</style>


<script>
function main(){
 grid(300,50);
}
function grid(left,top){
 var x,y;
 for(n=0;n<9;n++){
  x=n%3;
  y=Math.floor(n/3);
  creaDiv(x,y,left,top,n);
 }
  
}
function creaDiv(x,y,left,top,n){
 var mioDiv=document.createElement('div');
 mioDiv.className="numero";
 mioDiv.innerHTML=n+1;
 mioDiv.style.position="absolute";
 mioDiv.style.left=left+x*parseInt(getComputedStyle(mioDiv).getPropertyValue("width"));
 mioDiv.style.top=top+y*parseInt(getComputedStyle(mioDiv).getPropertyValue("height"));
 document.getElementsByTagName("body")[0].appendChild(mioDiv); 
}
</script>

<body onLoad="main()">


</body>
Ecco la griglia:



Mi piace, ogni tanto, ripassare e approfondire il JavaScript!