Autore Topic: Generazione onda sinusoidale con ampiezza variabile  (Letto 1323 volte)

Offline InterDroid

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Alcatel onetouch 993d
  • Sistema operativo:
    Windows 8.1
Generazione onda sinusoidale con ampiezza variabile
« il: 08 Febbraio 2014, 12:11:13 CET »
0
Ciao a tutti!

ho utilizzato la classe Audiotrack per produrre in uscita dal mio smartphone un'onda sinusoidale ad una determinata frequenza. Il mio problema consiste ora nel voler farle aumentare l'ampiezza da un livello 0 ad uno massimo (in modo lineare/logaritmico non ha importanza), nel giro di 4/5 secondi e poi rimanere a volume costante.
Qualcuno ha idea di come potrei fare?

Codice (Java): [Seleziona]
wave = new Thread( new Runnable( )
    {
       public void run( )
       {                       
          final float frequency = 440;
          float increment = (float)(2*Math.PI) * frequency / 44100;
          float angle = 0;
          int amp= 1;
          DispAudio disp = new DispAudio( );// all'interno istanzio la classe audiotrack e gestisco i campioni
          float campioni[] = new float[1024];
       
       
         
          while( true )
          {
             for( int i = 0; i < campioni.length; i++ )
             {           
           
                campioni[i] = (float) (amp * Math.sin( angle ));
                 angle += increment;
               
                }
             
             
           
            disp.ScriviCampioni( campioni );
          }            
       }
    }) ;
 
        wave.start();
grazie a tutti in anticipo.

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #1 il: 08 Febbraio 2014, 12:20:27 CET »
0
Definisci un fattore di scala che vada da 0 al TUOMAX lungo tutta la durata dei campioni. La questione dei 4/5 secondi dipende da quanti campioni hai. Se vuoi che vada a regime prima devi giocare con il fattore di divisione e saturare il valore della rampa al massimo.

Comunque questo dovrebbe darti un'idea di come agire:

Codice (Java): [Seleziona]
float k;
float TUOMAX = 255.0

for( int i = 0; i < campioni.length; i++ )   {          
   k = TUOMAX * i / campioni.length ;
   campioni[i] = (float) (k * amp * Math.sin( angle ));
   angle += increment;
}
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline InterDroid

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Alcatel onetouch 993d
  • Sistema operativo:
    Windows 8.1
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #2 il: 08 Febbraio 2014, 12:38:50 CET »
0
il problema è che in questo modo l'ampiezza aumenta , ma lo fa in modo ciclico (il buffer viene riempito ogni volta), mentre a me servirebbe che lo facesse solo all'inizio per poi rimanere sempre ad ampiezza costante. Avevo pensato di utilizzare le funzioni del timer o calendar di android, ma non ho trovato niente di adatto.

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #3 il: 10 Febbraio 2014, 15:02:26 CET »
0
Scusa, l'ampiezza di una onda è il volume a cui la senti.

Perché non metti un metti un Handler ed ogni 100ms modifichi il volume con setStereoVolume?

Ciao.

Offline InterDroid

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Alcatel onetouch 993d
  • Sistema operativo:
    Windows 8.1
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #4 il: 13 Febbraio 2014, 17:16:28 CET »
0
il fatto di utilizzare setStereoVolume l'avevo già preso in considerazione, ma avevo stranamente un raddoppio della frequenza (problema tuttavia superabile).
Sono ai primi passi con la programmazione Android, come posso implementare un handler?
Alternativamente qualcuno è a conoscenza di un metodo per far si che un thread venga eseguito solo una volta e duri solo per un certo periodo ?
grazie!

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #5 il: 13 Febbraio 2014, 17:43:51 CET »
0
Un azione periodica la puoi fare così:

Codice: [Seleziona]
    private Handler mUpdateHandler = new Handler() {
        @Override
        public void handleMessage( Message msg )
        {
            // Do Job
            // Qui chiami setStereoVolume (o qualunque altro meccanismo per cambiare l'ampiezza dell'onda)

            // and program next update
            mUpdateHandler.removeMessages( 0 );
            mUpdateHandler.sendEmptyMessageDelayed( 0, cStatsPeriodMs );
        }
    };

e lo fai partire con

Codice: [Seleziona]
            mUpdateHandler.sendEmptyMessageDelayed( 0, cStatsPeriodMs );


Alternativamente puoi lanciare un AsyncTask, ma gira in un thread a parte.
Non so se ci sia un altro modo di programmare eventi ciclici.


Offline InterDroid

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Alcatel onetouch 993d
  • Sistema operativo:
    Windows 8.1
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #6 il: 16 Febbraio 2014, 16:04:24 CET »
0
Il codice:
 private Handler mUpdateHandler = new Handler() {
        @Override
        public void handleMessage( Message msg )
        {
            // Do Job
            // Qui chiami setStereoVolume (o qualunque altro meccanismo per cambiare l'ampiezza dell'onda)

            // and program next update
            mUpdateHandler.removeMessages( 0 );
            mUpdateHandler.sendEmptyMessageDelayed( 0, cStatsPeriodMs );
        }
    };
va messo all'interno del thread? perchè nel caso vada messo fuori invece (nell'activity principale in cui lo lancio), non capisco come fa a riferirsi allo specifico Thread "wave".
setStereoVolume è un metodo della classe AudioTrack, la quale è istanziata all'interno della classe DispAudio, come faccio quindi ad utilizzarlo?
Come faccio ad imporre che l'aumento del volume avvenga ogni 100ms ma per un totale di 4000ms?
Allego il progetto completo:
Codice (Java): [Seleziona]
public class DispAudio {
       
        AudioTrack track;
           short[] buffer = new short[1024];
         
           public DispAudio( )
           {
              int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT );        
              track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
                                                AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
                                                minSize, AudioTrack.MODE_STREAM);
             
           
              track.play();
             
              }    
         
           public void scriviCampioni(float[] campioni)
           {   
              fillBuffer( campioni );
              track.write( buffer, 0, campioni.length );
           }
         
           private void fillBuffer( float[] campioni )
           {
              if( buffer.length < campioni.length )
                 buffer = new short[campioni.length];
         
              for( int i = 0; i < campioni.length; i++ )
                 buffer[i] = (short)(campioni[i] * Short.MAX_VALUE);;
           }
}

mentre nell' activity principale:

Codice (Java): [Seleziona]
wave = new Thread( new Runnable( )
    {
       public void run( )
       {                       
          final float frequency = 1000;
          float increment = (float)(2*Math.PI) * frequency / 44100; // angular increment for each sample
          float angle = 0;
          float amp=1;
     
          DispAudio disp = new DispAudio( );
          float campioni[] = new float[1024];
               
          while( play )
          {
                               
             for( int i = 0; i < campioni.length; i++ )
             {           
               
                 campioni[i] = (float) (amp * Math.sin( angle ));
                 angle += increment;
               
                }
                       
             disp.scriviCampioni( campioni );  
                               
          }
                                 
       }
    }) ;
 
        wave.start();  

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #7 il: 17 Febbraio 2014, 10:26:05 CET »
0
Se usi il volume non serve proprio un thread a parte.
Così funziona... solo che anche al volume minimo è abbastanza alto...

Codice: [Seleziona]
    public class DispAudio
    {
        static final int cPeriodMs = 100;
        static final int cDurationMs = 4000;

        AudioTrack track;
        short[] buffer = new short[1024];
        int iteration;
        int maxIteration;

        public DispAudio( )
        {
            int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT );
            track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
                    AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
                    minSize, AudioTrack.MODE_STATIC);
        }

        public void start()
        {
            track.play();

            track.setStereoVolume( 0, 0 );

            iteration = 0;
            maxIteration = cDurationMs/cPeriodMs;

            mUpdateHandler.sendEmptyMessageDelayed( 0, cPeriodMs );
        }

        public void scriviCampioni(float[] campioni)
        {
            fillBuffer( campioni );
            track.write( buffer, 0, campioni.length );
            track.setLoopPoints(0, campioni.length, -1);
        }

        private void fillBuffer( float[] campioni )
        {
            if( buffer.length < campioni.length )
                buffer = new short[campioni.length];

            for( int i = 0; i < campioni.length; i++ )
                buffer[i] = (short)(campioni[i] * Short.MAX_VALUE);;
        }

        private Handler mUpdateHandler = new Handler() {
            @Override
            public void handleMessage( Message msg )
            {
                iteration++;

                float minVol = track.getMinVolume();
                float maxVol = track.getMaxVolume();
                float volume = minVol + ((float)(iteration)/(float)(maxIteration)) * (maxVol-minVol);
                track.setStereoVolume( volume, volume );

                if( iteration < maxIteration )
                {
                    // and program next update
                    mUpdateHandler.removeMessages( 0 );
                    mUpdateHandler.sendEmptyMessageDelayed( 0, cPeriodMs );
                }
            }
        };
    }


        final float frequency = 1000;
        int samples = (int)Math.round((float)44100 / frequency);
        float increment = (float)(2*Math.PI) / (float)samples; // angular increment for each sample
        float angle = 0;
         DispAudio disp = new DispAudio( );
        float campioni[] = new float[samples];
        for( int i = 0; i < samples; i++ )
        {
            campioni[i] = (float) Math.sin( angle );
            angle += increment;
        }

        disp.scriviCampioni( campioni );

        disp.start();

Post unito: 17 Febbraio 2014, 10:30:11 CET
Mettendo

Codice: [Seleziona]
float maxVol = track.getMaxVolume();

campioni[i] = 0.5f * (float) Math.sin( angle );

è più ragionevole.
« Ultima modifica: 17 Febbraio 2014, 10:30:11 CET da arlabs, Reason: Merged DoublePost »

Offline InterDroid

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Alcatel onetouch 993d
  • Sistema operativo:
    Windows 8.1
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #8 il: 17 Febbraio 2014, 11:20:10 CET »
0
ti ringrazio per la completezza della risposta,ho eliminato la creazione del thread e inserito il codice che mi hai fornito, ma non sembra funzionare: ho solo del rumore in uscita (a -50 dB che riesco a vedere solo con un oscilloscopio e che sembra durare proprio 4s, dopodichè più nulla).Il volume del cell è comunque in ogni caso al massimo. Hai per caso idea di quale potrebbe essere il problema? Grazie! 

Post unito: 17 Febbraio 2014, 11:42:37 CET
rettifico: l'onda riesco a vederla,avevo scordato di inserire una riga di codice nel metodo writeSamples, ed effettivamente aumenta di volume, solo che non si tratta di un'onda sinusoidale pura: allego una foto del risultato, e comunque dopo pochi secondi (non necessariamente 4) in uscita non ho più nulla.
« Ultima modifica: 17 Febbraio 2014, 11:42:37 CET da InterDroid, Reason: Merged DoublePost »

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Generazione onda sinusoidale con ampiezza variabile
« Risposta #9 il: 17 Febbraio 2014, 13:43:03 CET »
+1
Quello che ho immesso è un onda sinusoidale pura... con play in loop.

Poi agisco alzando un po' il volume ogni 100ms.

In effetti, non l'ho stoppata dopo 4 sec, ma l'ho lasciata al volume massimo.
Puoi mettere stop nell'else nel handler.

Io sento perfettamente un fischio (che è un onda sinusoidale) che aumenta di volume.

A me questo sembra il modo più semplice... poi puoi ovviamente, come volevi fare tu, generare i sample con ampiezza crescente poco alla volta.
Ma è più complicato, l'unica cosa è che devi tenere traccia della fase (nel tuo esempio ogni volta ricominciavi a generare l'onda da 0).