// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.
package net.avadeaux.klipspringer.codec;
import java.nio.ByteBuffer;
import java.io.IOException;
import javax.sound.sampled.*;
/** Audio decoder that reads from an AudioInputStream. This allows obtaining an {@link AudioDecoder}
* for any format supported by the standard Java Sound system by wrapping a stream obtained for
* instance by {@link AudioSystem#getAudioInputStream(File)}.
*/
public class AudioInputStreamDecoder implements AudioDecoder {
private enum State { NEW, INITIALIZED, METADATA_DECODED, AUDIO_DECODED, DESTROYED }
private final Target target;
private final AudioInputStream stream;
private State state = State.NEW;
// Set in decodeMetadata, to postpone actually reading input.
private PcmFormat format;
/** Sets up for sending data from the given AudioInputStream to the given target. */
public AudioInputStreamDecoder(AudioInputStream stream, Target target) {
this.target = target;
this.stream = stream;
state = State.INITIALIZED;
}
/** Returns false, indicating that decodeAll can be called at most once. */
public boolean reusable() { return false; }
public synchronized void close() throws IOException {
state = State.DESTROYED;
stream.close();
}
// Rethrows exception after closing.
private void fail(RuntimeException ex) throws IOException {
try { close(); } catch (Throwable th) {
if (System.getProperty("klipspringer.debug") != null) { th.printStackTrace(); }
}
throw ex;
}
// Rethrows exception after closing.
private void fail(IOException ex) throws IOException {
try { close(); } catch (Throwable th) {
if (System.getProperty("klipspringer.debug") != null) { th.printStackTrace(); }
}
throw ex;
}
public synchronized void decodeMetadata() throws IOException {
try {
if (state != State.INITIALIZED) {
throw new IllegalStateException("Attempted decodeMetadata in state "+state);
}
format = PcmFormat.of(stream.getFormat());
target.metadata(format, stream.getFrameLength());
state = State.METADATA_DECODED;
} catch (IOException ex) { fail(ex); } catch (RuntimeException ex) { fail(ex); }
}
public synchronized boolean decodeAll(long fromSampleNo) throws IOException {
try (this) {
if (state == State.INITIALIZED) {
decodeMetadata();
}
if (state != State.METADATA_DECODED) {
throw new IllegalStateException("Attempted decodeAll in state "+state);
}
if (fromSampleNo != 0) { stream.skip(fromSampleNo * format.getFrameSize()); }
byte[] b = new byte[1024 * format.fs()];
ByteBuffer bb = ByteBuffer.wrap(b);
bb.order(format.order());
while (true) {
int n = stream.read(b);
if (n < 0) { state = State.AUDIO_DECODED; return true; }
bb.position(0);
bb.limit(n);
if (!target.write(bb)) { state = State.AUDIO_DECODED; return false; }
}
} catch (IOException ex) { fail(ex); } catch (RuntimeException ex) { fail(ex); }
return false; // unreachable
}
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home