title: Subtle 'ticks' noise in WAVE files with AudioRecord
body: <p>My app saves WAVE MONO files at 44100 and 16BIT PCM captured with <code>AudioRecord</code> using a RODE VideoMicro. Everything works just fine, except for the fact that I can see some very subtle &quot;ticks&quot; in the WAVE files uniformly spaced by 0.135 seconds. You can hear the ticks if your volume is very loud, or you can see them clearly using Audacity. <a href="https://luis.impa.br/photo/lixo/tics.wav" rel="nofollow noreferrer">Here is a sample WAVE file for you to analyze</a>, and here is a screenshot of Audacity with 4 ticks in the soundfile:</p>
  <p><a href="https://i.stack.imgur.com/2caj2.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/2caj2.jpg" alt="enter image description here" /></a></p>
  <p>Any idea what is happening?</p>
  <p>I am not sure where those ticks come from, but I suspect that there is something strange in my code, since the tics are so uniformly spaced. But of course it could be either the phone, or the external microphone, or the interaction between the two. The <code>BufferSize</code> is 3528 and is retrieved with</p>
  <pre><code> static int BufferSize = AudioRecord.getMinBufferSize(SampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
  </code></pre>
  <p>This is the relevant part of my code:</p>
  <pre><code>            ...
              recorder = new AudioRecord((denoise.isChecked()) ? MediaRecorder.AudioSource.VOICE_RECOGNITION : MediaRecorder.AudioSource.MIC,
                  SampleRate, AudioFormat.CHANNEL_IN_MONO, AudioEncoding, 2 * BufferSize); // 2 bytes in 16bit format
  
          try {
              recordingThread = new Thread(this::writeAudioDataToPCMFile, &quot;AudioRecorder Thread&quot;);
              recordingThread.start();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
  }
  private void writeAudioDataToPCMFile() {
      String auxfile = AveActivity.REC_DIR + &quot;/aux&quot; + new Random().nextInt() + &quot;.pcm&quot;;
      short[] sData = new short[BufferSize];
      int product;
      int i, readSize;
      int tooloud;
      int tooloudlim = BufferSize / 50; // 2% clip
      long mean;
      FileOutputStream os;
      try {
          os = new FileOutputStream(auxfile);
      } catch (FileNotFoundException e) {
          runOnUiThread(() -&gt; textsound.setText(r.getString(R.string.errstart)));
          e.printStackTrace();
          return;
      }
      waveformLastList = new ArrayList&lt;&gt;();
  
      //int j=0;
      while (isRecording) {
          readSize = recorder.read(sData, 0, BufferSize);
          tooloud = 0;
          mean = 0;
          for (i = 0; i &lt; readSize; ++i) {
              product = (int) (sData[i] * gain);
              if (Math.abs(product) &lt;= Short.MAX_VALUE) {
                  sData[i] = (short) product;
              } else {
                  // Audio clipping!
                  sData[i] = (short) (Integer.signum(product) * Short.MAX_VALUE);
                  ++tooloud;
              }
              mean += sData[i] * sData[i];
          }
          if (tooloud &gt; tooloudlim)
              runOnUiThread(this::tooLoudWarning);
  
          try {
              os.write(short2byte(sData), 0, 2 * BufferSize); // 2 bytes in 16bit format
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      try {
          os.close();
          CharSequence time = DateFormat.format(&quot;yyMMdd-kkmmss&quot;, new Date(System.currentTimeMillis()));
          song = birdname + &quot;_&quot; + time + ((denoise.isChecked()) ? &quot;_NR&quot; : &quot;&quot;) + ((db == 0) ? &quot;&quot; : &quot;_db&quot; + db) + &quot;.&quot; + songFormat;
          runOnUiThread(() -&gt; textsound.setText(r.getString(R.string.saving, songFormat)));
          PCMtoWAV(new File(auxfile), new File(AveActivity.REC_DIR, song));
      } catch (IOException e) {
          runOnUiThread(() -&gt; textsound.setText(r.getString(R.string.errsaving, songFormat)));
          e.printStackTrace();
      }
      runOnUiThread(() -&gt; {
          activatebtns();
          micColor(Color.WHITE);
      });
  }
  private void PCMtoWAV(File input, File output) throws IOException {
      // Bien rapido: 1/10 sec para cada minuto de gravacion
      byte[] data = new byte[2 * BufferSize];
      try (FileOutputStream outStream = new FileOutputStream(output)) {
          FileInputStream inStream = new FileInputStream(input);
          // Write WAVE header
          outStream.write(buildWAVHeader(input.length()));
          // Write audio data
          while (inStream.read(data) != -1)
              outStream.write(data);
          // Write Metadata to LIST....INFO chunk
          outStream.write(buildWAVMeta());
          inStream.close();
          outStream.close();
          input.delete();
      }
  }
  </code></pre>
  <p><strong>EDIT:</strong></p>
  <p>Following Uriel advice, I implemented a <code>BlockingQueue</code> like this:</p>
  <pre><code>public void RecordSong() {
      if (isRecording) {
         isRecording = false;
         recorder.stop();
         recorder.release();
         recordingThread.interrupt();
         writingThread.interrupt();
      ...
      } else {
          recorder = new AudioRecord(...);
          try {
            LinkedBlockingDeque&lt;Byte&gt; queue = new LinkedBlockingDeque&lt;&gt;();
            recorder.startRecording();
            recordingThread = new Thread(() -&gt; captureAudioData(queue), &quot;AudioRecorder Capture Thread&quot;);
            writingThread = new Thread(() -&gt; writeAudioDataToPCMFile(queue), &quot;AudioRecorder Writing Thread&quot;);
            recordingThread.setPriority(Thread.MAX_PRIORITY);
            writingThread.setPriority(Thread.MAX_PRIORITY);
            recordingThread.start();
            writingThread.start();
            ...
         }
    }
  
  private void captureAudioData(LinkedBlockingDeque&lt;Byte&gt; q) {
      ...
      while (isRecording) {
          capture,gain,etc...
      
          try {
              for (byte b : short2byte(sData)) q.put(b);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  
  private void writeAudioDataToPCMFile(LinkedBlockingDeque&lt;Byte&gt; q) {
      String auxfile = AveActivity.REC_DIR + &quot;/aux&quot; + new Random().nextInt() + &quot;.pcm&quot;;
      FileOutputStream os;
      try {
          os = new FileOutputStream(auxfile);
      } catch (FileNotFoundException e) {
          e.printStackTrace();
          return;
      }
      
      try {
          while (true) os.write(q.take());
      } catch (IOException e) {
          e.printStackTrace();
      } catch (InterruptedException e) {
          try {
              os.close();
          } catch (IOException e1) {
              e.printStackTrace();
          }
      }
  }
  </code></pre>
  <p><strong>EDIT 2:</strong></p>
  <p>I tried the new approach (locked in the bathroom at 2am!). Unfortunately the problem persists. No change. It seems it is either a phone issue or a mic issue. In fact, removing the mic seems to get rid of the ticks, so probably it is a mic issue.</p>