kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Klangerzeugung mit Android unter Verwendung einer Audio-Datei.

(EN google-translate)

(PL google-translate)

Das vorhergehende Programm wird so ergänzt, dass eine wav-Audio-Datei zyklisch gespielt wird anstatt eines Sinustones.

Einige Hinweise zu dem nachfolgenden Programm

Die Samplingrate der wav - Datei sr_wav könnte unterschiedlich zu der des Audiostreams sr sein. Dies wird in der Schleife berücksichtigt, wo zyklisch der Puffer aktualisiert wird.

Es wird die Technik eines kontinuierlichen Zeitstroms t beibehalten. Aus t wird zunächst der Zeitpunkt t_file berechnet. Das ist die zeitliche Stelle, die gerade von der zyklischen Datei gespielt werden muß.

Der Zeitpunkt t_file liegt im allgemeinen zwischen zwei Samples. Darum muß bestimmt werden welche beiden Samples das sind. Ihre Indices sind index_lo und index_hi.

Es wird dann noch bestimmt wo genau zwischen diesen Samples die aktuelle Zeit liegt und beide Samples werden gewichtet berücksichtigt.

Das alles ist sehr allgemein gehalten und kann in späteren Programmen etwas vereinfacht werden, wenn bestimmte Randbedingungen gegeben sind, wie beispielsweise, dass sr gleich sr_wav ist u.ä.

Bitte beachten Sie, dass die verwendete .wav-Datei zuvor in einen Unterordner "data" des Sketch Ordners gelegt werden muß.

Die Methode zum Laden der wav-Datei kann einfach benutzt werden und wird hier nicht weiter erklärt.

//Shortest program to just play two sinus waves with Android

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import android.media.AudioTrack;
import android.media.AudioFormat;
import android.media.AudioManager;

import android.media.AudioRecord;
import android.media.MediaRecorder;

Sound sound;
float[] mywavtone;
public void setup()
{
    mywavtone = ladeWavMix("ton69.wav");
    sound = new Sound();
    fullScreen();
    orientation(LANDSCAPE);
}

public void draw()
{
    background(0,0,255);
}

public class Sound implements Runnable
{
    private AudioTrack audioTrack; //Connection to sound card
    private int sr=44100; //Sample rate
    private int sr_wav=44100; //Sample rate of the loaded tone. So this is handled seperately.
    //Thus, you do not care about if changing sampling rate sr. It may differ to one of sound files!
    private int buffsize=512; //buffer size
    private int buffsize2=buffsize*2;
    private ScheduledExecutorService schedExecService; //realtime process    
    private short[] shortbuffer = new short[buffsize*2]; //stereo buffer l0 r0 l1 r1 l2 r2 ... frequently sent to sound card

    private double t=0.0; //realtime
    private double dt = 1.0/(double)sr; //time step according to sampling rate.
    private double dt_wav = 1.0/(double)sr_wav; //time step according to sampling rate of loaded file.
    private float MAX_SHORT = 32767.0;
    public Sound()
    {
        try
        {                                                
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sr, 
                                    AudioFormat.CHANNEL_OUT_STEREO, 
                                    AudioFormat.ENCODING_PCM_16BIT, 
                                    buffsize*10, 
                                    AudioTrack.MODE_STREAM);
            audioTrack.setStereoVolume(1.0f, 1.0f);
            
            audioTrack.play();            
        }
        catch(Exception eee)
        {
            System.out.println("FEHLER: "+eee);
        }     
        
        schedExecService = Executors.newSingleThreadScheduledExecutor();
        long period = (buffsize*1000)/sr; //Seconds per Beat==60/BPM, die Hälfte weil 8tel, mal 1000 weil Millisekunden.
        schedExecService.scheduleAtFixedRate(this, 0, period, TimeUnit.MILLISECONDS);                
        
        
    }
    public void run()
    {
        for(int i=0;i<buffsize2;i+=2)
        {
             //ACCURACY OF TIME SHOULD BE HIGHER THAN ONE OF AMPLITUDE.
             //SO TIME IS CALCULATED USING double AND AMPLITUDE USING float.
          
             //This may be the part of the programm where you might want to modify something like
             //introducing other sound sources / directional sound and so on:
             
             //Calculate the actual sample from the provided wav-file:
             //Actual time may be in between two samples:
             double T_entire = dt_wav*(double)mywavtone.length; //duration of file
             double t_file = t - T_entire*Math.floor(t/T_entire); //actual time in looped wav file
             
             int index_lo = (int)(t_file/(double)dt_wav); //first sample index
             int index_hi = (index_lo+1)%mywavtone.length; //following sample index
             
             //calculating weight for each sample:
             double t_part = t_file - dt_wav*Math.floor(t_file/dt_wav);
             double part   = t_part/dt_wav; //range [0,1]
             float weight_lo = 1.0 - (float)part;
             float weight_hi = (float)part;
             
             float amplitude = weight_lo*mywavtone[index_lo] + weight_hi*mywavtone[index_hi];
             
             float left  = MAX_SHORT*amplitude;
             float right = MAX_SHORT*amplitude;
             shortbuffer[i]=(short)left;
             shortbuffer[i+1]=(short)right;
             t+=dt; //increment of realtime for each sample
        }
      
        audioTrack.write(shortbuffer, 0,buffsize2);      
    }
}

    /**
     * Loads a Stereo-Wav-Datei. It is mixed to one channel and provided as float-Array.
     */
    public float[] ladeWavMix(String name) //Gleich nur Mix aus linkem und rechtem Kanal laden (aus 44100 2x16Bit extrahieren)
    {
            int zl,zh,lo,gesamt;
            float gross = 0.5f*(255.0f*256.0f + 255.0f);          
            byte[] dat = loadBytes(name);
            int anz = dat.length;          
            float[] y = new float[(anz-44)/4];                        

            int inx=44;
            
            for(int i=0;i<y.length;i++)
            {
                 zl = dat[inx++];
                 zh = dat[inx++];
                 if(zl>127)
                     zl-=256;
            
                 if(zh>127)
                     zh-=256;

                 lo     =  zl;
                 if(lo<0)
                     lo+=256;
                 gesamt = (zh+128)*256;                
                 gesamt+=lo;
                
                 y[i] = 0.5f*((float)gesamt - gross)/gross;        

                 zl = dat[inx++];
                 zh = dat[inx++];
                 if(zl>127)
                     zl-=256;            
                 if(zh>127)
                     zh-=256;

                 lo     =  zl;
                 if(lo<0)
                     lo+=256;
                 gesamt = (zh+128)*256;
                
                 gesamt+=lo;
                
                 y[i] += 0.5f*((float)gesamt - gross)/gross;                         
            }                
            return y;    
    }

Code 0-1: Androidsound002

Androidsound002.zip -- Sketch folder.