1 /* 2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.media.sound; 26 27 import javax.sound.sampled.AudioFormat; 28 import javax.sound.sampled.AudioInputStream; 29 import java.io.EOFException; 30 import java.io.IOException; 31 import java.io.InputStream; 32 33 /** 34 * A jitter corrector to be used with SoftAudioPusher. 35 * 36 * @author Karl Helgason 37 */ 38 public final class SoftJitterCorrector extends AudioInputStream { 39 40 private static class JitterStream extends InputStream { 41 42 static int MAX_BUFFER_SIZE = 1048576; 43 boolean active = true; 44 Thread thread; 45 AudioInputStream stream; 46 // Cyclic buffer 47 int writepos = 0; 48 int readpos = 0; 49 byte[][] buffers; 50 private final Object buffers_mutex = new Object(); 51 52 // Adapative Drift Statistics 53 int w_count = 1000; 54 int w_min_tol = 2; 55 int w_max_tol = 10; 56 int w = 0; 57 int w_min = -1; 58 // Current read buffer 59 int bbuffer_pos = 0; 60 int bbuffer_max = 0; 61 byte[] bbuffer = null; 62 63 public byte[] nextReadBuffer() { 64 synchronized (buffers_mutex) { 65 if (writepos > readpos) { 66 int w_m = writepos - readpos; 67 if (w_m < w_min) 68 w_min = w_m; 69 70 int buffpos = readpos; 71 readpos++; 72 return buffers[buffpos % buffers.length]; 73 } 74 w_min = -1; 75 w = w_count - 1; 76 } 77 while (true) { 78 try { 79 Thread.sleep(1); 80 } catch (InterruptedException e) { 81 //e.printStackTrace(); 82 return null; 83 } 84 synchronized (buffers_mutex) { 85 if (writepos > readpos) { 86 w = 0; 87 w_min = -1; 88 w = w_count - 1; 89 int buffpos = readpos; 90 readpos++; 91 return buffers[buffpos % buffers.length]; 92 } 93 } 94 } 95 } 96 97 public byte[] nextWriteBuffer() { 98 synchronized (buffers_mutex) { 99 return buffers[writepos % buffers.length]; 100 } 101 } 102 103 public void commit() { 104 synchronized (buffers_mutex) { 105 writepos++; 106 if ((writepos - readpos) > buffers.length) { 107 int newsize = (writepos - readpos) + 10; 108 newsize = Math.max(buffers.length * 2, newsize); 109 buffers = new byte[newsize][buffers[0].length]; 110 } 111 } 112 } 113 114 JitterStream(AudioInputStream s, int buffersize, 115 int smallbuffersize) { 116 this.w_count = 10 * (buffersize / smallbuffersize); 117 if (w_count < 100) 118 w_count = 100; 119 this.buffers 120 = new byte[(buffersize/smallbuffersize)+10][smallbuffersize]; 121 this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize; 122 this.stream = s; 123 124 125 Runnable runnable = new Runnable() { 126 127 public void run() { 128 AudioFormat format = stream.getFormat(); 129 int bufflen = buffers[0].length; 130 int frames = bufflen / format.getFrameSize(); 131 long nanos = (long) (frames * 1000000000.0 132 / format.getSampleRate()); 133 long now = System.nanoTime(); 134 long next = now + nanos; 135 int correction = 0; 136 while (true) { 137 synchronized (JitterStream.this) { 138 if (!active) 139 break; 140 } 141 int curbuffsize; 142 synchronized (buffers) { 143 curbuffsize = writepos - readpos; 144 if (correction == 0) { 145 w++; 146 if (w_min != Integer.MAX_VALUE) { 147 if (w == w_count) { 148 correction = 0; 149 if (w_min < w_min_tol) { 150 correction = (w_min_tol + w_max_tol) 151 / 2 - w_min; 152 } 153 if (w_min > w_max_tol) { 154 correction = (w_min_tol + w_max_tol) 155 / 2 - w_min; 156 } 157 w = 0; 158 w_min = Integer.MAX_VALUE; 159 } 160 } 161 } 162 } 163 while (curbuffsize > bbuffer_max) { 164 synchronized (buffers) { 165 curbuffsize = writepos - readpos; 166 } 167 synchronized (JitterStream.this) { 168 if (!active) 169 break; 170 } 171 try { 172 Thread.sleep(1); 173 } catch (InterruptedException e) { 174 //e.printStackTrace(); 175 } 176 } 177 178 if (correction < 0) 179 correction++; 180 else { 181 byte[] buff = nextWriteBuffer(); 182 try { 183 int n = 0; 184 while (n != buff.length) { 185 int s = stream.read(buff, n, buff.length 186 - n); 187 if (s < 0) 188 throw new EOFException(); 189 if (s == 0) 190 Thread.yield(); 191 n += s; 192 } 193 } catch (IOException e1) { 194 //e1.printStackTrace(); 195 } 196 commit(); 197 } 198 199 if (correction > 0) { 200 correction--; 201 next = System.nanoTime() + nanos; 202 continue; 203 } 204 long wait = next - System.nanoTime(); 205 if (wait > 0) { 206 try { 207 Thread.sleep(wait / 1000000L); 208 } catch (InterruptedException e) { 209 //e.printStackTrace(); 210 } 211 } 212 next += nanos; 213 } 214 } 215 }; 216 217 thread = new Thread(null, runnable, "JitterCorrector", 0, false); 218 thread.setDaemon(true); 219 thread.setPriority(Thread.MAX_PRIORITY); 220 thread.start(); 221 } 222 223 public void close() throws IOException { 224 synchronized (this) { 225 active = false; 226 } 227 try { 228 thread.join(); 229 } catch (InterruptedException e) { 230 //e.printStackTrace(); 231 } 232 stream.close(); 233 } 234 235 public int read() throws IOException { 236 byte[] b = new byte[1]; 237 if (read(b) == -1) 238 return -1; 239 return b[0] & 0xFF; 240 } 241 242 public void fillBuffer() { 243 bbuffer = nextReadBuffer(); 244 bbuffer_pos = 0; 245 } 246 247 public int read(byte[] b, int off, int len) { 248 if (bbuffer == null) 249 fillBuffer(); 250 int bbuffer_len = bbuffer.length; 251 int offlen = off + len; 252 while (off < offlen) { 253 if (available() == 0) 254 fillBuffer(); 255 else { 256 byte[] bbuffer = this.bbuffer; 257 int bbuffer_pos = this.bbuffer_pos; 258 while (off < offlen && bbuffer_pos < bbuffer_len) 259 b[off++] = bbuffer[bbuffer_pos++]; 260 this.bbuffer_pos = bbuffer_pos; 261 } 262 } 263 return len; 264 } 265 266 public int available() { 267 return bbuffer.length - bbuffer_pos; 268 } 269 } 270 271 public SoftJitterCorrector(AudioInputStream stream, int buffersize, 272 int smallbuffersize) { 273 super(new JitterStream(stream, buffersize, smallbuffersize), 274 stream.getFormat(), stream.getFrameLength()); 275 } 276 }