diff --git a/core/PSound.java b/core/PSound.java index 0c25af8d5..f1a2d0ba0 100755 --- a/core/PSound.java +++ b/core/PSound.java @@ -25,6 +25,7 @@ package processing.core; import java.io.*; +import java.lang.reflect.*; import sun.audio.*; // add check for reflection in host applet for sound completion @@ -32,18 +33,30 @@ import sun.audio.*; /** - * This is the crappy 8 khz ulaw version that's compatible + * This is the crappy 8 khz mono ulaw version that's compatible * with Java 1.1 and 1.2. For Java 1.3 and higher, PSound2 is used. */ public class PSound { - PApplet applet; + // supposedly this is actually 8012.8210513 according to spec + public static final int SAMPLING_RATE = 8000; + + PApplet parent; + Method soundEventMethod; + + InputStream stream; + boolean loop; + float volume = 1; + + //int length; + int position; + int data[]; - public PSound() { } // for subclass + public PSound() { } // for 1.3 subclass - public PSound(PApplet applet, InputStream input) { - this.applet = applet; + public PSound(PApplet parent, InputStream input) { + this.parent = parent; /* try { @@ -52,10 +65,40 @@ public class PSound { error("", e); } */ + + parent.registerDispose(this); + + try { + soundEventMethod = + parent.getClass().getMethod("soundEvent", + new Class[] { PSound.class }); + + // if we're here, then it means that there's a method for it + /* + clip.addLineListener(new LineListener() { + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.STOP) { + try { + soundEventMethod.invoke(parent, + new Object[] { PSound.this }); + } catch (Exception e) { + System.err.println("error, disabling soundEvent()"); + e.printStackTrace(); + soundEventMethod = null; + } + } + } + }); + */ + + } catch (Exception e) { + // no such method, or an error.. which is fine, just ignore + } } public void play() { + AudioPlayer.player.start(stream); } @@ -63,6 +106,7 @@ public class PSound { * either sets repeat flag, or begins playing (and sets) */ public void loop() { + loop = true; } @@ -72,10 +116,12 @@ public class PSound { * continue to the end of the clip." */ public void noLoop() { + loop = false; } public void pause() { + AudioPlayer.player.stop(stream); } @@ -83,6 +129,8 @@ public class PSound { * Stops the audio and rewinds to the beginning. */ public void stop() { + AudioPlayer.player.stop(stream); + } @@ -90,7 +138,8 @@ public class PSound { * current position inside the clip (in seconds, just like video) */ public float time() { - return 0; + //return 0; + return (float)position / (float)SAMPLING_RATE; } @@ -103,6 +152,7 @@ public class PSound { public void volume(float v) { // ranges 0..1 + this.volume = v; } @@ -111,7 +161,154 @@ public class PSound { * I think of something slightly more intelligent to do. */ protected void error(String where, Exception e) { - applet.die("Error inside PSound." + where + "()", e); + parent.die("Error inside PSound." + where + "()", e); //e.printStackTrace(); } + + + // ------------------------------------------------------------ + + + class Stream extends InputStream { + int index; + + public int available() throws IOException { + return data.length - position; + } + + public void close() throws IOException { } + + public synchronized void mark() { } + + public boolean markSupported() { + return false; + } + + public int read() throws IOException { + return 0; + } + + public int read(byte b[]) throws IOException { + return 0; + } + + public int read(byte b[], int off, int len) throws IOException { + return 0; + } + + public synchronized void reset() { + position = 0; + } + + public long skip(long n) { + position = (position + (int)n) % data.length; + return n; + } + } + + + + // ------------------------------------------------------------ + + // Conversion + // ulaw from http://www-svr.eng.cam.ac.uk/comp.speech/Section2/Q2.7.html + + static final short BIAS = 0x84; + static final int CLIP = 32635; + + static final int[] LINEAR_LUT = { + 0, 132, 396, 924, 1980, 4092, 8316, 16764 + }; + + static final int[] LAW_LUT = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + }; + + + /** + * 16 bit linear to 8 bit ulaw + */ + static byte linear2ulaw(int linear) { + //int sign, exponent, mantissa; + //byte ulaw; + + if (linear > 32767) { + linear = 32767; + } else if(linear < -32768) { + linear = -32768; + } + + int sign = linear >> 8 & 0x80; + if (sign != 0) linear = -linear; + if (linear > CLIP) linear = CLIP; + + linear += BIAS; + int exponent = LAW_LUT[linear >> 7 & 0xFF]; + int mantissa = linear >> exponent + 3 & 0xf; + byte ulaw = (byte) ( ~(sign | exponent << 4 | mantissa)); + if (ulaw == 0) ulaw = 2; // CCITT trap + + return ulaw; + } + + /** + * 8 bit ulaw to 16 bit linear + */ + static int ulaw2linear(int ulaw) { + ulaw = ~ulaw; + + int sign = ulaw & 0x80; + int exponent = (ulaw >> 4) & 7; + int mantissa = ulaw & 0x0F; + int linear = LINEAR_LUT[exponent] + (mantissa << exponent + 3); + + return (short) ((sign != 0) ? -linear : linear); + } + + + /** + * cheap resampling with pitch distortion and aliasing + */ + static int[] resample(int original[], int oldfreq, int newfreq) { + int resampled[] = null; + float factor = 0; + int newlength; + + if (oldfreq > newfreq) { // downsample + factor = (float)oldfreq / (float)newfreq; + newlength = (int)(original.length / factor); + resampled = new int[newlength]; + + for (int i = 0; i < newlength; i++) { + resampled[i] = original[(int)(i * factor)]; + } + return resampled; + + } else if (oldfreq < newfreq) { // upsample (is it necesary??) + factor = (float)newfreq / (float)oldfreq; + newlength = (int) (original.length * factor); + resampled = new int[newlength]; + + for (int i = 0; i < newlength; i++) { + resampled[i] = original[(int) (i * factor)]; + } + return resampled; + } + return original; + } } diff --git a/core/PSound2.java b/core/PSound2.java index b3b5dc50b..ce3b6f173 100755 --- a/core/PSound2.java +++ b/core/PSound2.java @@ -33,7 +33,7 @@ import javax.sound.sampled.*; // for this class: http://javaalmanac.com/egs/javax.sound.sampled/pkg.html public class PSound2 extends PSound { - PApplet parent; + //PApplet parent; Method soundEventMethod; Clip clip; @@ -47,12 +47,11 @@ public class PSound2 extends PSound { AudioInputStream stream = AudioSystem.getAudioInputStream(input); - // *** this code appears as though it may just be faulty *** - // At present, ALAW and ULAW encodings must be converted // to PCM_SIGNED before it can be played AudioFormat format = stream.getFormat(); if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) { + // *** this code appears as though it may just be faulty *** format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits()*2, @@ -61,7 +60,7 @@ public class PSound2 extends PSound { format.getFrameRate(), true); // big endian stream = AudioSystem.getAudioInputStream(format, stream); - //} else { + //} else { //System.out.println("no conversion necessary"); } @@ -202,6 +201,7 @@ public class PSound2 extends PSound { gainControl.setValue(dB); } + /** mute */ /* public void noVolume() {