1 /*
2  * Copyright (c) 1999, 2020, 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 
26 package com.sun.media.sound;
27 
28 import javax.sound.sampled.AudioFormat;
29 import javax.sound.sampled.AudioInputStream;
30 import javax.sound.sampled.AudioSystem;
31 
32 /**
33  * Common conversions etc.
34  *
35  * @author Kara Kytle
36  * @author Florian Bomers
37  */
38 public final class Toolkit {
39 
40     /**
41      * Suppresses default constructor, ensuring non-instantiability.
42      */
Toolkit()43     private Toolkit() {
44     }
45 
46     /**
47      * Converts bytes from signed to unsigned.
48      */
getUnsigned8(byte[] b, int off, int len)49     static void getUnsigned8(byte[] b, int off, int len) {
50         for (int i = off; i < (off+len); i++) {
51             b[i] += 128;
52         }
53     }
54 
55     /**
56      * Swaps bytes.
57      * @throws ArrayIndexOutOfBoundsException if len is not a multiple of 2.
58      */
getByteSwapped(byte[] b, int off, int len)59     static void getByteSwapped(byte[] b, int off, int len) {
60 
61         byte tempByte;
62         for (int i = off; i < (off+len); i+=2) {
63 
64             tempByte = b[i];
65             b[i] = b[i+1];
66             b[i+1] = tempByte;
67         }
68     }
69 
70     /**
71      * Linear to DB scale conversion.
72      */
linearToDB(float linear)73     static float linearToDB(float linear) {
74 
75         float dB = (float) (Math.log(((linear==0.0)?0.0001:linear))/Math.log(10.0) * 20.0);
76         return dB;
77     }
78 
79     /**
80      * DB to linear scale conversion.
81      */
dBToLinear(float dB)82     static float dBToLinear(float dB) {
83 
84         float linear = (float) Math.pow(10.0, dB/20.0);
85         return linear;
86     }
87 
88     /*
89      * returns bytes aligned to a multiple of blocksize
90      * the return value will be in the range of (bytes-blocksize+1) ... bytes
91      */
align(long bytes, int blockSize)92     static long align(long bytes, int blockSize) {
93         // prevent null pointers
94         if (blockSize <= 1) {
95             return bytes;
96         }
97         return bytes - (bytes % blockSize);
98     }
99 
align(int bytes, int blockSize)100     static int align(int bytes, int blockSize) {
101         // prevent null pointers
102         if (blockSize <= 1) {
103             return bytes;
104         }
105         return bytes - (bytes % blockSize);
106     }
107 
108     /*
109      * gets the number of bytes needed to play the specified number of milliseconds
110      */
millis2bytes(AudioFormat format, long millis)111     static long millis2bytes(AudioFormat format, long millis) {
112         long result = (long) (millis * format.getFrameRate() / 1000.0f * format.getFrameSize());
113         return align(result, format.getFrameSize());
114     }
115 
116     /*
117      * gets the time in milliseconds for the given number of bytes
118      */
bytes2millis(AudioFormat format, long bytes)119     static long bytes2millis(AudioFormat format, long bytes) {
120         return (long) (bytes / format.getFrameRate() * 1000.0f / format.getFrameSize());
121     }
122 
123     /*
124      * gets the number of bytes needed to play the specified number of microseconds
125      */
micros2bytes(AudioFormat format, long micros)126     static long micros2bytes(AudioFormat format, long micros) {
127         long result = (long) (micros * format.getFrameRate() / 1000000.0f * format.getFrameSize());
128         return align(result, format.getFrameSize());
129     }
130 
131     /*
132      * gets the time in microseconds for the given number of bytes
133      */
bytes2micros(AudioFormat format, long bytes)134     static long bytes2micros(AudioFormat format, long bytes) {
135         return (long) (bytes / format.getFrameRate() * 1000000.0f / format.getFrameSize());
136     }
137 
138     /*
139      * gets the number of frames needed to play the specified number of microseconds
140      */
micros2frames(AudioFormat format, long micros)141     static long micros2frames(AudioFormat format, long micros) {
142         return (long) (micros * format.getFrameRate() / 1000000.0f);
143     }
144 
145     /*
146      * gets the time in microseconds for the given number of frames
147      */
frames2micros(AudioFormat format, long frames)148     static long frames2micros(AudioFormat format, long frames) {
149         return (long) (((double) frames) / format.getFrameRate() * 1000000.0d);
150     }
151 
152     /**
153      * Throws an exception if the buffer size does not represent an integral
154      * number of sample frames.
155      */
validateBuffer(final int frameSize, final int bufferSize)156     static void validateBuffer(final int frameSize, final int bufferSize) {
157         if (bufferSize % frameSize == 0) {
158             return;
159         }
160         throw new IllegalArgumentException(String.format(
161                 "Buffer size (%d) does not represent an integral number of "
162                         + "sample frames (%d)", bufferSize, frameSize));
163     }
164 
165 
isFullySpecifiedAudioFormat(AudioFormat format)166     static void isFullySpecifiedAudioFormat(AudioFormat format) {
167         // Our code requires a positive frame size, that's probably is not
168         // necessary for non-linear encodings, but for now
169         // IllegalArgumentException is better than ArithmeticException
170         if (format.getFrameSize() <= 0) {
171             throw new IllegalArgumentException("invalid frame size: "
172                                                +((format.getFrameSize() == -1) ?
173                     "NOT_SPECIFIED":String.valueOf(format.getFrameSize())));
174         }
175         if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)
176             && !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)
177             && !format.getEncoding().equals(AudioFormat.Encoding.ULAW)
178             && !format.getEncoding().equals(AudioFormat.Encoding.ALAW)) {
179             // we don't know how to verify possibly non-linear encodings
180             return;
181         }
182         if (format.getFrameRate() <= 0) {
183             throw new IllegalArgumentException("invalid frame rate: "
184                                                +((format.getFrameRate()==-1)?
185                                                  "NOT_SPECIFIED":String.valueOf(format.getFrameRate())));
186         }
187         if (format.getSampleRate() <= 0) {
188             throw new IllegalArgumentException("invalid sample rate: "
189                                                +((format.getSampleRate()==-1)?
190                                                  "NOT_SPECIFIED":String.valueOf(format.getSampleRate())));
191         }
192         if (format.getSampleSizeInBits() <= 0) {
193             throw new IllegalArgumentException("invalid sample size in bits: "
194                                                +((format.getSampleSizeInBits()==-1)?
195                                                  "NOT_SPECIFIED":String.valueOf(format.getSampleSizeInBits())));
196         }
197         if (format.getChannels() <= 0) {
198             throw new IllegalArgumentException("invalid number of channels: "
199                                                +((format.getChannels()==-1)?
200                                                  "NOT_SPECIFIED":String.valueOf(format.getChannels())));
201         }
202     }
203 
isFullySpecifiedPCMFormat(AudioFormat format)204     static boolean isFullySpecifiedPCMFormat(AudioFormat format) {
205         if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)
206             && !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
207             return false;
208         }
209         if ((format.getFrameRate() <= 0)
210             || (format.getSampleRate() <= 0)
211             || (format.getSampleSizeInBits() <= 0)
212             || (format.getFrameSize() <= 0)
213             || (format.getChannels() <= 0)) {
214             return false;
215         }
216         return true;
217     }
218 
getPCMConvertedAudioInputStream(AudioInputStream ais)219     public static AudioInputStream getPCMConvertedAudioInputStream(AudioInputStream ais) {
220         // we can't open the device for non-PCM playback, so we have
221         // convert any other encodings to PCM here (at least we try!)
222         AudioFormat af = ais.getFormat();
223 
224         if( (!af.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) &&
225             (!af.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED))) {
226 
227             try {
228                 AudioFormat newFormat =
229                     new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
230                                      af.getSampleRate(),
231                                      16,
232                                      af.getChannels(),
233                                      af.getChannels() * 2,
234                                      af.getSampleRate(),
235                                      Platform.isBigEndian());
236                 ais = AudioSystem.getAudioInputStream(newFormat, ais);
237             } catch (Exception e) {
238                 if (Printer.err) e.printStackTrace();
239                 ais = null;
240             }
241         }
242 
243         return ais;
244     }
245 }
246