// 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/>.

import java.io.IOException;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import net.avadeaux.klipspringer.codec.FlacDecoder;
import net.avadeaux.klipspringer.codec.PcmFormat;

/** Demonstration of Klipspringer FLAC decoding interface that reads a FLAC file and writes a WAV
  * file. Compile and run example:
  * <pre>
  *     javac -cp lib/klipcodec.jar examples/FlacDecodeFile.java
  *     java -cp examples:lib/klipcodec.jar --enable-native-access=ALL-UNNAMED -Djava.library.path=lib FlacDecodeFile some_audio_file.flac output.wav
  * </pre>
  */
public class FlacDecodeFile {
    public static void main(String[] args) throws IOException {
        if (args.length != 2) {
            System.err.println("arguments: infile.flac outfile.wav");
            System.exit(1);
        }
        final FileChannel fc = new FileOutputStream(args[1]).getChannel();
        FlacDecoder decoder = new FlacDecoder(args[0], new FlacDecoder.Target() {
                public void close() throws IOException {
                    fc.close();
                }

                public void metadata(PcmFormat fmt, long totalSamples) throws IOException {
                    long totalSize = totalSamples * fmt.fs();

                    // WAV length must fit in 32 bits.
                    if (totalSize+36 > 0xffffffffL) {
                        throw new IOException("Cannot handle data size (too large): "+totalSize);
                    }
                    // Check conditions that would require bit fiddling (which Klipspringer can do,
                    // but not in this simple example).
                    if (fmt.ss()*8 != fmt.bips() || fmt.signed() != fmt.bips() > 8 || fmt.bigend()) {
                        throw new IOException("Cannot handle format (not directly WAV compatible): "+fmt);
                    }

                    ByteBuffer hb = ByteBuffer.allocate(44);
                    hb.order(ByteOrder.LITTLE_ENDIAN);
                    hb.putInt(0x46464952);              // "RIFF", backwards because litte endian
                    hb.putInt((int) totalSize + 36);
                    hb.putLong(0x20746d6645564157L);    // "WAVEfmt "
                    hb.putInt(16);                      // PCM
                    hb.putShort((short) 1);             // PCM = 1 (i.e. linear quantization)
                    hb.putShort((short) fmt.channels());
                    hb.putInt(fmt.rate());
                    hb.putInt(fmt.rate() * fmt.fs());
                    hb.putShort((short) fmt.fs());
                    hb.putShort((short) fmt.bips());
                    hb.putInt(0x61746164);              // "data"
                    hb.putInt((int) totalSize);
                    hb.flip();
                    fc.write(hb);
                }

                public boolean write(ByteBuffer buffer) throws IOException {
                    fc.write(buffer);
                    return true;
                }
            });
        decoder.decodeAll(0);
    }
}

