Audio Rechteck Generator

atiemark

Mitglied
Hallo,

ich möchte gerne einen Signal Generator schreiben der ein Rechteck-Signal mit variabler Frequenz erzeugt.
Die Frequenz soll vom Benutzer per Maus in Echtzeit verändert werden können.

Mein Problem ist der Signalgenerator selbst, ich habe eine Methode implementiert (setFrequency) die die Frequenz bei Aufruf ändern soll, jedoch geschieht dies nur mit einer starken Verzögerung.

Da ich das meiste aus dieser Klasse aus Codesnippets gesammelt habe, bin ich mir nicht ganz sicher wo genau der Fehler liegen könnte oder wie dies zu beheben wäre.
Leider habe ich auch nach intensiver Suche nicht viel zu diesem Thema gefunden und hoffe nun auf Hilfe von außen!

Hier mal der Code (leider sehr lang):

Java:
package csig;

import javax.sound.sampled.*;


public class SQUPlayer extends Thread{

    
// AudioFormat Parameter
public static final int SAMPLE_RATE = 22050;
private static final int SAMPLE_SIZE = 16;
private static final int CHANNELS = 1;
private static final boolean SIGNED = true;
private static final boolean BIG_ENDIAN = true;


// Buffer Größe, verarbeiteter Chunk
public static final int BUFFER_SIZE = 1000;
public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2;

//Variablen
private AudioFormat format;
private Line.Info info;
private SourceDataLine auline;
private boolean done;
private byte [] sampleData = new byte[BUFFER_SIZE];
private long periodSamples;
private long sampleNumber;



//Konstruktor
public SQUPlayer() {

    // Create the audio format we wish to use
format = new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE, CHANNELS, SIGNED, BIG_ENDIAN);

info = new DataLine.Info(SourceDataLine.class, format);


}

//Frequency Setter
public void setFrequency(double frequency) 
    {
    periodSamples = (long)(SQUPlayer.SAMPLE_RATE / frequency);
    }


//Erzeugt Rechteck
protected double getSample() {
    
    double value;  
    double x = sampleNumber / (double) periodSamples;

        if (sampleNumber < (periodSamples / 2)) {
          value = 1.0;
        }  else  {
          value = -1.0;
        }
  
    sampleNumber = (sampleNumber + 1) % periodSamples;
    return value;
  }





  public int getSamples(byte [] buffer) {
    int index = 0;
    for (int i = 0; i < SQUPlayer.SAMPLES_PER_BUFFER; i++) {
      double ds = getSample() * Short.MAX_VALUE;
      short ss = (short) Math.round(ds);
      buffer[index++] = (byte)(ss >> 8);
      buffer[index++] = (byte)(ss & 0xFF);      
    }
    return SQUPlayer.BUFFER_SIZE;
  }





    @Override
public void run() {

        
int nBytesRead = 0;

try {

    // Get line to write data to
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
auline.start();

while ((nBytesRead != -1)) 
    {
    nBytesRead = getSamples(sampleData);

    if (nBytesRead > 0) 
        {
         auline.write(sampleData, 0, nBytesRead);
        }

    }

} catch(Exception e) {

    e.printStackTrace(); 
} 

      
finally 
    {
        auline.drain();
        auline.close(); 
    }

}

public void startPlayer() 

{
      start();
}

}


in meiner Main methode erzeuge ich ein neues Objekt meines SQUPlayers [Java]SQUPlayer player = new SQUPlayer()[/Java]
und rufe die Methode
Java:
player.setFrequency(
//Hier der Wert eines Sliders zB
jSlider1.getValue()
)

//und

player.startPlayer();

auf

Wenn ich nun den Slider und somit die Frequenz verändere geschieht dies mit großer Verzögerung.

Ich habe bereits versucht den Buffer (zeile 18 in der SQUPlayer Klasse) kleiner zu machen, das ändert jedoch garnichts.

Bin für jeden Vorschlag dankbar!
 

Andi_CH

Top Contributor
Ein ähnliches Problem hatten wir schon mal, worauf ich einige Verusche angestellt habe.
Der Code löst dein Problem nicht direkt (du musst sicher noch "sinus" durch "rechteck" ersetzen), aber es tönt ;-)

hth

Java:
import javax.sound.sampled.Clip;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.AudioFormat;
 
public class ToneGeneratorExample {
 
    public byte[] getMonoSinusTone(int frequency, AudioFormat af) {
        byte sampleSize = (byte) (af.getSampleSizeInBits() / 8);
        byte[] data = new byte[(int) af.getSampleRate() * sampleSize];
        double step_width = (2 * Math.PI) / af.getSampleRate();
        double x = 0;
        for (int i = 0; i < data.length; i += sampleSize) {
            int sample_max_value = (int) Math.pow(2, af.getSampleSizeInBits()) / 2 - 1;
            int value = (int) (sample_max_value * Math.sin(frequency * x));
            for (int j = 0; j < sampleSize; j++) {
                byte sample_byte = (byte) ((value >> (8 * j)) & 0xff);
                data[i + j] = sample_byte;
            }
            x += step_width;
        }
        return data;
    }
 
    public byte[] getStereoSinusTone(int frequency1, int frequency2, AudioFormat af) {
        byte sampleSize = (byte) (af.getSampleSizeInBits() / 8);
        byte[] data = new byte[(int) af.getSampleRate() * sampleSize  * 2];
        double stepWidth = (2 * Math.PI) / af.getSampleRate();
        double x = 0;
        for (int i = 0; i < data.length; i += sampleSize * 2) {
            int sample_max_value = (int) Math.pow(2, af.getSampleSizeInBits()) / 2 - 1;
            int value = (int) (sample_max_value * Math.sin(frequency1 * x));
            for (int j = 0; j < sampleSize; j++) {
                byte sampleByte = (byte) ((value >> (8 * j)) & 0xff);
                data[i + j] = sampleByte;
            }
            value = (int) (sample_max_value * Math.sin(frequency2 * x));
            for (int j = 0; j < sampleSize; j++) {
                byte sampleByte = (byte) ((value >> (8 * j)) & 0xff);
                int index = i + j + sampleSize;
                data[index] = sampleByte;
            }
            x += stepWidth;
        }
        return data;
    }
 
    public void play(int frequenzy) {
        AudioFormat af = new AudioFormat(44100, 16, 1, true, false);
        byte[] data = getMonoSinusTone(frequenzy, af);
        try {
            Clip c = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
            c.open(af, data, 0, data.length);
            c.loop(1);
            while(c.isRunning()) {
                try {
                    Thread.sleep(50);
                } catch (Exception ex) {}
            }
        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
        }
    }
 
    public void play(int frequenzyLeft, int  frequencyRight) {
        AudioFormat af = new AudioFormat(44100, 16, 2, true, false);
        byte[] data = getStereoSinusTone(frequenzyLeft, frequencyRight, af);
        try {
            Clip c = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
            c.open(af, data, 0, data.length);
            c.loop(1);
            while(c.isRunning()) {
                try {
                    Thread.sleep(50);
                } catch (Exception ex) {}
            }
        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        ToneGeneratorExample tge = new ToneGeneratorExample();
        if (args.length == 1) {
            tge.play(Integer.parseInt(args[0]));
        } else if (args.length == 2) {
            tge.play(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
        } else {
        	System.out.println("Mono Ton");
            tge.play(500);
            System.out.println("Ton auf dem linken Kanal");
            tge.play(500, 0);
            System.out.println("Ton auf dem rechten Kanal");
            tge.play(0, 500);
            System.out.println("Ton auf dem beiden Kanälen");
            tge.play(500, 1300);
            System.out.println("Fertig");
        }
    }
}
 

atiemark

Mitglied
Vielen Dank für deine Antwort Andi, das hilft mir etwas weiter.

Mein Hauptproblem besteht jedoch darin, dass ich die Frequenz des Tones während der Laufzeit fließend verändern möchte! (Wie bei einem Funktionsgenerator)
Das ist mit deinem Code nicht möglich.

Leider habe ich dazu gar keinen richtigen Ansatz, da ich nicht sicher bin ob das überhaupt möglich ist (ich hoffe es).
 

Andi_CH

Top Contributor
Natürlich ist das möglich - meine Prozeduren berechnen einen Ton (oder im Fall von stereo zwei Tönen) mit konstanter Frequenz - wenn du diese Berechnungsroutine änderst, dann tut das.

By the way - bist du dir sicher mit Rechteck? Das tönt nämlich ziemlich hässlich
 

atiemark

Mitglied
Ich möchte das erzeugte Signal gerne in Echtzeit streamen, um ohne große Verzögerungen die Frequenz ändern zu können.

Bei deinem Beispiel berechnest du zuerst ein byte array "data" in der Methode
Java:
getMonoSinusTone(int frequency, AudioFormat af)
mit einer bestimmten Länge, das in der Methode
Java:
play(int frequency)
aufgerufen wird, und dann von Clip c 1mal geloopt wird.

Muss ich also das byte array "data" so klein wie möglich machen damit man eine Änderung der Frequenz möglichst schnell zu hören bekommt? leider bin ich hier etwas aufgeschmissen :(

PS: ich möchte meine Soundkarte als Funktions/Signalgenerator misbrauchen, er soll später mal Sinus, Dreieck, Sägezahn etc erzeugen jedoch ist das Rechteck am wichtigsten (bin Elektronik/Technische Informatik - Schüler)
 

Andi_CH

Top Contributor
Mit ein wenig Fantasie findest du selbst heraus wie das geht - das habe ich irgendwann letztes Jahr geschrieben und weiss doch nicht mehr im Detail was ich da warum und wie mache...

Da wird ein Byte[] abgefüllt und das von einer Routine die sinus mit einer konstanten Frequenz rechnet.
Ändere die Routine so, dass sie eben Rechteck rechnet und das mit einer variablen Frequenz - wo ist das Problem?

Es gibt keine eingebauten Sinus oder Rechteckgeneraroren - weder auf der Soundkarte noch in Java - also muss man alles rechnen - der Delay ist durch die Buffergrösse gegeben und kann auch damit beeinflusst werden.

Java ist eh keine Highest Speed Sprache - also who cares. C wäre näher an der HW aber damit handelst du dir noch ganz andere Probleme ein.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
E Server Client Audio Allgemeine Java-Themen 6
E Server Client Audio Allgemeine Java-Themen 0
B Java Audio in Wörter/Phonems aufteilen Allgemeine Java-Themen 0
Seikuassi Compiler-Fehler Xuggler-Problem-Video > Audio bzw. Video > Audio-API Allgemeine Java-Themen 2
W audio files metadaten Allgemeine Java-Themen 2
X Audio Internet-Stream Allgemeine Java-Themen 2
Haave Audio Device Unavailable: Kein gleichzeitiger Zugriff auf Soundsystem möglich Allgemeine Java-Themen 7
X Audio-Eingabegerät auswählen (MIC oder LINE-IN) Allgemeine Java-Themen 11
A applet audio funktioniert nur sporadisch Allgemeine Java-Themen 4
T Audio streamen/verschicken Allgemeine Java-Themen 3
B Java und Audio (JMF, mp3) Allgemeine Java-Themen 4
S Java und Audio Allgemeine Java-Themen 5
N Kollision zwischen ImageIcon und Rechteck Allgemeine Java-Themen 1
M Quadrate in Rechteck packen Allgemeine Java-Themen 17
J Java eigenen Button programmieren (ob Cursor im Rechteck ist oder nicht..../button pressed or not) Allgemeine Java-Themen 6
L Bildauschnitt mit Rechteck wählen Allgemeine Java-Themen 0
S groesstes Rechteck innerhalb eines Polygons/Shape finden..? Allgemeine Java-Themen 5
D Rechteck um 90 Grad drehen Allgemeine Java-Themen 19
Buroto Arrays generator Allgemeine Java-Themen 10
E Random Generator Allgemeine Java-Themen 6
L Generator für einen Parser implementieren Allgemeine Java-Themen 13
1 Name Generator für Videos Allgemeine Java-Themen 1
S QR-Code generator Allgemeine Java-Themen 2
D Parser-generator für mathematische Funktionen Allgemeine Java-Themen 12
borobudur MVC Model Generator Allgemeine Java-Themen 2
boxi Registartions CodeBild generator Allgemeine Java-Themen 2
G Scanner-Generator zur Erkennung von Java Tokens Allgemeine Java-Themen 7
B Von neumann generator Allgemeine Java-Themen 7

Ähnliche Java Themen

Neue Themen


Oben