/* * [U_Law.java] * * Summary: convert mu-law 8-bit sound encoding back and forth to 16-bit linear. * * Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 1997-12-05 * 1.1 1998-11-10 add name and address. */ package com.mindprod.sound; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import static java.lang.System.*; /** * convert mu-law 8-bit sound encoding back and forth to 16-bit linear. *

* Deals with images of sound files in RAM, not disk-based. * Based on anonymous source. * * @author Roedy Green, Canadian Mind Products * @version 1.1 1998-11-10 add name and address. * @since 1997-12-05 */ public final class U_Law { /** * if true, causes debug info to be displayed */ private static final boolean DEBUGGING = false; /* * bias in mu-law encoding. */ private static final int BIAS = 0x84;// +132 private static final int FIRST_COPYRIGHT_YEAR = 1998; /* * offset into a *.au file where you find the info field. */ private static final int info_offset = 24; /** * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /* * Boundary points for various mu-law segments, * (range bands in the 16-bit linear values. * Each segment is recorded * with a different amount of precision. */ private static final short[] seg_end = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF }; /** * Just the samplings, 8-bit mu-law encoded. Does not include the header. */ private final byte[] data; /** * Arbitrary string to label the *.au file internally. */ private String info = " Built by U_Law "; /** * The first 4 signature characters of a *.au sound (snd) file. */ private String magic = ".snd"; /** * For stereo this would be 2, the number of channels. We only handle one channel with this class. */ private int channelCount = 1; /** * Code for 8-bit mu-law encoding in the *.au file header. */ private int dataFormat = 1; /** * Offset of the mu-law sound data into the *.au file. */ private int dataLocation; /** * Size of the sound in samples. */ private int dataSize; /** * Samples per second. */ private int samplingRate = 8000; /** * constructor. * * @param b Mbyte array image of mu-law encoded .au file, including headers. */ public U_Law( byte[] b ) { magic = new String( b, 0, 4 ); try { DataInputStream bis = new DataInputStream( new ByteArrayInputStream( b, 4, b .length - 4 ) ); dataLocation = bis.readInt(); dataSize = bis.readInt(); dataFormat = bis.readInt(); samplingRate = bis.readInt(); channelCount = bis.readInt(); info = new String( b, info_offset, dataLocation - info_offset ); } catch ( Exception e ) { err.println(); err.println(); e.printStackTrace( System.err ); err.println(); System.exit( 4 ); } data = new byte[ dataSize ]; System.arraycopy( b, dataLocation, data, 0, dataSize ); if ( DEBUGGING ) { out.println( "magic <" + magic + "> dataLocation <" + dataLocation + "> dataSize <" + dataSize + "< dataFormat <" + dataFormat + "> samplingRate <" + samplingRate + "> channelCount <" + channelCount + "> info <" + info + ">" ); } } // end constructor U_Law /** * Constructor for 16-bit linear. * * @param info Arbitrary string to label the *.au file internally. * @param b Linear array of shorts. */ public U_Law( String info, short[] b ) { this.info = info; dataSize = b.length; data = new byte[ b.length ]; /* * Convert a linear 16-bit PCM value to 8-bit u-law * In order to simplify the encoding process, the original linear magnitude * is biased by adding 33 which shifts the encoding range from (0 - 8158) to * (33 - 8191). The result can be seen in the following encoding table: * Biased Linear Input Code Compressed Code * ------------------------ --------------- * 00000001wxyza 000wxyz * 0000001wxyzab 001wxyz * 000001wxyzabc 010wxyz * 00001wxyzabcd 011wxyz * 0001wxyzabcde 100wxyz * 001wxyzabcdef 101wxyz * 01wxyzabcdefg 110wxyz * 1wxyzabcdefgh 111wxyz * Each biased linear code has a leading 1 which identifies the segment * number. The value of the segment number is equal to 7 minus the number * of leading 0's. The quantization interval is directly available as the * four bits wxyz. * The trailing bits (a - h) are ignored. * Ordinarily the complement of the resulting code word is used for * transmission, and so the code word is complemented before it is returned. * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ for ( int p = 0; p < b.length; p++ ) { int mask; int seg; byte uval; short pcm_val = b[ p ]; /* Get the sign and the magnitude of the value. */ if ( pcm_val < 0 ) { pcm_val = ( short ) ( BIAS - pcm_val ); mask = 0x7F; } else { pcm_val += BIAS; mask = 0xFF; } /* Convert the scaled magnitude to segment number. */ seg = calcSegment( pcm_val ); /* * Combine the sign, segment, quantization bits; * and complement the code word. */ if ( seg >= 8 )/* out of range, return maximum value. */ { data[ p ] = ( byte ) ( 0x7F ^ mask ); } else { uval = ( byte ) ( ( seg << 4 ) | ( ( pcm_val >> ( seg + 3 ) ) & 0xF ) ); data[ p ] = ( byte ) ( uval ^ mask ); } } } // end constructor U_Law /** * find which mu-law segment this 16-bit value fits in. * * @param val a sound sampling * * @return mulaw band. */ private static int calcSegment( int val ) { for ( int i = 0; i < seg_end.length; i++ ) { if ( val <= seg_end[ i ] ) { return ( i ); } } return seg_end.length; } // end calcSegment /** * Add header to byte array representation to create mu-law encoded *.au file. * * @return Mulaw-encoded sound as a byte array. */ public byte[] toBytes() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( bos ); dataLocation = info_offset + info.length(); dos.write( magic.getBytes(), 0, 4 ); dos.writeInt( dataLocation ); dos.writeInt( dataSize ); dos.writeInt( dataFormat ); dos.writeInt( samplingRate ); dos.writeInt( channelCount ); dos.write( info.getBytes() ); dos.write( data ); return bos.toByteArray(); } catch ( Exception e ) { e.printStackTrace( System.err ); err.println(); System.exit( 4 ); } return null; } // end toBytes /** * tolinear() - Convert a u-law values to 16-bit linear PCM *

* First, a biased linear code is derived from the code word. An unbiased output can then be obtained by subtracting * from the biased code. * * @return array of shorts linear encoded sound. */ public short[] toLinear() { short[] pcm_array = new short[ dataSize ]; for ( int p = 0; p < dataSize; p++ ) { // uncomplement the codeword byte u_val = ( byte ) ( ( ~data[ p ] ) & 0xff ); // Extract and bias the quantization bits. int t = ( ( u_val & 0xf ) << 3 ) + BIAS; // shift up by the segment number t <<= ( u_val & 0x70 ) >> 4; // subtract out the bias. pcm_array[ p ] = ( short ) ( ( u_val < 0 ) ? ( BIAS - t ) : ( t - BIAS ) ); } return pcm_array; } // end toLinear }