changeset 3796:d4e3c1f2177a

6938426: Concurrency bug in ALAW encoder causes random bursts of static/noise in output. Reviewed-by: stayer
author amenkov
date Thu, 03 Mar 2011 15:41:44 +0300
parents 1ef5b85d41f2
children d3df100509ad
files src/share/classes/com/sun/media/sound/AlawCodec.java test/javax/sound/sampled/FileWriter/AlawEncoderSync.java
diffstat 2 files changed, 118 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/media/sound/AlawCodec.java	Mon Feb 28 18:36:33 2011 +0300
+++ b/src/share/classes/com/sun/media/sound/AlawCodec.java	Thu Mar 03 15:41:44 2011 +0300
@@ -52,9 +52,6 @@
     private static final short seg_end [] = {0xFF, 0x1FF, 0x3FF,
                                              0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
 
-    private static final int tempBufferSize = 64;
-    private byte tempBuffer [] = null;
-
     /**
      * Initializes the decode tables
      */
@@ -199,12 +196,9 @@
         AudioFormat inputFormat = stream.getFormat();
 
         if( inputFormat.matches(outputFormat) ) {
-
             cs = stream;
         } else {
-
             cs = (AudioInputStream) (new AlawCodecStream(stream, outputFormat));
-            tempBuffer = new byte[tempBufferSize];
         }
 
         return cs;
@@ -264,6 +258,10 @@
 
     class AlawCodecStream extends AudioInputStream {
 
+        // tempBuffer required only for encoding (when encode is true)
+        private static final int tempBufferSize = 64;
+        private byte tempBuffer [] = null;
+
         /**
          * True to encode to a-law, false to decode to linear
          */
@@ -303,6 +301,7 @@
                 encodeFormat = outputFormat;
                 decodeFormat = inputFormat;
                 PCMIsBigEndian = inputFormat.isBigEndian();
+                tempBuffer = new byte[tempBufferSize];
             }
 
             if (PCMIsBigEndian) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/sound/sampled/FileWriter/AlawEncoderSync.java	Thu Mar 03 15:41:44 2011 +0300
@@ -0,0 +1,113 @@
+/**
+ * @test
+ * @bug 6938426
+ * @summary Tests that Alaw encoder works properly in multithreaded environment
+ * @author Alex Menkov
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+
+public class AlawEncoderSync {
+
+    static final int THREAD_COUNT   = 20;
+
+    static final AudioFormat pcmFormat = new AudioFormat(8000f, 16, 2, true, false);
+    static final int STREAM_LENGTH = 10;    // in seconds
+    static byte[] pcmBuffer;
+    static final AudioFormat alawFormat
+            = new AudioFormat(AudioFormat.Encoding.ALAW, 8000f, 8, 2, 2, 8000f, false);
+
+    static final ConversionThread[] threads = new ConversionThread[THREAD_COUNT];
+
+    public static void main(String[] args) {
+        preparePCMBuffer();
+        log("pcmStream size: " + pcmBuffer.length);
+
+        for (int i=0; i<THREAD_COUNT; i++) {
+            threads[i] = new ConversionThread(i);
+            threads[i].start();
+        }
+
+        for (int i=1; i<THREAD_COUNT; i++) {
+            try {
+                threads[i].join();
+            } catch (InterruptedException ex) {
+                log("Main thread was interrupted, exiting.");
+                return;
+            }
+        }
+
+        int failed = 0;
+        log("comparing result arrays...");
+        for (int i=1; i<THREAD_COUNT; i++) {
+            if (!Arrays.equals(threads[0].resultArray, threads[i].resultArray)) {
+                failed++;
+                log("NOT equals: 0 and " + i);
+            }
+        }
+        if (failed > 0) {
+            throw new RuntimeException("test FAILED");
+        }
+        log("test PASSED.");
+    }
+
+
+    static void preparePCMBuffer() {
+        pcmBuffer = new byte[STREAM_LENGTH * (int)pcmFormat.getSampleRate()
+                * (pcmFormat.getSampleSizeInBits() / 8) * pcmFormat.getChannels()];
+        for (int i=0; i<pcmBuffer.length; i++) {
+            pcmBuffer[i] = (byte)(Math.random() * 256.0 - 128.0);
+        }
+    }
+
+    static AudioInputStream createPCMStream() {
+        InputStream byteStream = new ByteArrayInputStream(pcmBuffer);
+        return new AudioInputStream(byteStream, pcmFormat, AudioSystem.NOT_SPECIFIED);
+    }
+
+    static class ConversionThread extends Thread {
+        public final int num;
+        public byte[] resultArray = null;
+        public ConversionThread(int num) {
+            this.num = num;
+        }
+        @Override
+        public void run() {
+            log("ConversionThread[" + num + "] started.");
+            try {
+                InputStream inStream = new ByteArrayInputStream(pcmBuffer);
+
+                AudioInputStream pcmStream = new AudioInputStream(
+                        inStream, pcmFormat, AudioSystem.NOT_SPECIFIED);
+                AudioInputStream alawStream = AudioSystem.getAudioInputStream(alawFormat, pcmStream);
+
+                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+                int read = 0;
+                byte[] data = new byte[4096];
+                while((read = alawStream.read(data)) != -1) {
+                    outStream.write(data, 0, read);
+               }
+               alawStream.close();
+               resultArray = outStream.toByteArray();
+            } catch (Exception ex) {
+                log("ConversionThread[" + num + "] exception:");
+                log(ex);
+            }
+            log("ConversionThread[" + num + "] completed.");
+        }
+    }
+
+    static void log(String s) {
+        System.out.println(s);
+    }
+
+    static void log(Exception ex) {
+        ex.printStackTrace(System.out);
+    }
+}