Skip to content
Browse files

Merge pull request #3 from jblezoray/master

The famous changes we talk some time ago.
  • Loading branch information...
2 parents f9596e0 + c12870b commit 7840a6d8d553c46e1e40677193fe623ed1d7dfe5 bencall committed Jun 30, 2011
Showing with 243 additions and 2,964 deletions.
  1. BIN Binaries/Rplay.jar
  2. BIN Binaries/Rplay_lib/jald.jar
  3. BIN Binaries/Rplay_lib/jmdns.jar
  4. BIN bin/com/beatofthedrum/alacdecoder/AlacContext.class
  5. BIN bin/com/beatofthedrum/alacdecoder/AlacDecodeUtils.class
  6. BIN bin/com/beatofthedrum/alacdecoder/AlacFile.class
  7. BIN bin/com/beatofthedrum/alacdecoder/AlacInputStream.class
  8. BIN bin/com/beatofthedrum/alacdecoder/AlacUtils.class
  9. BIN bin/com/beatofthedrum/alacdecoder/ChunkInfo.class
  10. BIN bin/com/beatofthedrum/alacdecoder/DecodeResult.class
  11. BIN bin/com/beatofthedrum/alacdecoder/Defines.class
  12. BIN bin/com/beatofthedrum/alacdecoder/DemuxResT.class
  13. BIN bin/com/beatofthedrum/alacdecoder/DemuxUtils.class
  14. BIN bin/com/beatofthedrum/alacdecoder/LeadingZeros.class
  15. BIN bin/com/beatofthedrum/alacdecoder/MyStream.class
  16. BIN bin/com/beatofthedrum/alacdecoder/QTMovieT.class
  17. BIN bin/com/beatofthedrum/alacdecoder/SampleDuration.class
  18. BIN bin/com/beatofthedrum/alacdecoder/SampleInfo.class
  19. BIN bin/com/beatofthedrum/alacdecoder/StreamUtils.class
  20. BIN bin/com/beatofthedrum/alacdecoder/WavWriter.class
  21. +59 −0 build.xml
  22. 0 {src/com/beatofthedrum/alacdecoder → libs/Java-Apple-Lossless-decoder}/License.txt
  23. BIN libs/Java-Apple-Lossless-decoder/jald.jar
  24. +58 −23 src/LaunchThread.java
  25. +19 −0 src/RPlayNoGui.java
  26. +5 −1 src/RTSPPacket.java
  27. +71 −113 src/RTSPResponder.java
  28. +31 −0 src/RTSPResponse.java
  29. +0 −24 src/com/beatofthedrum/alacdecoder/AlacContext.java
  30. +0 −1,020 src/com/beatofthedrum/alacdecoder/AlacDecodeUtils.java
  31. +0 −62 src/com/beatofthedrum/alacdecoder/AlacFile.java
  32. +0 −32 src/com/beatofthedrum/alacdecoder/AlacInputStream.java
  33. +0 −332 src/com/beatofthedrum/alacdecoder/AlacUtils.java
  34. +0 −11 src/com/beatofthedrum/alacdecoder/ChunkInfo.java
  35. +0 −10 src/com/beatofthedrum/alacdecoder/DecodeResult.java
  36. +0 −17 src/com/beatofthedrum/alacdecoder/Defines.java
  37. +0 −45 src/com/beatofthedrum/alacdecoder/DemuxResT.java
  38. +0 −890 src/com/beatofthedrum/alacdecoder/DemuxUtils.java
  39. +0 −18 src/com/beatofthedrum/alacdecoder/LeadingZeros.java
  40. +0 −19 src/com/beatofthedrum/alacdecoder/MyStream.java
  41. +0 −25 src/com/beatofthedrum/alacdecoder/QTMovieT.java
  42. +0 −18 src/com/beatofthedrum/alacdecoder/SampleDuration.java
  43. +0 −18 src/com/beatofthedrum/alacdecoder/SampleInfo.java
  44. +0 −168 src/com/beatofthedrum/alacdecoder/StreamUtils.java
  45. +0 −118 src/com/beatofthedrum/alacdecoder/WavWriter.java
View
BIN Binaries/Rplay.jar
Binary file not shown.
View
BIN Binaries/Rplay_lib/jald.jar
Binary file not shown.
View
BIN Binaries/Rplay_lib/jmdns.jar
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/AlacContext.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/AlacDecodeUtils.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/AlacFile.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/AlacInputStream.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/AlacUtils.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/ChunkInfo.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/DecodeResult.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/Defines.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/DemuxResT.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/DemuxUtils.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/LeadingZeros.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/MyStream.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/QTMovieT.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/SampleDuration.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/SampleInfo.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/StreamUtils.class
Binary file not shown.
View
BIN bin/com/beatofthedrum/alacdecoder/WavWriter.class
Binary file not shown.
View
59 build.xml
@@ -0,0 +1,59 @@
+<project name="ShairPort" default="dist" basedir=".">
+
+ <property name="src" location="src" />
+ <property name="lib" location="libs" />
+ <property name="bin" location="bin" />
+ <property name="dist" location="Binaries" />
+ <property name="dist_lib" location="${dist}/Rplay_lib" />
+ <property name="dist_jar" location="${dist}/Rplay.jar" />
+
+ <path id="lib_cp">
+ <fileset dir="${lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="build">
+ <mkdir dir="${bin}"/>
+ <javac srcdir="${src}" destdir="${bin}"
+ classpathref="lib_cp" includeantruntime="false"
+ verbose="true" debug="true"
+ source="1.6" target="1.6" />
+ </target>
+
+ <target name="dist" depends="build">
+ <mkdir dir="${dist}"/>
+ <manifestclasspath property="jar_cp" jarfile="${dist_jar}">
+ <classpath>
+ <fileset dir="${dist_lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ </classpath>
+ </manifestclasspath>
+ <jar destfile="${dist_jar}">
+ <fileset dir="${bin}"/>
+ <manifest>
+ <attribute name="Main-Class" value="Rplay"/>
+ <attribute name="Class-Path" value="${jar_cp}" />
+ </manifest>
+ </jar>
+
+ <mkdir dir="${dist_lib}"/>
+ <copy todir="${dist_lib}" flatten="true">
+ <path refid="lib_cp" />
+ </copy>
+ </target>
+
+ <target name="run">
+ <!--
+ <java fork="true" classname="RPlayNoGui">
+ <classpath path="${dist_jar}"/>
+ <arg value="myAirport"/>
+ </java>
+ -->
+ <java fork="true" classname="Rplay">
+ <classpath path="${dist_jar}"/>
+ </java>
+ </target>
+
+</project>
View
0 ...com/beatofthedrum/alacdecoder/License.txt → libs/Java-Apple-Lossless-decoder/License.txt
File renamed without changes.
View
BIN libs/Java-Apple-Lossless-decoder/jald.jar
Binary file not shown.
View
81 src/LaunchThread.java
@@ -1,19 +1,24 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.Socket;
import java.net.SocketException;
+import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
+import com.apple.dnssd.DNSSDException;
+
/**
* LaunchThread class which starts services
* @author bencall
*
*/
public class LaunchThread extends Thread{
- private RTSPResponder repondeur;
- private BonjourEmitter emetteur;
+ private BonjourEmitter emitter;
private String name;
+ private boolean stopThread = false;
/**
* Constructor
@@ -24,9 +29,7 @@ public LaunchThread(String name){
this.name = name;
}
-
- public void run(){
- int port = 5000;
+ private byte[] getHardwareAdress() {
byte[] hwAddr = null;
InetAddress local;
@@ -36,38 +39,70 @@ public void run(){
if (ni != null) {
hwAddr = ni.getHardwareAddress();
}
- } catch (UnknownHostException e1) {
- e1.printStackTrace();
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
}
-
+ return hwAddr;
+ }
+
+
+ private String getStringHardwareAdress(byte[] hwAddr) {
StringBuilder sb = new StringBuilder();
for (byte b : hwAddr) {
sb.append(String.format("%02x", b));
}
+ return sb.toString();
+ }
+
+
+ public void run(){
+ System.out.println("service started.");
+ int port = 5000;
+ ServerSocket servSock = null;
try {
- // DNS Emitter (Bonjour)
- repondeur = new RTSPResponder(port, hwAddr);
- emetteur = new BonjourEmitter(name, sb.toString(), repondeur.getPort());
-
- repondeur.start();
+ // We listen for new connections
+ try {
+ servSock = new ServerSocket(port);
+ } catch (IOException e) {
+ servSock = new ServerSocket();
+ }
+ // DNS Emitter (Bonjour)
+ byte[] hwAddr = getHardwareAdress();
+ emitter = new BonjourEmitter(name, getStringHardwareAdress(hwAddr), port);
+
+ servSock.setSoTimeout(1000);
+ while (!stopThread) {
+ try {
+ Socket socket = servSock.accept();
+ System.out.println("got connection from " + socket.toString());
+ new RTSPResponder(hwAddr, socket).start();
+ } catch(SocketTimeoutException e) {
+ // ignore
+ }
+ }
- } catch (Exception e) {
- // Bonjour error
- e.printStackTrace();
+ } catch (DNSSDException e) {
+ throw new RuntimeException(e);
+
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+
+ } finally {
+ try {
+ servSock.close(); // will stop all RTSPResponders.
+ emitter.stop();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
+ System.out.println("service stopped");
}
public synchronized void stopThread(){
- emetteur.stop();
-
- try {
- repondeur.stopThread();
- } catch (IOException e) {
- e.printStackTrace();
- }
+ stopThread = true;
}
}
View
19 src/RPlayNoGui.java
@@ -0,0 +1,19 @@
+
+public class RPlayNoGui {
+
+
+ private static final void usage() {
+ System.err.println("Java port of shairport.");
+ System.err.println("usage : ");
+ System.err.println(" java "+RPlayNoGui.class.getCanonicalName()+" <AP_name>");
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 1 && args[1].length()>1) {
+ usage();
+ System.exit(-1);
+ }
+ new LaunchThread(args[0]).start();
+ }
+
+}
View
6 src/RTSPPacket.java
@@ -73,7 +73,6 @@ public String getDirectory(){
}
public int getCode(){
-
return 200;
}
@@ -84,4 +83,9 @@ public String valueOfHeader(String headerName){
}
return headerContent.elementAt(i);
}
+
+ @Override
+ public String toString() {
+ return " < " + rawPacket.replaceAll("\r\n", "\r\n < ");
+ }
}
View
184 src/RTSPResponder.java
@@ -5,14 +5,13 @@
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
-import java.net.ServerSocket;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.security.KeyPair;
import java.security.Security;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.net.InetSocketAddress;
import javax.crypto.Cipher;
@@ -28,15 +27,15 @@
*/
public class RTSPResponder extends Thread{
- private ServerSocket sock; // Initial socket
private Socket socket; // Connected socket
private int[] fmtp;
private byte[] aesiv, aeskey; // ANNOUNCE request infos
private AudioServer serv; // Audio listener
byte[] hwAddr;
- boolean stopThread = false;
+ private BufferedReader in;
+ private static final Pattern completedPacket = Pattern.compile("(.*)\r\n\r\n");
- private String key =
+ private static final String key =
"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"
+"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"
@@ -61,45 +60,18 @@
+"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKaXTyY=\n"
+"-----END RSA PRIVATE KEY-----\n";
- /**
- *
- * @param port Will try to use the defined port. If not possible, check getPort() to know which port was taken.
- * @throws IOException
- */
- public RTSPResponder(int port, byte[] hwAddr) throws IOException{
+ public RTSPResponder(byte[] hwAddr, Socket socket) throws IOException {
this.hwAddr = hwAddr;
-
- // Try to take the given port. If not possible, take another. If not possible: ERROR
- try {
- sock = new ServerSocket(5000);
- } catch (IOException e) {
- sock = new ServerSocket();
- }
- }
-
-
- public void stopThread() throws IOException{
- stopThread = true;
- if(serv != null){
- serv.stop();
- }
- sock.close();
- }
-
-
- /**
- * @return port number
- */
- public int getPort(){
- return sock.getLocalPort();
+ this.socket = socket;
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
- public void handlePacket(RTSPPacket packet){
+ public RTSPResponse handlePacket(RTSPPacket packet){
// We init the response holder
- StringBuilder response = new StringBuilder("RTSP/1.0 200 OK\r\n");
- response.append("Audio-Jack-Status: connected; type=analog\r\n");
- response.append("CSeq: "+ packet.valueOfHeader("CSeq")+"\r\n");
+ RTSPResponse response = new RTSPResponse("RTSP/1.0 200 OK");
+ response.append("Audio-Jack-Status", "connected; type=analog");
+ response.append("CSeq", packet.valueOfHeader("CSeq"));
// Apple Challenge-Response field if needed
String challenge;
@@ -143,21 +115,16 @@ public void handlePacket(RTSPPacket packet){
ret = ret.replace("=", "").replace("\r", "").replace("\n", "");
// Write
- response.append("Apple-Response: " + ret + "\r\n");
-
+ response.append("Apple-Response", ret);
}
// Paquet request
String REQ = packet.getReq();
- if(REQ.contentEquals("OPTIONS")){ // OPTIONS
- System.out.println("REQUEST: OPTIONS");
-
+ if(REQ.contentEquals("OPTIONS")){
// The response field
- response.append("Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER\r\n");
+ response.append("Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
- } else if (REQ.contentEquals("ANNOUNCE")){ // ANNOUNCE
- System.out.println("REQUEST: ANNOUNCE");
-
+ } else if (REQ.contentEquals("ANNOUNCE")){
// Nothing to do here. Juste get the keys and values
Pattern p = Pattern.compile("^a=([^:]+):(.+)", Pattern.MULTILINE);
Matcher m = p.matcher(packet.getContent());
@@ -178,7 +145,6 @@ public void handlePacket(RTSPPacket packet){
}
} else if (REQ.contentEquals("SETUP")){
- System.out.println("REQUEST: SETUP");
int controlPort = 0;
int timingPort = 0;
@@ -201,10 +167,10 @@ public void handlePacket(RTSPPacket packet){
// Launching audioserver
serv = new AudioServer(new AudioSession(aesiv, aeskey, fmtp, controlPort, timingPort));
- response.append("Transport: " + packet.valueOfHeader("Transport") + ";server_port=" + serv.getServerPort() + "\r\n");
+ response.append("Transport", packet.valueOfHeader("Transport") + ";server_port=" + serv.getServerPort());
// ??? Why ???
- response.append("Session: DEADBEEF\r\n");
+ response.append("Session", "DEADBEEF");
} else if (REQ.contentEquals("RECORD")){
// Headers
// Range: ntp=0-
@@ -216,7 +182,7 @@ public void handlePacket(RTSPPacket packet){
serv.flush();
} else if (REQ.contentEquals("TEARDOWN")){
- response.append("Connection: close\r\n");
+ response.append("Connection", "close");
} else if (REQ.contentEquals("SET_PARAMETER")){
// Timing port
@@ -233,30 +199,8 @@ public void handlePacket(RTSPPacket packet){
}
// We close the response
- response.append("\r\n");
-
- System.out.println(packet.getRawPacket());
- System.out.println(response.toString());
-
- // Write the packet to the wire
- BufferedWriter oStream;
- try {
- oStream = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
- oStream.write(response.toString());
- oStream.flush();
-
- // Don't know why, but connection must be closed after the OPTIONS request
- if(REQ.contentEquals("OPTIONS") || REQ.contentEquals("TEARDOWN") ){
- socket.close();
- socket = null;
- }
-
- // We listen for a new connection or new data
- this.run();
-
- } catch (IOException e) {
- e.printStackTrace();
- }
+ response.finalize();
+ return response;
}
/**
@@ -292,7 +236,7 @@ public void handlePacket(RTSPPacket packet){
try{
Security.addProvider(new BouncyCastleProvider());
- // La cl� RSA
+ // La clef RSA
PEMReader pemReader = new PEMReader(new StringReader(key));
KeyPair pObj = (KeyPair) pemReader.readObject();
@@ -312,47 +256,61 @@ public void handlePacket(RTSPPacket packet){
* Thread to listen packets
*/
public void run() {
- boolean fin = stopThread;
try {
-
- // Socket & Streams
- // Socket init
- if(socket == null && !stopThread){
- socket = sock.accept();
- }
-
- if(socket != null){
- BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-
- String packet = "";
- while(!fin){
- // Buffer
+ do {
+ System.out.println("listening packets ... ");
+ // feed buffer until packet completed
+ StringBuffer packet = new StringBuffer();
+ int ret = 0;
+ do {
char[] buffer = new char[4096];
- in.read(buffer);
- String temp = new String(buffer);
- packet = packet + temp;
-
- // If packet completed
- Pattern p = Pattern.compile("(.*)\r\n\r\n");
- Matcher m = p.matcher(packet);
- if(m.find()){
- //System.out.println(packet);
- // We handle the packet
- RTSPPacket paquet = new RTSPPacket(packet);
- this.handlePacket(paquet);
- packet = "";
- break;
- }
-
- synchronized(this){
- fin = stopThread;
+ ret = in.read(buffer);
+ packet.append(new String(buffer));
+ } while (ret!=-1 && !completedPacket.matcher(packet.toString()).find());
+
+ if (ret!=-1) {
+ // We handle the packet
+ RTSPPacket request = new RTSPPacket(packet.toString());
+ RTSPResponse response = this.handlePacket(request);
+ System.out.println(request.toString());
+ System.out.println(response.toString());
+
+ // Write the response to the wire
+ try {
+ BufferedWriter oStream = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ oStream.write(response.getRawPacket());
+ oStream.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
}
+
+ if("TEARDOWN".equals(request.getReq())){
+ socket.close();
+ socket = null;
+ }
+ } else {
+ socket.close();
+ socket = null;
}
- in.close();
- socket.close();
- }
+ } while (socket!=null);
+
} catch (IOException e) {
e.printStackTrace();
- }
+
+ } finally {
+ try {
+ if (in!=null) in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (socket!=null) socket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ System.out.println("connection ended.");
}
+
}
View
31 src/RTSPResponse.java
@@ -0,0 +1,31 @@
+
+public class RTSPResponse {
+
+ private StringBuilder response = new StringBuilder();
+
+ public RTSPResponse(String header) {
+ response.append(header + "\r\n");
+ }
+
+ public void append(String key, String value) {
+ response.append(key + ": " + value + "\r\n");
+ }
+
+ /**
+ * close the response
+ */
+ public void finalize() {
+ response.append("\r\n");
+ }
+
+
+ public String getRawPacket() {
+ return response.toString();
+ }
+
+ @Override
+ public String toString() {
+ return " > " + response.toString().replaceAll("\r\n", "\r\n > ");
+ }
+
+}
View
24 src/com/beatofthedrum/alacdecoder/AlacContext.java
@@ -1,24 +0,0 @@
-/*
-** AlacContext.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-
-package com.beatofthedrum.alacdecoder;
-
-public class AlacContext
-{
- DemuxResT demux_res = new DemuxResT();
- AlacFile alac = new AlacFile();
- AlacInputStream input_stream;
- int current_sample_block = 0;
- int offset;
- public boolean error;
- public String error_message = "";
- byte[] read_buffer = new byte[1024 *80]; // sample big enough to hold any input for a single alac frame
-}
View
1,020 src/com/beatofthedrum/alacdecoder/AlacDecodeUtils.java
@@ -1,1020 +0,0 @@
-/*
-** AlacDecodeUtils.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-package com.beatofthedrum.alacdecoder;
-
-public class AlacDecodeUtils
-{
-
- public static void alac_set_info(AlacFile alac, int[] inputbuffer)
- {
- int ptrIndex = 0;
- ptrIndex += 4; // size
- ptrIndex += 4; // frma
- ptrIndex += 4; // alac
- ptrIndex += 4; // size
- ptrIndex += 4; // alac
-
- ptrIndex += 4; // 0 ?
-
- alac.setinfo_max_samples_per_frame = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]); // buffer size / 2 ?
- ptrIndex += 4;
- alac.setinfo_7a = inputbuffer[ptrIndex];
- ptrIndex += 1;
- alac.setinfo_sample_size = inputbuffer[ptrIndex];
- ptrIndex += 1;
- alac.setinfo_rice_historymult = (inputbuffer[ptrIndex] & 0xff);
- ptrIndex += 1;
- alac.setinfo_rice_initialhistory = (inputbuffer[ptrIndex] & 0xff);
- ptrIndex += 1;
- alac.setinfo_rice_kmodifier = (inputbuffer[ptrIndex] & 0xff);
- ptrIndex += 1;
- alac.setinfo_7f = inputbuffer[ptrIndex];
- ptrIndex += 1;
- alac.setinfo_80 = (inputbuffer[ptrIndex] << 8) + inputbuffer[ptrIndex+1];
- ptrIndex += 2;
- alac.setinfo_82 = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]);
- ptrIndex += 4;
- alac.setinfo_86 = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]);
- ptrIndex += 4;
- alac.setinfo_8a_rate = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]);
- ptrIndex += 4;
-
- }
-
- /* stream reading */
-
- /* supports reading 1 to 16 bits, in big endian format */
- static int readbits_16(AlacFile alac, int bits )
- {
- int result = 0;
- int new_accumulator = 0;
- int part1 = 0;
- int part2 = 0;
- int part3 =0;
-
- part1 = (alac.input_buffer[alac.ibIdx] & 0xff);
- part2 = (alac.input_buffer[alac.ibIdx + 1] & 0xff);
- part3 = (alac.input_buffer[alac.ibIdx + 2] & 0xff);
-
- result = ((part1 << 16) | (part2 << 8) | part3);
-
- /* shift left by the number of bits we've already read,
- * so that the top 'n' bits of the 24 bits we read will
- * be the return bits */
- result = result << alac.input_buffer_bitaccumulator;
-
- result = result & 0x00ffffff;
-
- /* and then only want the top 'n' bits from that, where
- * n is 'bits' */
- result = result >> (24 - bits);
-
- new_accumulator = (alac.input_buffer_bitaccumulator + bits);
-
- /* increase the buffer pointer if we've read over n bytes. */
- alac.ibIdx += (new_accumulator >> 3);
-
- /* and the remainder goes back into the bit accumulator */
- alac.input_buffer_bitaccumulator = (new_accumulator & 7);
-
- return result;
- }
-
- /* supports reading 1 to 32 bits, in big endian format */
- static int readbits(AlacFile alac, int bits )
- {
- int result = 0;
-
- if (bits > 16)
- {
- bits -= 16;
-
- result = readbits_16(alac, 16) << bits;
- }
-
- result |= readbits_16(alac, bits);
-
- return result;
- }
-
- /* reads a single bit */
- static int readbit(AlacFile alac)
- {
- int result = 0;
- int new_accumulator = 0;
- int part1 = 0;
-
- part1 = (alac.input_buffer[alac.ibIdx] & 0xff);
-
- result = part1;
-
- result = result << alac.input_buffer_bitaccumulator;
-
- result = result >> 7 & 1;
-
- new_accumulator = (alac.input_buffer_bitaccumulator + 1);
-
- alac.ibIdx += new_accumulator / 8;
-
- alac.input_buffer_bitaccumulator = (new_accumulator % 8);
-
- return result;
- }
-
- static void unreadbits(AlacFile alac, int bits )
- {
- int new_accumulator = (alac.input_buffer_bitaccumulator - bits);
-
- alac.ibIdx += (new_accumulator >> 3);
-
- alac.input_buffer_bitaccumulator = (new_accumulator & 7);
- if (alac.input_buffer_bitaccumulator < 0)
- alac.input_buffer_bitaccumulator *= -1;
- }
-
- static LeadingZeros count_leading_zeros_extra(int curbyte, int output, LeadingZeros lz)
- {
-
- if ((curbyte & 0xf0)==0)
- {
- output += 4;
- }
- else
- curbyte = curbyte >> 4;
-
- if ((curbyte & 0x8) != 0)
- {
- lz.output = output;
- lz.curbyte = curbyte;
- return lz;
- }
- if ((curbyte & 0x4) != 0)
- {
- lz.output = output + 1;
- lz.curbyte = curbyte;
- return lz;
- }
- if ((curbyte & 0x2) != 0)
- {
- lz.output = output + 2;
- lz.curbyte = curbyte;
- return lz;
- }
- if ((curbyte & 0x1) != 0)
- {
- lz.output = output + 3;
- lz.curbyte = curbyte;
- return lz;
- }
-
- /* shouldn't get here: */
-
- lz.output = output + 4;
- lz.curbyte = curbyte;
- return lz;
-
- }
- static int count_leading_zeros(int input, LeadingZeros lz)
- {
- int output = 0;
- int curbyte = 0;
-
- curbyte = input >> 24;
- if (curbyte != 0)
- {
- count_leading_zeros_extra(curbyte, output, lz);
- output = lz.output;
- curbyte = lz.curbyte;
- return output;
- }
- output += 8;
-
- curbyte = input >> 16;
- if ((curbyte & 0xFF) != 0)
- {
- count_leading_zeros_extra(curbyte, output, lz);
- output = lz.output;
- curbyte = lz.curbyte;
-
- return output;
- }
- output += 8;
-
- curbyte = input >> 8;
- if ((curbyte & 0xFF) != 0)
- {
- count_leading_zeros_extra(curbyte, output, lz);
- output = lz.output;
- curbyte = lz.curbyte;
-
- return output;
- }
- output += 8;
-
- curbyte = input;
- if ((curbyte & 0xFF) != 0)
- {
- count_leading_zeros_extra(curbyte, output, lz);
- output = lz.output;
- curbyte = lz.curbyte;
-
- return output;
- }
- output += 8;
-
- return output;
- }
-
- public static int entropy_decode_value(AlacFile alac, int readSampleSize , int k , int rice_kmodifier_mask )
- {
- int x = 0; // decoded value
-
- // read x, number of 1s before 0 represent the rice value.
- while (x <= Defines.RICE_THRESHOLD && readbit(alac) != 0)
- {
- x++;
- }
-
- if (x > Defines.RICE_THRESHOLD)
- {
- // read the number from the bit stream (raw value)
- int value = 0;
-
- value = readbits(alac, readSampleSize);
-
- // mask value
- value &= ((0xffffffff) >> (32 - readSampleSize));
-
- x = value;
- }
- else
- {
- if (k != 1)
- {
- int extraBits = readbits(alac, k);
-
- x *= (((1 << k) - 1) & rice_kmodifier_mask);
-
- if (extraBits > 1)
- x += extraBits - 1;
- else
- unreadbits(alac, 1);
- }
- }
-
- return x;
- }
-
- public static void entropy_rice_decode(AlacFile alac, int[] outputBuffer, int outputSize , int readSampleSize , int rice_initialhistory , int rice_kmodifier , int rice_historymult , int rice_kmodifier_mask )
- {
- int history = rice_initialhistory;
- int outputCount = 0;
- int signModifier = 0;
-
- while(outputCount < outputSize)
- {
- int decodedValue = 0;
- int finalValue = 0;
- int k = 0;
-
- k = 31 - rice_kmodifier - count_leading_zeros((history >> 9) + 3, alac.lz);
-
- if (k < 0)
- k += rice_kmodifier;
- else
- k = rice_kmodifier;
-
- // note: don't use rice_kmodifier_mask here (set mask to 0xFFFFFFFF)
- decodedValue = entropy_decode_value(alac, readSampleSize, k, 0xFFFFFFFF);
-
- decodedValue += signModifier;
- finalValue = ((decodedValue + 1) / 2); // inc by 1 and shift out sign bit
- if ((decodedValue & 1) != 0) // the sign is stored in the low bit
- finalValue *= -1;
-
- outputBuffer[outputCount] = finalValue;
-
- signModifier = 0;
-
- // update history
- history += (decodedValue * rice_historymult) - ((history * rice_historymult) >> 9);
-
- if (decodedValue > 0xFFFF)
- history = 0xFFFF;
-
- // special case, for compressed blocks of 0
- if ((history < 128) && (outputCount + 1 < outputSize))
- {
- int blockSize = 0;
-
- signModifier = 1;
-
- k = count_leading_zeros(history, alac.lz) + ((history + 16) / 64) - 24;
-
- // note: blockSize is always 16bit
- blockSize = entropy_decode_value(alac, 16, k, rice_kmodifier_mask);
-
- // got blockSize 0s
- if (blockSize > 0)
- {
- int countSize = 0;
- countSize = blockSize;
- for (int j = 0; j < countSize; j++)
- {
- outputBuffer[outputCount + 1 + j] = 0;
- }
- outputCount += blockSize;
- }
-
- if (blockSize > 0xFFFF)
- signModifier = 0;
-
- history = 0;
- }
-
- outputCount++;
- }
- }
-
- static int[] predictor_decompress_fir_adapt(int[] error_buffer, int output_size , int readsamplesize , int[] predictor_coef_table, int predictor_coef_num , int predictor_quantitization )
- {
- int buffer_out_idx = 0;
- int[] buffer_out;
- int bitsmove = 0;
-
- /* first sample always copies */
- buffer_out = error_buffer;
-
- if (predictor_coef_num == 0)
- {
- if (output_size <= 1)
- return(buffer_out);
- int sizeToCopy = 0;
- sizeToCopy = (output_size-1) * 4;
- System.arraycopy(error_buffer, 1, buffer_out, 1, sizeToCopy);
- return(buffer_out);
- }
-
- if (predictor_coef_num == 0x1f) // 11111 - max value of predictor_coef_num
- {
- /* second-best case scenario for fir decompression,
- * error describes a small difference from the previous sample only
- */
- if (output_size <= 1)
- return(buffer_out);
-
- for (int i = 0; i < (output_size - 1); i++)
- {
- int prev_value = 0;
- int error_value = 0;
-
- prev_value = buffer_out[i];
- error_value = error_buffer[i+1];
-
- bitsmove = 32 - readsamplesize;
- buffer_out[i+1] = (((prev_value + error_value) << bitsmove) >> bitsmove);
- }
- return(buffer_out);
- }
-
- /* read warm-up samples */
- if (predictor_coef_num > 0)
- {
- for (int i = 0; i < predictor_coef_num; i++)
- {
- int val = 0;
-
- val = buffer_out[i] + error_buffer[i+1];
-
- bitsmove = 32 - readsamplesize;
-
- val = ((val << bitsmove) >> bitsmove);
-
- buffer_out[i+1] = val;
- }
- }
-
- /* general case */
- if (predictor_coef_num > 0)
- {
- buffer_out_idx = 0;
- for (int i = predictor_coef_num + 1; i < output_size; i++)
- {
- int j ;
- int sum = 0;
- int outval ;
- int error_val = error_buffer[i];
-
- for (j = 0; j < predictor_coef_num; j++)
- {
- sum += (buffer_out[buffer_out_idx + predictor_coef_num-j] - buffer_out[buffer_out_idx]) * predictor_coef_table[j];
- }
-
- outval = (1 << (predictor_quantitization-1)) + sum;
- outval = outval >> predictor_quantitization;
- outval = outval + buffer_out[buffer_out_idx] + error_val;
- bitsmove = 32 - readsamplesize;
-
- outval = ((outval << bitsmove) >> bitsmove);
-
- buffer_out[buffer_out_idx+predictor_coef_num+1] = outval;
-
- if (error_val > 0)
- {
- int predictor_num = predictor_coef_num - 1;
-
- while (predictor_num >= 0 && error_val > 0)
- {
- int val = buffer_out[buffer_out_idx] - buffer_out[buffer_out_idx + predictor_coef_num - predictor_num];
- int sign = ((val < 0) ? (-1) : ((val > 0) ? (1) : (0)));
-
- predictor_coef_table[predictor_num] -= sign;
-
- val *= sign; // absolute value
-
- error_val -= ((val >> predictor_quantitization) * (predictor_coef_num - predictor_num));
-
- predictor_num--;
- }
- }
- else if (error_val < 0)
- {
- int predictor_num = predictor_coef_num - 1;
-
- while (predictor_num >= 0 && error_val < 0)
- {
- int val = buffer_out[buffer_out_idx] - buffer_out[buffer_out_idx + predictor_coef_num - predictor_num];
- int sign = - ((val < 0) ? (-1) : ((val > 0) ? (1) : (0)));
-
- predictor_coef_table[predictor_num] -= sign;
-
- val *= sign; // neg value
-
- error_val -= ((val >> predictor_quantitization) * (predictor_coef_num - predictor_num));
-
- predictor_num--;
- }
- }
-
- buffer_out_idx++;
- }
- }
- return(buffer_out);
- }
-
-
- public static void deinterlace_16(int[] buffer_a, int[] buffer_b, int[] buffer_out, int numchannels , int numsamples , int interlacing_shift , int interlacing_leftweight )
- {
-
- if (numsamples <= 0)
- return;
-
- /* weighted interlacing */
- if (0 != interlacing_leftweight)
- {
- for (int i = 0; i < numsamples; i++)
- {
- int difference = 0;
- int midright = 0;
- int left = 0;
- int right = 0;
-
- midright = buffer_a[i];
- difference = buffer_b[i];
-
- right = (midright - ((difference * interlacing_leftweight) >> interlacing_shift));
- left = (right + difference);
-
- /* output is always little endian */
-
- buffer_out[i *numchannels] = left;
- buffer_out[i *numchannels + 1] = right;
- }
-
- return;
- }
-
- /* otherwise basic interlacing took place */
- for (int i = 0; i < numsamples; i++)
- {
- int left = 0;
- int right = 0;
-
- left = buffer_a[i];
- right = buffer_b[i];
-
- /* output is always little endian */
-
- buffer_out[i *numchannels] = left;
- buffer_out[i *numchannels + 1] = right;
- }
- }
-
-
- public static void deinterlace_24(int[] buffer_a, int[] buffer_b, int uncompressed_bytes , int[] uncompressed_bytes_buffer_a, int[] uncompressed_bytes_buffer_b, int[] buffer_out, int numchannels , int numsamples , int interlacing_shift , int interlacing_leftweight )
- {
- if (numsamples <= 0)
- return;
-
- /* weighted interlacing */
- if (interlacing_leftweight != 0)
- {
- for (int i = 0; i < numsamples; i++)
- {
- int difference = 0;
- int midright = 0;
- int left = 0;
- int right = 0;
-
- midright = buffer_a[i];
- difference = buffer_b[i];
-
- right = midright - ((difference * interlacing_leftweight) >> interlacing_shift);
- left = right + difference;
-
- if (uncompressed_bytes != 0)
- {
- int mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8));
- left <<= (uncompressed_bytes * 8);
- right <<= (uncompressed_bytes * 8);
-
- left = left | (uncompressed_bytes_buffer_a[i] & mask);
- right = right | (uncompressed_bytes_buffer_b[i] & mask);
- }
-
- buffer_out[i * numchannels * 3] = (left & 0xFF);
- buffer_out[i * numchannels * 3 + 1] = ((left >> 8) & 0xFF);
- buffer_out[i * numchannels * 3 + 2] = ((left >> 16) & 0xFF);
-
- buffer_out[i * numchannels * 3 + 3] = (right & 0xFF);
- buffer_out[i * numchannels * 3 + 4] = ((right >> 8) & 0xFF);
- buffer_out[i * numchannels * 3 + 5] = ((right >> 16) & 0xFF);
- }
-
- return;
- }
-
- /* otherwise basic interlacing took place */
- for (int i = 0; i < numsamples; i++)
- {
- int left = 0;
- int right = 0;
-
- left = buffer_a[i];
- right = buffer_b[i];
-
- if (uncompressed_bytes != 0)
- {
- int mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8));
- left <<= (uncompressed_bytes * 8);
- right <<= (uncompressed_bytes * 8);
-
- left = left | (uncompressed_bytes_buffer_a[i] & mask);
- right = right | (uncompressed_bytes_buffer_b[i] & mask);
- }
-
- buffer_out[i * numchannels * 3] = (left & 0xFF);
- buffer_out[i * numchannels * 3 + 1] = ((left >> 8) & 0xFF);
- buffer_out[i * numchannels * 3 + 2] = ((left >> 16) & 0xFF);
-
- buffer_out[i * numchannels * 3 + 3] = (right & 0xFF);
- buffer_out[i * numchannels * 3 + 4] = ((right >> 8) & 0xFF);
- buffer_out[i * numchannels * 3 + 5] = ((right >> 16) & 0xFF);
-
- }
-
- }
-
-
- public static int decode_frame(AlacFile alac, byte[] inbuffer, int[] outbuffer, int outputsize )
- {
- int channels ;
- int outputsamples = alac.setinfo_max_samples_per_frame;
-
- /* setup the stream */
- alac.input_buffer = inbuffer;
- alac.input_buffer_bitaccumulator = 0;
- alac.ibIdx = 0;
-
-
- channels = readbits(alac, 3);
-
- outputsize = outputsamples * alac.bytespersample;
-
- if(channels == 0) // 1 channel
- {
- int hassize ;
- int isnotcompressed ;
- int readsamplesize ;
-
- int uncompressed_bytes ;
- int ricemodifier ;
-
- int tempPred = 0;
-
- /* 2^result = something to do with output waiting.
- * perhaps matters if we read > 1 frame in a pass?
- */
- readbits(alac, 4);
-
- readbits(alac, 12); // unknown, skip 12 bits
-
- hassize = readbits(alac, 1); // the output sample size is stored soon
-
- uncompressed_bytes = readbits(alac, 2); // number of bytes in the (compressed) stream that are not compressed
-
- isnotcompressed = readbits(alac, 1); // whether the frame is compressed
-
- if (hassize != 0)
- {
- /* now read the number of samples,
- * as a 32bit integer */
- outputsamples = readbits(alac, 32);
- outputsize = outputsamples * alac.bytespersample;
- }
-
- readsamplesize = alac.setinfo_sample_size - (uncompressed_bytes * 8);
-
- if (isnotcompressed == 0)
- { // so it is compressed
- int[] predictor_coef_table = alac.predictor_coef_table;
- int predictor_coef_num ;
- int prediction_type ;
- int prediction_quantitization ;
- int i ;
-
- /* skip 16 bits, not sure what they are. seem to be used in
- * two channel case */
- readbits(alac, 8);
- readbits(alac, 8);
-
- prediction_type = readbits(alac, 4);
- prediction_quantitization = readbits(alac, 4);
-
- ricemodifier = readbits(alac, 3);
- predictor_coef_num = readbits(alac, 5);
-
- /* read the predictor table */
-
- for (i = 0; i < predictor_coef_num; i++)
- {
- tempPred = readbits(alac,16);
- if(tempPred > 32767)
- {
- // the predictor coef table values are only 16 bit signed
- tempPred = tempPred - 65536;
- }
-
- predictor_coef_table[i] = tempPred;
- }
-
- if (uncompressed_bytes != 0)
- {
- for (i = 0; i < outputsamples; i++)
- {
- alac.uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8);
- }
- }
-
-
- entropy_rice_decode(alac, alac.predicterror_buffer_a, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1);
-
- if (prediction_type == 0)
- { // adaptive fir
- alac.outputsamples_buffer_a = predictor_decompress_fir_adapt(alac.predicterror_buffer_a, outputsamples, readsamplesize, predictor_coef_table, predictor_coef_num, prediction_quantitization);
- }
- else
- {
- System.err.println("FIXME: unhandled predicition type: " +prediction_type);
-
- /* i think the only other prediction type (or perhaps this is just a
- * boolean?) runs adaptive fir twice.. like:
- * predictor_decompress_fir_adapt(predictor_error, tempout, ...)
- * predictor_decompress_fir_adapt(predictor_error, outputsamples ...)
- * little strange..
- */
- }
-
- }
- else
- { // not compressed, easy case
- if (alac.setinfo_sample_size <= 16)
- {
- int bitsmove = 0;
- for (int i = 0; i < outputsamples; i++)
- {
- int audiobits = readbits(alac, alac.setinfo_sample_size);
- bitsmove = 32 - alac.setinfo_sample_size;
-
- audiobits = ((audiobits << bitsmove) >> bitsmove);
-
- alac.outputsamples_buffer_a[i] = audiobits;
- }
- }
- else
- {
- int x ;
- int m = 1 << (24 -1);
- for (int i = 0; i < outputsamples; i++)
- {
- int audiobits ;
-
- audiobits = readbits(alac, 16);
- /* special case of sign extension..
- * as we'll be ORing the low 16bits into this */
- audiobits = audiobits << (alac.setinfo_sample_size - 16);
- audiobits = audiobits | readbits(alac, alac.setinfo_sample_size - 16);
- x = audiobits & ((1 << 24) - 1);
- audiobits = (x ^ m) - m; // sign extend 24 bits
-
- alac.outputsamples_buffer_a[i] = audiobits;
- }
- }
- uncompressed_bytes = 0; // always 0 for uncompressed
- }
-
- switch(alac.setinfo_sample_size)
- {
- case 16:
- {
-
- for (int i = 0; i < outputsamples; i++)
- {
- int sample = alac.outputsamples_buffer_a[i];
- outbuffer[i * alac.numchannels] = sample;
-
- /*
- ** We have to handle the case where the data is actually mono, but the stsd atom says it has 2 channels
- ** in this case we create a stereo file where one of the channels is silent. If mono and 1 channel this value
- ** will be overwritten in the next iteration
- */
-
- outbuffer[(i * alac.numchannels) + 1] = 0;
- }
- break;
- }
- case 24:
- {
- for (int i = 0; i < outputsamples; i++)
- {
- int sample = alac.outputsamples_buffer_a[i];
-
- if (uncompressed_bytes != 0)
- {
- int mask = 0;
- sample = sample << (uncompressed_bytes * 8);
- mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8));
- sample = sample | (alac.uncompressed_bytes_buffer_a[i] & mask);
- }
-
- outbuffer[i * alac.numchannels * 3] = ((sample) & 0xFF);
- outbuffer[i * alac.numchannels * 3 + 1] = ((sample >> 8) & 0xFF);
- outbuffer[i * alac.numchannels * 3 + 2] = ((sample >> 16) & 0xFF);
-
- /*
- ** We have to handle the case where the data is actually mono, but the stsd atom says it has 2 channels
- ** in this case we create a stereo file where one of the channels is silent. If mono and 1 channel this value
- ** will be overwritten in the next iteration
- */
-
- outbuffer[i * alac.numchannels * 3 + 3] = 0;
- outbuffer[i * alac.numchannels * 3 + 4] = 0;
- outbuffer[i * alac.numchannels * 3 + 5] = 0;
-
- }
- break;
- }
- case 20:
- case 32:
- System.err.println("FIXME: unimplemented sample size " + alac.setinfo_sample_size);
- default:
-
- }
- }
- else if(channels == 1) // 2 channels
- {
- int hassize ;
- int isnotcompressed ;
- int readsamplesize ;
-
- int uncompressed_bytes ;
-
- int interlacing_shift ;
- int interlacing_leftweight ;
-
- /* 2^result = something to do with output waiting.
- * perhaps matters if we read > 1 frame in a pass?
- */
- readbits(alac, 4);
-
- readbits(alac, 12); // unknown, skip 12 bits
-
- hassize = readbits(alac, 1); // the output sample size is stored soon
-
- uncompressed_bytes = readbits(alac, 2); // the number of bytes in the (compressed) stream that are not compressed
-
- isnotcompressed = readbits(alac, 1); // whether the frame is compressed
-
- if (hassize != 0)
- {
- /* now read the number of samples,
- * as a 32bit integer */
- outputsamples = readbits(alac, 32);
- outputsize = outputsamples * alac.bytespersample;
- }
-
- readsamplesize = alac.setinfo_sample_size - (uncompressed_bytes * 8) + 1;
-
- if (isnotcompressed == 0)
- { // compressed
- int[] predictor_coef_table_a = alac.predictor_coef_table_a;
- int predictor_coef_num_a ;
- int prediction_type_a ;
- int prediction_quantitization_a ;
- int ricemodifier_a ;
-
- int[] predictor_coef_table_b = alac.predictor_coef_table_b;
- int predictor_coef_num_b ;
- int prediction_type_b ;
- int prediction_quantitization_b ;
- int ricemodifier_b ;
-
- int tempPred = 0;
-
- interlacing_shift = readbits(alac, 8);
- interlacing_leftweight = readbits(alac, 8);
-
- /******** channel 1 ***********/
- prediction_type_a = readbits(alac, 4);
- prediction_quantitization_a = readbits(alac, 4);
-
- ricemodifier_a = readbits(alac, 3);
- predictor_coef_num_a = readbits(alac, 5);
-
- /* read the predictor table */
-
- for (int i = 0; i < predictor_coef_num_a; i++)
- {
- tempPred = readbits(alac,16);
- if(tempPred > 32767)
- {
- // the predictor coef table values are only 16 bit signed
- tempPred = tempPred - 65536;
- }
- predictor_coef_table_a[i] = tempPred;
- }
-
- /******** channel 2 *********/
- prediction_type_b = readbits(alac, 4);
- prediction_quantitization_b = readbits(alac, 4);
-
- ricemodifier_b = readbits(alac, 3);
- predictor_coef_num_b = readbits(alac, 5);
-
- /* read the predictor table */
-
- for (int i = 0; i < predictor_coef_num_b; i++)
- {
- tempPred = readbits(alac,16);
- if(tempPred > 32767)
- {
- // the predictor coef table values are only 16 bit signed
- tempPred = tempPred - 65536;
- }
- predictor_coef_table_b[i] = tempPred;
- }
-
- /*********************/
- if (uncompressed_bytes != 0)
- { // see mono case
- for (int i = 0; i < outputsamples; i++)
- {
- alac.uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8);
- alac.uncompressed_bytes_buffer_b[i] = readbits(alac, uncompressed_bytes * 8);
- }
- }
-
- /* channel 1 */
-
- entropy_rice_decode(alac, alac.predicterror_buffer_a, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier_a * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1);
-
- if (prediction_type_a == 0)
- { // adaptive fir
-
- alac.outputsamples_buffer_a = predictor_decompress_fir_adapt(alac.predicterror_buffer_a, outputsamples, readsamplesize, predictor_coef_table_a, predictor_coef_num_a, prediction_quantitization_a);
-
- }
- else
- { // see mono case
- System.err.println("FIXME: unhandled predicition type: " + prediction_type_a);
- }
-
- /* channel 2 */
- entropy_rice_decode(alac, alac.predicterror_buffer_b, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier_b * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1);
-
- if (prediction_type_b == 0)
- { // adaptive fir
- alac.outputsamples_buffer_b = predictor_decompress_fir_adapt(alac.predicterror_buffer_b, outputsamples, readsamplesize, predictor_coef_table_b, predictor_coef_num_b, prediction_quantitization_b);
- }
- else
- {
- System.err.println("FIXME: unhandled predicition type: " + prediction_type_b);
- }
- }
- else
- { // not compressed, easy case
- if (alac.setinfo_sample_size <= 16)
- {
- int bitsmove ;
-
- for (int i = 0; i < outputsamples; i++)
- {
- int audiobits_a ;
- int audiobits_b ;
-
- audiobits_a = readbits(alac, alac.setinfo_sample_size);
- audiobits_b = readbits(alac, alac.setinfo_sample_size);
-
- bitsmove = 32 - alac.setinfo_sample_size;
-
- audiobits_a = ((audiobits_a << bitsmove) >> bitsmove);
- audiobits_b = ((audiobits_b << bitsmove) >> bitsmove);
-
- alac.outputsamples_buffer_a[i] = audiobits_a;
- alac.outputsamples_buffer_b[i] = audiobits_b;
- }
- }
- else
- {
- int x ;
- int m = 1 << (24 -1);
-
- for (int i = 0; i < outputsamples; i++)
- {
- int audiobits_a ;
- int audiobits_b ;
-
- audiobits_a = readbits(alac, 16);
- audiobits_a = audiobits_a << (alac.setinfo_sample_size - 16);
- audiobits_a = audiobits_a | readbits(alac, alac.setinfo_sample_size - 16);
- x = audiobits_a & ((1 << 24) - 1);
- audiobits_a = (x ^ m) - m; // sign extend 24 bits
-
- audiobits_b = readbits(alac, 16);
- audiobits_b = audiobits_b << (alac.setinfo_sample_size - 16);
- audiobits_b = audiobits_b | readbits(alac, alac.setinfo_sample_size - 16);
- x = audiobits_b & ((1 << 24) - 1);
- audiobits_b = (x ^ m) - m; // sign extend 24 bits
-
- alac.outputsamples_buffer_a[i] = audiobits_a;
- alac.outputsamples_buffer_b[i] = audiobits_b;
- }
- }
- uncompressed_bytes = 0; // always 0 for uncompressed
- interlacing_shift = 0;
- interlacing_leftweight = 0;
- }
-
- switch(alac.setinfo_sample_size)
- {
- case 16:
- {
- deinterlace_16(alac.outputsamples_buffer_a, alac.outputsamples_buffer_b, outbuffer, alac.numchannels, outputsamples, interlacing_shift, interlacing_leftweight);
- break;
- }
- case 24:
- {
- deinterlace_24(alac.outputsamples_buffer_a, alac.outputsamples_buffer_b, uncompressed_bytes, alac.uncompressed_bytes_buffer_a, alac.uncompressed_bytes_buffer_b, outbuffer, alac.numchannels, outputsamples, interlacing_shift, interlacing_leftweight);
- break;
- }
- case 20:
- case 32:
- System.err.println("FIXME: unimplemented sample size " + alac.setinfo_sample_size);
-
- default:
-
- }
- }
- return outputsize;
- }
-
- public static AlacFile create_alac(int samplesize , int numchannels )
- {
- AlacFile newfile = new AlacFile();
-
- newfile.samplesize = samplesize;
- newfile.numchannels = numchannels;
- newfile.bytespersample = (samplesize / 8) * numchannels;
-
- return newfile;
- }
-}
-
View
62 src/com/beatofthedrum/alacdecoder/AlacFile.java
@@ -1,62 +0,0 @@
-/*
-** AlacFile.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-package com.beatofthedrum.alacdecoder;
-
-public class AlacFile
-{
-
- byte input_buffer[];
- int ibIdx = 0;
- int input_buffer_bitaccumulator = 0; /* used so we can do arbitary
- bit reads */
-
- int samplesize = 0;
- int numchannels = 0;
- int bytespersample = 0;
-
- LeadingZeros lz = new LeadingZeros();
-
-
- private int buffer_size = 16384;
- /* buffers */
- int predicterror_buffer_a[] = new int[buffer_size];
- int predicterror_buffer_b[] = new int[buffer_size];
-
- int outputsamples_buffer_a[] = new int[buffer_size];
- int outputsamples_buffer_b[] = new int[buffer_size];
-
- int uncompressed_bytes_buffer_a[] = new int[buffer_size];
- int uncompressed_bytes_buffer_b[] = new int[buffer_size];
-
-
-
- /* stuff from setinfo */
- public int setinfo_max_samples_per_frame = 0; // 0x1000 = 4096
- /* max samples per frame? */
-
- public int setinfo_7a = 0; // 0x00
- public int setinfo_sample_size = 0; // 0x10
- public int setinfo_rice_historymult = 0; // 0x28
- public int setinfo_rice_initialhistory = 0; // 0x0a
- public int setinfo_rice_kmodifier = 0; // 0x0e
- public int setinfo_7f = 0; // 0x02
- public int setinfo_80 = 0; // 0x00ff
- public int setinfo_82 = 0; // 0x000020e7
- /* max sample size?? */
- public int setinfo_86 = 0; // 0x00069fe4
- /* bit rate (avarge)?? */
- public int setinfo_8a_rate = 0; // 0x0000ac44
- /* end setinfo stuff */
-
- public int[] predictor_coef_table = new int[1024];
- public int[] predictor_coef_table_a = new int[1024];
- public int[] predictor_coef_table_b = new int[1024];
-}
View
32 src/com/beatofthedrum/alacdecoder/AlacInputStream.java
@@ -1,32 +0,0 @@
-package com.beatofthedrum.alacdecoder;
-
-import java.io.DataInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Author: Denis Tulskiy
- * Date: 4/7/11
- */
-public class AlacInputStream extends DataInputStream {
- /**
- * Creates a DataInputStream that uses the specified
- * underlying InputStream.
- *
- * @param in the specified input stream
- */
- public AlacInputStream(InputStream in) {
- super(in);
- }
-
- public void seek(long pos) {
- if (in instanceof FileInputStream) {
- try {
- ((FileInputStream) in).getChannel().position(pos);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-}
View
332 src/com/beatofthedrum/alacdecoder/AlacUtils.java
@@ -1,332 +0,0 @@
-/*
-** AlacUtils.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-package com.beatofthedrum.alacdecoder;
-
-public class AlacUtils
-{
- public static AlacContext AlacOpenFileInput(String inputfilename)
- {
- int headerRead;
- QTMovieT qtmovie = new QTMovieT();
- DemuxResT demux_res = new DemuxResT();
- AlacContext ac = new AlacContext();
- AlacInputStream input_stream;
- AlacFile alac;
-
- ac.error = false;
-
- try
- {
- java.io.FileInputStream fistream;
- fistream = new java.io.FileInputStream(inputfilename);
- input_stream = new AlacInputStream(fistream);
- }
- catch (java.io.FileNotFoundException fe)
- {
- ac.error_message = "Input file not found";
- ac.error = true;
- return (ac);
- }
-
- ac.input_stream = input_stream;
-
- /* if qtmovie_read returns successfully, the stream is up to
- * the movie data, which can be used directly by the decoder */
- headerRead = DemuxUtils.qtmovie_read(input_stream, qtmovie, demux_res);
-
- if (headerRead == 0)
- {
- ac.error = true;
- if (demux_res.format_read == 0)
- {
- ac.error_message = "Failed to load the QuickTime movie headers.";
- if (demux_res.format_read != 0)
- ac.error_message = ac.error_message + " File type: " + DemuxUtils.SplitFourCC(demux_res.format);
- }
- else
- {
- ac.error_message = "Error while loading the QuickTime movie headers.";
- }
- return (ac);
- }
- else if(headerRead == 3)
- {
- /*
- ** This section is used when the stream system being used doesn't support seeking
- ** We have kept track within the file where we need to go to, we close the file and
- ** skip bytes to go directly to that point
- */
-
- try
- {
- ac.input_stream.close();
- }
- catch(java.io.IOException ioe)
- {
- ac.error_message = "Error when seeking to start of music data";
- ac.error = true;
- return (ac);
- }
-
- try
- {
- java.io.FileInputStream fistream;
- fistream = new java.io.FileInputStream(inputfilename);
- input_stream = new AlacInputStream(fistream);
- ac.input_stream = input_stream;
-
- qtmovie.qtstream.stream = input_stream;
- qtmovie.qtstream.currentPos = 0;
- StreamUtils.stream_skip(qtmovie.qtstream, qtmovie.saved_mdat_pos);
- }
- catch (java.io.FileNotFoundException fe)
- {
- ac.error_message = "Input file not found";
- ac.error = true;
- return (ac);
- }
- }
-
- /* initialise the sound converter */
-
- alac = AlacDecodeUtils.create_alac(demux_res.sample_size, demux_res.num_channels);
-
- AlacDecodeUtils.alac_set_info(alac, demux_res.codecdata);
-
- ac.demux_res = demux_res;
- ac.alac = alac;
-
- return (ac);
-
- }
-
- public static void AlacCloseFile(AlacContext ac)
- {
- if(null != ac.input_stream)
- {
- try
- {
- ac.input_stream.close();
- }
- catch(java.io.IOException ioe)
- {
- }
- }
- }
-
- // Heres where we extract the actual music data
-
- public static int AlacUnpackSamples(AlacContext ac, int[] pDestBuffer)
- {
- int sample_byte_size;
- SampleDuration sampleinfo = new SampleDuration();
- byte[] read_buffer = ac.read_buffer;
- int destBufferSize = 1024 *24 * 3; // 24kb buffer = 4096 frames = 1 alac sample (we support max 24bps)
- int outputBytes;
- MyStream inputStream = new MyStream();
-
- inputStream.stream = ac.input_stream;
-
- // if current_sample_block is beyond last block then finished
-
- if(ac.current_sample_block >= ac.demux_res.sample_byte_size.length)
- {
- return 0;
- }
-
- if (get_sample_info(ac.demux_res, ac.current_sample_block , sampleinfo) == 0)
- {
- // getting sample failed
- return 0;
- }
-
- sample_byte_size = sampleinfo.sample_byte_size;
-
- StreamUtils.stream_read(inputStream, sample_byte_size, read_buffer, 0);
-
- /* now fetch */
- outputBytes = destBufferSize;
-
- outputBytes = AlacDecodeUtils.decode_frame(ac.alac, read_buffer, pDestBuffer, outputBytes);
-
- ac.current_sample_block = ac.current_sample_block + 1;
- outputBytes -= ac.offset * AlacGetBytesPerSample(ac);
- System.arraycopy(pDestBuffer, ac.offset, pDestBuffer, 0, outputBytes);
- ac.offset = 0;
- return outputBytes;
-
- }
-
-
- // Returns the sample rate of the specified ALAC file
-
- public static int AlacGetSampleRate(AlacContext ac)
- {
- if ( null != ac && ac.demux_res.sample_rate != 0)
- {
- return ac.demux_res.sample_rate;
- }
- else
- {
- return (44100);
- }
- }
-
- public static int AlacGetNumChannels(AlacContext ac)
- {
- if ( null != ac && ac.demux_res.num_channels != 0)
- {
- return ac.demux_res.num_channels;
- }
- else
- {
- return 2;
- }
- }
-
- public static int AlacGetBitsPerSample(AlacContext ac)
- {
- if (null != ac && ac.demux_res.sample_size != 0)
- {
- return ac.demux_res.sample_size;
- }
- else
- {
- return 16;
- }
- }
-
-
- public static int AlacGetBytesPerSample(AlacContext ac)
- {
- if ( null != ac && ac.demux_res.sample_size != 0)
- {
- return (int)Math.ceil(ac.demux_res.sample_size/8);
- }
- else
- {
- return 2;
- }
- }
-
-
- // Get total number of samples contained in the Apple Lossless file, or -1 if unknown
-
- public static int AlacGetNumSamples(AlacContext ac)
- {
- /* calculate output size */
- int num_samples = 0;
- int thissample_duration;
- int thissample_bytesize = 0;
- SampleDuration sampleinfo = new SampleDuration();
- int i;
- boolean error_found = false;
- int retval = 0;
-
- for (i = 0; i < ac.demux_res.sample_byte_size.length; i++)
- {
- thissample_duration = 0;
- thissample_bytesize = 0;
-
- retval = get_sample_info(ac.demux_res, i, sampleinfo);
-
- if(retval == 0)
- {
- return (-1);
- }
- thissample_duration = sampleinfo.sample_duration;
- thissample_bytesize = sampleinfo.sample_byte_size;
-
- num_samples += thissample_duration;
- }
-
- return (num_samples);
- }
-
-
- static int get_sample_info(DemuxResT demux_res, int samplenum, SampleDuration sampleinfo)
- {
- int duration_index_accum = 0;
- int duration_cur_index = 0;
-
- if (samplenum >= demux_res.sample_byte_size.length)
- {
- System.err.println("sample " + samplenum + " does not exist ");
- return 0;
- }
-
- if (demux_res.num_time_to_samples == 0) // was null
- {
- System.err.println("no time to samples");
- return 0;
- }
- while ((demux_res.time_to_sample[duration_cur_index].sample_count + duration_index_accum) <= samplenum)
- {
- duration_index_accum += demux_res.time_to_sample[duration_cur_index].sample_count;
- duration_cur_index++;
- if (duration_cur_index >= demux_res.num_time_to_samples)
- {
- System.err.println("sample " + samplenum + " does not have a duration");
- return 0;
- }
- }
-
- sampleinfo.sample_duration = demux_res.time_to_sample[duration_cur_index].sample_duration;
- sampleinfo.sample_byte_size = demux_res.sample_byte_size[samplenum];
-
- return 1;
- }
-
- /**
- * sets position in pcm samples
- * @param ac alac context
- * @param position position in pcm samples to go to
- */
-
- public static void AlacSetPosition(AlacContext ac, long position) {
- DemuxResT res = ac.demux_res;
-
- int current_position = 0;
- int current_sample = 0;
- SampleDuration sample_info = new SampleDuration();
- for (int i = 0; i < res.stsc.length; i++) {
- ChunkInfo chunkInfo = res.stsc[i];
- int last_chunk;
-
- if (i < res.stsc.length - 1) {
- last_chunk = res.stsc[i + 1].first_chunk;
- } else {
- last_chunk = res.stco.length;
- }
-
- for (int chunk = chunkInfo.first_chunk; chunk <= last_chunk; chunk++) {
- int pos = res.stco[chunk - 1];
- int sample_count = chunkInfo.samples_per_chunk;
- while (sample_count > 0) {
- int ret = get_sample_info(res, current_sample, sample_info);
- if (ret == 0) return;
- current_position += sample_info.sample_duration;
- if (position < current_position) {
- ac.input_stream.seek(pos);
- ac.current_sample_block = current_sample;
- ac.offset =
- (int) (position - (current_position - sample_info.sample_duration))
- * AlacGetNumChannels(ac);
- return;
- }
- pos += sample_info.sample_byte_size;
- current_sample++;
- sample_count--;
- }
- }
- }
- }
-}
View
11 src/com/beatofthedrum/alacdecoder/ChunkInfo.java
@@ -1,11 +0,0 @@
-package com.beatofthedrum.alacdecoder;
-
-/**
- * Author: Denis Tulskiy
- * Date: 4/9/11
- */
-public class ChunkInfo {
- int first_chunk;
- int samples_per_chunk;
- int sample_desc_index;
-}
View
10 src/com/beatofthedrum/alacdecoder/DecodeResult.java
@@ -1,10 +0,0 @@
-package com.beatofthedrum.alacdecoder;
-
-/**
- * Author: Denis Tulskiy
- * Date: 4/9/11
- */
-public class DecodeResult {
- public int bytesUnpacked;
- public int offset;
-}
View
17 src/com/beatofthedrum/alacdecoder/Defines.java
@@ -1,17 +0,0 @@
-/*
-** Defines.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-
-package com.beatofthedrum.alacdecoder;
-
-class Defines
-{
- static int RICE_THRESHOLD = 8;
-}
View
45 src/com/beatofthedrum/alacdecoder/DemuxResT.java
@@ -1,45 +0,0 @@
-/*
-** DemuxResT.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-package com.beatofthedrum.alacdecoder;
-
-class DemuxResT
-{
- public int format_read;
-
- public int num_channels;
- public int sample_size;
- public int sample_rate;
- public int format;
- public int[] buf = new int[1024*80];
-
- public SampleInfo[] time_to_sample = new SampleInfo[16];
- public int num_time_to_samples;
-
- public int[] sample_byte_size;
-
- public int codecdata_len;
-
- public int[] codecdata = new int[1024];
-
- public int[] stco;
- public ChunkInfo[] stsc;
-
- public int mdat_len;
-
- public DemuxResT()
- {
- // not sure how many of these I need, so make 16
- for (int i = 0; i < 16; i++)
- {
- time_to_sample[i] = new SampleInfo();
- }
- }
-}
View
890 src/com/beatofthedrum/alacdecoder/DemuxUtils.java
@@ -1,890 +0,0 @@
-/*
-** DemuxUtils.java
-**
-** Copyright (c) 2011 Peter McQuillan
-**
-** All Rights Reserved.
-**
-** Distributed under the BSD Software License (see license.txt)
-**
-*/
-
-package com.beatofthedrum.alacdecoder;
-
-
-class DemuxUtils
-{
- public static int MakeFourCC(int ch0, int ch1, int ch2, int ch3)
- {
- return ( ((ch0) << 24)| ((ch1) << 16)| ((ch2) << 8) | ((ch3)) );
- }
-
- public static int MakeFourCC32(int ch0, int ch1, int ch2, int ch3)
- {
- int retval = 0;
- int tmp = ch0;
-
- retval = tmp << 24;
-
- tmp = ch1;
-
- retval = retval | (tmp << 16);
- tmp = ch2;
-
- retval = retval | (tmp << 8);
- tmp = ch3;
-
- retval = retval | tmp;
-
- return (retval);
- }
-
- public static String SplitFourCC(int code)
- {
- String retstr;
- char c1;
- char c2;
- char c3;
- char c4;
-
- c1 = (char)((code >> 24) & 0xFF);
- c2 = (char)((code >> 16) & 0xFF);
- c3 = (char)((code >> 8) & 0xFF);
- c4 = (char)(code & 0xFF);
- retstr = c1 + " " + c2 + " " + c3 + " " + c4;
-
- return retstr;
-
- }
-
-
- public static int qtmovie_read(java.io.DataInputStream file, QTMovieT qtmovie, DemuxResT demux_res)
- {
- int found_moov = 0;
- int found_mdat = 0;
-
- /* construct the stream */
- qtmovie.qtstream.stream = file;
-
- qtmovie.res = demux_res;
-
- // reset demux_res TODO
-
- /* read the chunks */
- while (true)
- {
- int chunk_len;
- int chunk_id = 0;
-
- try
- {
- chunk_len = StreamUtils.stream_read_uint32(qtmovie.qtstream);
- }
- catch (Exception e)
- {
- System.err.println("(top) error reading chunk_len - possibly number too large");
- chunk_len = 1;
- }
-
- if (StreamUtils.stream_eof(qtmovie.qtstream) != 0)