8 Replies Latest reply: Oct 23, 2010 5:21 AM by 805365 RSS

    Reading from audioInputStream and getting its power level.

    805365
      Hello:

      I am trying to read an audioInputStream (play it) and showing its power level in a meter.In the "while" loop after the
      audioInputStreamaudio.read(byte[] b,int off,int len)
      , I calculate the power of the buffer
      byte[] 
      before the
      line.write(byte[] b,int off,int len)
      . This presents two problems:

      1: It has some delay (I am using a buffer of 125ms)

      2: The sound keeps playing because it has reached the end of the audioStream and goes out of the loop and the call to the method that calculates the power of the buffer is inside.
      try {
                      while ((nBytesRead = ais.read(abData, 0, abData.length)) != -1) {
                          if (medidor != null) {
                              vumeter(abData, audioFormat);
                          }
                          while (pause) {
                              if (line.isRunning()) {
                                  line.stop();
                                  vumeter(emptyArray, audioFormat);
                              }
                              try {
                                  wait();
                              } catch (InterruptedException e) {
                              }
                          }
      
                          if (!line.isRunning()) {
                              line.start();
                          }
                          line.write(abData, 0, nBytesRead);
      
                      }
                  } catch (IOException e) {
                  }
                  line.drain();
                  line.stop();
                  vumeter(emptyArray, audioFormat);
                  line.close();
      What would be a good aproach?

      Thank u

      Edited by: 802362 on 14-oct-2010 1:43

      Edited by: 802362 on 14-oct-2010 2:47

      Edited by: 802362 on 14-oct-2010 2:55

      Edited by: 802362 on 14-oct-2010 2:56

      Edited by: 802362 on 14-oct-2010 3:00

      Edited by: 802362 on 14-oct-2010 3:01
        • 1. Re: Reading from audioInputStream and getting its power level.
          captfoss
          I would assume the approach is fine, but I'd recommend running your view meter on a separate thread... something that just reads some shared memory and outputs the level...

          IE
          while(true) {
            vumeter(abData, audioFormat);
            wait();
          }
          And then at the top of your while loop, just signal the vuemeter thread to wake up and run the calculations again...
          • 2. Re: Reading from audioInputStream and getting its power level.
            805365
            thank you for your answer but i tried what you said and i don't get to make it work:

            My method for playing is now:
            public synchronized void play() {
                        initEveryThing();
                        MyTask myTask = new MyTask(abData, audioFormat);//this the new Thread
                        try {
                            while ((nBytesRead = ais.read(abData, 0, abData.length)) != -1) {
                                if (meter != null) {
                                    notifyAll();//try to wake up the thread myTask from wait, but doesn't work
            
                                }
                                while (pause) {
                                    if (line.isRunning()) {
                                        line.stop();
                                    }
                                    try {
                                        wait();
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                if (!line.isRunning()) {
                                    line.start();
                                }
                                line.write(abData, 0, nBytesRead);
                            }
                        } catch (IOException e) {
                        }
                        line.drain();
                        line.stop();
                        line.close();
            play() was already a synchronized method because i use the next method to wake it up when i pulse a pause botton:
                  public synchronized void doNotify(){
                 notifyAll();
            }
            This keeps working but the new thread "myTask" is never woken up from the play()method. The class MyTask is:
            private class MyTask implements Runnable {
            
                        Thread t = new Thread(this);
                        byte[] theBytes;
                        AudioFormat af;
            
                        MyTask(byte[] bytes,AudioFormat audioFormat) {
                            theBytes = bytes;
                            af = audioFormat;
                            t.start();
                        }
            
                        public synchronized void calculate() {
                            System.out.println("Running");
                            
                                vumeter(losBites, af);
                                try {
                                    wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                
                            
                        }
            
                        public void run() {
                            while (true) {
                                calculate();
                            }
            
                        }
            I am getting a bit frustrated with this "thread" related code.Thanks in advance
            • 3. Re: Reading from audioInputStream and getting its power level.
              captfoss
              NotifyAll only wakes up threads that are syncronized on the same object's monitor... because you're using class-level syncronization, your instance of "MyTask" and your instance of whatever the play function is a part of, are using different monitors.

              I believe you'd want to do myTask.notify() or myTask.notifyAll(), other than this.notifyAll()... Maybe not. I'm not a threading expert by any means. You could probably post the question in the threading forum (concurrency, I believe) to get an expert's opinion on the matter...
              • 4. Re: Reading from audioInputStream and getting its power level.
                805365
                Thank you for your answer.

                Besides the thread confusion about calling notify...the "problem" is the blocking nature of line.write(). Since I want to calculate the power of the buffer I have to do it before it gets to line.write() because it will block until all bytes are played. The good part of this is that I guarantee that each loop i have calculated and shown the value. The bad thing is that I can't sync the playing of the buffer with the value of the vumeter shown. I separated the calculation with the showing of the calculation. The calculation is done only when ais.read is done and the line is written only when the calculation of the vumeter is done, doing the calculation in a thread and calling join before writting to line.

                Since I am using a 125ms. buffer I will never find a "real sync" between values of vumeter and buffer played. If a listen to a percusive sound it will depend on when inside the buffer this is done because i will shown the value of the buffer a little bit before its played so if the percusive sound is in the beginning of the buffer it will look like a peakmeter but if it near the end of the buffer it will lool like it's delayed. Anyhow I don't need a peakmeter, it's just that I didn't realized this until I have done it.

                Thanks.
                • 5. Re: Reading from audioInputStream and getting its power level.
                  captfoss
                  No, I don't think any of that is true...

                  The audio data is going to be played at the sample rate assuming you keep the internal play buffers full... so, all you need to do is syncronize the display with the playback...

                  JavasSound supports line syncronization
                  http://download.oracle.com/javase/1.5.0/docs/guide/sound/programmer_guide/chapter4.html#113725

                  So, you could theoretically write a custom Line class that displays audio data and syncronize it with the line that's playing the data... and you'd be guaranteed to be displaying what's playing, assuming you implement the syncronization correctly...
                  • 6. Re: Reading from audioInputStream and getting its power level.
                    805365
                    Thanks again for your answer.

                    I know what you mean, but what I'm saying is that if you calculate the power signal of a buffer as long as 125ms,regardless of the time you display this info you can have a lot of power in that buffer when it's ending or at the begining and when it's displayed your eyes and ears are accurate enough to know that are not in sync. I am not talking about staying in total sync with line.write(). In other words, a vumeter has a ballistic of 300ms,where as a peak meter has a 5ms integration time which will make ourselves think that music and instant peaks are in sync.
                    • 7. Re: Reading from audioInputStream and getting its power level.
                      captfoss
                      802362 wrote:
                      if you calculate the power signal of a buffer as long as 125ms
                      Human eye is capable of picking up motion around 60 fps, so calculate the power based on a sliding-window of 125milliseconds every 15 milliseconds or so...

                      At an average samplerate of 44Khz, you'd only need to recalculate what? 44,000 samples a second, 44 samples a millisecond, so 660 (new) samples per window? There are rolling calculations methods that you could use to optimize the calculations...
                      • 8. Re: Reading from audioInputStream and getting its power level.
                        805365
                        Thans again for you time and answers. Yes, the sliding window shown each less time is a good idea. Let's see if even 25Hz is enough:

                        The persistance of vision is below 60 hz:
                        http://en.wikipedia.org/wiki/Persistence_of_vision

                        Thanks