How to get sound from an array to my sound card with minim (AudioOutput).

edited February 2015 in How To...

Hi to all the users out there. This is my first post and it is a question.Now, I've made a sketch with MPX demodulation and analysis in mind . MPX is a multiplexed signal for FM Stereo transmission. I got to the point that i demodulated the MPX and now i have two arrays with the sample for the Left and Right audio channels but i cannot find a way to 'stream' these arrays to my sound card. So now that is my question : How to go from arrays to the AudioOutput command in minim . The arrays are named "left" and "right" and they both are 8192 sample long and the sample rate is 192Khz.

Thank you very much. (I am a newbie so any help will be appreciated).

Answers

  • So it seems that there is no way to get sound out of a buffer. Or at least it looks like no one knows how to do it. Thank you very much for looking up the topic. But if there is a solution i will be interested in learning how to do it.

  • You can do it directly with java... I will have to dig around for the code I had but from memory it was by using javax.sound.sampled.AudioFormat, opening a data line and writing the data array to it, if that gives you a start?!! Will post again soon!

  • Thank you very much. I will be waiting. Any kind of audio out will do since i can do anything else by myself (re sampling, filtering and all the other usual things)

    Sincerely Yours Konstantinos

  • OK, here's some code, a very limited class to open a data line and write audio data - very incomplete (but it works) and far from 'good' .

    To use... create an instance and call play() followed by write() with your audio data.

    The idea was to do something similar to android's AudioTrack class to use for audio work rather than a library like Minim, with the aim of being able to then write audio code that will work with minimal changes on android/PC/Mac. Perhaps next year :)

    Hope it is of some use to you?!

    import java.io.File;
    import java.io.IOException;
    import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.DataLine;
    import javax.sound.sampled.LineUnavailableException;
    import javax.sound.sampled.SourceDataLine;
    import java.io.ByteArrayInputStream;
    
    public class AudioTrack {
      private static final int  EXTERNAL_BUFFER_SIZE = 5512;
      AudioInputStream  audioInputStream = null;
      byte[] audioData;
      int bufferSizeInBytes;
      SourceDataLine  line = null;
    
          AudioTrack(int _streamType, int _sampleRateInHz, int _channelConfig, int _audioFormat, int _bufferSizeInBytes, int _mode) {
    
    bufferSizeInBytes = _bufferSizeInBytes;
    
        //this next bit is a fudge! ... load an existing audio file and use it to get parameters (audioFormat) to set up the dataline. It work but really needs doing properly! The file I used was a mono 44.1kHz, 16bit snare sample.
    
        try {
          audioData = loadBytes("snareStereo.wav"); 
          InputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
          audioInputStream = AudioSystem.getAudioInputStream(byteArrayInputStream);
        }
        catch (Exception e) 
        AudioFormat  audioFormat = audioInputStream.getFormat();
        DataLine.Info  info = new DataLine.Info(SourceDataLine.class, audioFormat);
    
    try {
          line = (SourceDataLine) AudioSystem.getLine(info);
          line.open(audioFormat);
        }
        catch (LineUnavailableException e) {
          e.printStackTrace();
          System.exit(1);
        }
        catch (Exception e) {
          e.printStackTrace();
          System.exit(1);
        }
      }
      void play() {
        line.start();
      }
    //write stereo buffer to dataline
    void write(short[] _audioData, int _offset, int _dataLength) {
        audioData = convertShortArrayToByteArray(_audioData) ;
        line.write(audioData, 0, 2*_dataLength);
      }
    void release() {
        line.drain();
        line.close();
      }
    }
    
  • So now i have to test it. To make it work i will have to just call a play(); and a write ();? This might be a very stupid question but as i am very new to this staff is actually a big problem for me. So, i am sure that i have to remove all the things that are used to take the format and data of the snareStereo.wav and set them myself. But how i am going to get the stereo feed out from one array? Currently i am using 2 arrays called left and right . How i am going to get them combined in one without mixing the two channels ?

  • edited February 2015

    Ah...

    I will tidy up the class as it does need doing (I will need it again soon), but if for now you have a suitable 192 kHz wav file to use in place of my snare sample then I am guessing that may work as a source for the AudioFormat needed to set up the data line. Or if you can make a 16 bit / 44.1kHz version of your data for now? If you have other than 16-bit samples arrays of shorts won't work... you will need whatever is needed .. 32-bit would be int etc 24-bit also int but only 3 bytes worth. I will have to read up a bit about what formats are playable by a java audioline as I can't remember.

    Anyway, some code that should work with 44.1/16bit without too much trouble:)

    //create an instance and array for data
    
    //before setup()...
    AudioTrack audioTrack;
    int arrayLength = size of your left OR right array;
    short[] stereoArray = new short[2*arrayLength];
    
    //in setup()... edit sample rate to 192kHz?
    audioTrack = new AudioTrack(3, 44100, 12, 2, 22050, 1); 
    
    //the 22050 bufferSize is what I used as it was the size of my snare sample
    //This may need changing  in your case? 
    
    //copy your arrays to the stereo array (samples alternate L/R)
    //do this wherever!? Ideally in a function or class method, 
    //but could be in setup if only ever needs to be done once.
    for (int i=0; i<arrayLength; i++) {
     stereoArray[i]=leftArray[i]; //your left array data
     stereoArray[2*i]=rightArray[i]; //your right array data
    }
    
    //call play ... in a function/method or in draw()
    audioTrack.play();  /// opens data line
    
    //audioTrack now sits waiting for you to send it some data
    //maybe in mousePressed() to 'trigger'
    write(stereoArray, 0, steroArray.length) ; //
    

    Hope that is of some use? Sorry it's messy so good luck! I really do need to improve it, but it did for me at the time and I am mostly writing just for android now...where the AudioTrack class is nice and ready to use!

  • Also... for info, not all the parameters used in the class constructor are needed/used!! They are there simply because I want it to be identical to the android AudioTrack. This will mean I can can create an AudioTrack in Java mode or Android mode without having to edit the code... wasteful, but useful for me. ;)

  • ok thank you very much. I am going to give it a try real quick now.....

  • I am getting a problem. It says cannot find class or type named "AudioTrack". I have copied all the import strings but with no luck.

  • edited February 2015

    good luck, let me know how it goes

  • It seems that AudioTrack is only available for android devices.

  • Android has the AudioTrack class as standard. What I have done is created a version... the class above called AudioTrack... that copies some of what the android version does, to play wav data in java. Copy the class I placed above into a tab in Processing and when you create an instance it will use it.

  • Any progress... just interested to see how you are getting on!

  • Nop. I wasn't able to make it work. Thank you very much for all the help you provided me.

  • HI again. It seems that now i am really close to making it work. But i have a problem. It says that the function write does not exist but it does ... Any ideas why it is doing so ?

  • Btw for anybody that is still interested i found the solution for minim ! You must initialize an AudioSignal make two fifo bufers so the input sound card and output sound card don't have to be synchronized to each other and fill those buffers one block at a time and then read them back out one block at a time.

    Example code :

    Minim minim;
    AudioInput in;
    AudioOutput out; 
    
    int fifo_blocks=8; //How many blocks the fifo buffer will have  (at least 2)
    float [] [] fifo_output =new float [2][buffersize*fifo_blocks]; 
    int fifo_in_pointer=0;
    int fifo_out_pointer=fifo_blocks/2;
    
    
            void setup() {
             minim = new Minim(this);
             in = minim.getLineIn(Minim.STEREO, Buffersize, Samplerate);
             in.addListener(new Listener()); 
    
             out = minim.getLineOut(Minim.STEREO, buffersize, samplerate);
              out.addSignal(new MySignal());
             } 
    
        class Listener implements AudioListener {
          synchronized  public void  samples(float[] sample) {
          }
          synchronized public void samples(float[] left, float[] right) {
         for (int i=0; i<buffersize; i++ ) 
            {
              fifo_output[0] [i+(buffersize*fifo_in_pointer)]=left[i]; 
              fifo_output[1] [i+(buffersize*fifo_in_pointer)]=right[i];
    
            }
    
            if (fifo_blocks-1==fifo_in_pointer) {
              fifo_in_pointer=0;
            } else {
              fifo_in_pointer++;
            }
          }
    
    
    
        class MySignal implements AudioSignal {  
          synchronized  public void generate(float [] lm) {
          }
    
    
    
          synchronized void generate(float[] left, float[] right) {
    
    
            for (int i=0; i<buffersize; i++ ) 
            { 
              left[i]=fifo_output[0] [i+(buffersize*fifo_out_pointer)];  
              right[i]= fifo_output[1] [i+(buffersize*fifo_out_pointer)];
            }
            if (fifo_blocks-1==fifo_out_pointer) {
              fifo_out_pointer=0;
            } else {
              fifo_out_pointer++;
            }
          }
        }
    
Sign In or Register to comment.