2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.doorbird.internal.audio;
15 import java.io.BufferedInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
19 import javax.sound.sampled.AudioFormat;
20 import javax.sound.sampled.AudioFormat.Encoding;
21 import javax.sound.sampled.AudioInputStream;
22 import javax.sound.sampled.AudioSystem;
23 import javax.sound.sampled.UnsupportedAudioFileException;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
29 * This class convert a stream to the normalized ulaw
30 * format wanted by doorbird api
32 * @author Gwendal Roulleau - Initial contribution
35 public class ConvertedInputStream extends InputStream {
37 private static final javax.sound.sampled.AudioFormat INTERMEDIARY_PCM_FORMAT = new javax.sound.sampled.AudioFormat(
38 javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 2, 8000, false);
39 private static final javax.sound.sampled.AudioFormat TARGET_ULAW_FORMAT = new javax.sound.sampled.AudioFormat(
40 javax.sound.sampled.AudioFormat.Encoding.ULAW, 8000, 8, 1, 1, 8000, false);
42 private AudioInputStream pcmUlawInputStream;
44 public ConvertedInputStream(InputStream innerInputStream) throws UnsupportedAudioFileException, IOException {
45 pcmUlawInputStream = getULAWStream(new BufferedInputStream(innerInputStream));
48 public AudioInputStream getAudioInputStream() {
49 return pcmUlawInputStream;
53 public int read(byte @Nullable [] b) throws IOException {
54 return pcmUlawInputStream.read(b);
58 public int read(byte @Nullable [] b, int off, int len) throws IOException {
59 return pcmUlawInputStream.read(b, off, len);
63 public byte[] readAllBytes() throws IOException {
64 return pcmUlawInputStream.readAllBytes();
68 public byte[] readNBytes(int len) throws IOException {
69 return pcmUlawInputStream.readNBytes(len);
73 public int readNBytes(byte @Nullable [] b, int off, int len) throws IOException {
74 return pcmUlawInputStream.readNBytes(b, off, len);
78 public int read() throws IOException {
79 return pcmUlawInputStream.read();
83 public void close() throws IOException {
84 pcmUlawInputStream.close();
88 * Ensure the right ULAW format by converting if necessary (two pass)
90 * @param originalInputStream a mark/reset compatible stream
92 * @return A ULAW stream (1 channel, 8000hz, 16 bit signed)
94 * @throws UnsupportedAudioFileException
96 private AudioInputStream getULAWStream(InputStream originalInputStream)
97 throws UnsupportedAudioFileException, IOException {
99 AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(originalInputStream);
100 AudioFormat format = audioInputStream.getFormat();
102 boolean frameRateOk = Math.abs(format.getFrameRate() - 8000) < 1;
103 boolean sampleRateOk = Math.abs(format.getSampleRate() - 8000) < 1;
105 if (format.getEncoding().equals(Encoding.ULAW) && format.getChannels() == 1 && frameRateOk && sampleRateOk
106 && format.getFrameSize() == 1 && format.getSampleSizeInBits() == 8) {
107 return audioInputStream;
110 // we have to use an intermediary format with 16 bits, even if the final target format is 8 bits
111 // this is a limitation of the conversion library, which only accept 16 bits input to convert to ULAW.
112 AudioInputStream targetPCMFormat = audioInputStream;
113 if (format.getChannels() != 1 || !frameRateOk || !sampleRateOk || format.getFrameSize() != 2
114 || format.getSampleSizeInBits() != 16) {
115 targetPCMFormat = AudioSystem.getAudioInputStream(INTERMEDIARY_PCM_FORMAT, audioInputStream);
118 return AudioSystem.getAudioInputStream(TARGET_ULAW_FORMAT, targetPCMFormat);
119 } catch (IllegalArgumentException iarg) {
120 throw new UnsupportedAudioFileException(
121 "Cannot convert audio input to ULAW target format. Cause: " + iarg.getMessage());