How to convert the WAV/OGG file to FLAC file in Android?
Asked Answered
G

2

14

EDIT: Incorporated the changed uv001's answer.

I can only find that ICS 4.0 support decoding of FLAC, but encode. I need some encoder to convert wav to flac, but currenty I can't find it. I find there is a jFlac avaible , but I don't know how to use this library, just simply convert the files.

Could anyone give me a hand on it?

Today, I just some idea by myself, with using the JavaFlacEncoder. and it works for certain bitrates of WAV.

I changed the value into a hard coding value in which it is working now.

/*
 * Copyright (C) 2010  Preston Lacey http://javaflacencoder.sourceforge.net/
 * All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package javaFlacEncoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import java.io.IOException;
/**
 * FLAC_FileEncoder is a class to encode an input wav File to an output Flac
 * file. It allows the EncodingConfiguration to be set only once, prior to
 * encoding the entire File.
 * 
 * @author Preston Lacey
 * @author Bo Tan (Temple)
 */
public class FLAC_FileEncoder {
    /** Maximum number of bytes to read from file at once */
    private static final int MAX_READ = 16384;

    /** Status enum for encode result */
    public enum Status {
        /** Unknown State. */
        UNKNOWN,
        /** Everything went well */
        FULL_ENCODE,

        /** Something unspecified went wrong...*/
        GENERAL_ERROR,

        /** internal error is something that went haywire that was discovered
         * due to internal sanity checks. A problem in API. */
        INTERNAL_ERROR,

        /** File given was not able to be read */
        UNSUPPORTED_FILE,

        /** Generic file IO Error */
        FILE_IO_ERROR,

        /** Sample size unsupported */
        UNSUPPORTED_SAMPLE_SIZE,

        /** Error with output file */
        OUTPUT_FILE_ERROR,
        /** No errors found. */
        OK
    }
    FLACEncoder flac = null;
    StreamConfiguration sc = null;
    EncodingConfiguration ec = null;
    File outFile = null;
    int lastTotalSamples = 0;
    boolean useThreads;

    /**
     * Constructor creates a FLAC_FileEncoder object with default
     * StreamConfiguration configuration and default EncodingConfiguration.
     * Thread use defaults to true.
     */
    public FLAC_FileEncoder() {
        flac = new FLACEncoder();
        sc = new StreamConfiguration();
        ec = new EncodingConfiguration();
        useThreads = true;
    }

    /**
     * Specify whether to use multiple threads or not.
     * @param val true to use threads, false otherwise.
     */
    public void useThreads(boolean val) {
        useThreads = val;
    }

    private void adjustConfigurations(){//(AudioFormat format) {
        int sampleRate = 16000;//(int)format.getSampleRate();
        int sampleSize = 16; //(int)format.getSampleSizeInBits();
        int channels =1;// (int)format.getChannels();
        //int blockSize = sc.getMaxBlockSize();
        /*sc = new StreamConfiguration(channels, blockSize, blockSize,
                sampleRate, sampleSize);*/
        sc.setSampleRate(sampleRate);
        sc.setBitsPerSample(sampleSize);
        sc.setChannelCount(channels);
    }

    /**
     * Set the stream configuration for this encoder to use. Note that the audio
     * characteristics(number of channels, sample rate, and sample size), will
     * be set to match the input file at encode time, so needn't be set in the
     * given StreamConfiguration object.
     * 
     * @param config StreamConfiguration to use for encoding.
     */
    public void setStreamConfig(StreamConfiguration config) {sc = config; }

    /**
     * Set the EncodingConfiguration to use for encoding.
     * @param config EncodingConfiguration to use.
     */
    public void setEncodingConfig(EncodingConfiguration config){ec = config;}

    private Status openStream() {
        Status status = Status.OK;
        boolean result = flac.setStreamConfiguration(sc);
        result = result & flac.setEncodingConfiguration(ec);
        if( !result)
            status = Status.INTERNAL_ERROR;
        else {
            FLACFileOutputStream fout = null;
            try {
               fout = new FLACFileOutputStream(outFile.getPath());
            } catch(IOException e) {
               status = Status.OUTPUT_FILE_ERROR;
               e.printStackTrace();
            }
            if( status == Status.OK) {
                flac.setOutputStream(fout);
                try {
                    flac.openFLACStream();
                }catch(IOException e) {
                    status = Status.INTERNAL_ERROR;
                }
            }
            else
                status = Status.OUTPUT_FILE_ERROR;
        }
        return status;
    }

    /**
     * Encode the given input wav file to an output file.
     *
     * @param inputFile Input wav file to encode.
     * @param outputFile Output file to write FLAC stream to. If file exists, it
     * will be overwritten without prompting.
     *
     * @return Status flag for encode
     */
    public Status encode(File inputFile, File outputFile) {
        Status status = Status.FULL_ENCODE;
        this.outFile = outputFile;
        //take file and initial configuration.
        //open file
//        AudioInputStream sin = null;
//        AudioFormat format = null;
//        //File inputFile = new File("encoderTest.wav");
//        try {
//            sin = AudioSystem.getAudioInputStream(inputFile);
//        }catch(IOException e) {
//            status = Status.FILE_IO_ERROR;
//        }catch (UnsupportedAudioFileException e) {
//            status = Status.UNSUPPORTED_FILE;
//        }finally {
//            if(status != Status.FULL_ENCODE)
//                return status;
//        }


        FileInputStream sin=null;
        try {
            sin = new FileInputStream(inputFile);
        } catch (FileNotFoundException e1) {
            status = Status.FILE_IO_ERROR;
            e1.printStackTrace();
        }finally {
            if (status != Status.FULL_ENCODE)
                return status;
        }



        try {
//            format = sin.getFormat();
            //sanitize and optimize configurations
             adjustConfigurations();  //adjustConfigurations(format);
            //open stream
            openStream();
            int frameSize = 2;//format.getFrameSize();
            int sampleSize = 16;//format.getSampleSizeInBits();
            int bytesPerSample = sampleSize/8;
            if(sampleSize %8 != 0) {
                //end processing now
                Exception newEx = new Exception(Status.UNSUPPORTED_SAMPLE_SIZE.name());
                throw newEx;

            }
            int channels =1;// format.getChannels();
            boolean bigEndian =false;// format.isBigEndian();
            byte[] samplesIn = new byte[(int)MAX_READ];
            int samplesRead;
            int framesRead;
            int[] sampleData = new int[MAX_READ*channels/frameSize];
            int blockSize = sc.getMaxBlockSize();
            int unencodedSamples = 0;
            int totalSamples = 0;
            while((samplesRead = sin.read(samplesIn, 0, MAX_READ)) != -1) {
                //System.err.println("Read: " + read);
                framesRead = samplesRead/(frameSize);
                if(bigEndian) {
                    for(int i = 0; i < framesRead*channels; i++) {
                        int lower8Mask = 255;
                        int temp = 0;
                        int totalTemp = 0;
                        for(int x = bytesPerSample-1; x >= 0; x++) {
                            int upShift = 8*x;
                            if(x == 0)//don't mask...we want sign
                                temp = ((samplesIn[bytesPerSample*i+x]) << upShift);
                            else
                                temp = ((samplesIn[bytesPerSample*i+x] & lower8Mask) << upShift);
                            totalTemp = totalTemp | temp;
                        }
                        sampleData[i] = totalTemp;
                    }
                }
                else {
                    for(int i = 0; i < framesRead*channels; i++) {
                        int lower8Mask = 255;
                        int temp = 0;
                        int totalTemp = 0;
                        for(int x = 0; x < bytesPerSample; x++) {
                            int upShift = 8*x;
                            if(x == bytesPerSample-1)//don't mask...we want sign
                                temp = ((samplesIn[bytesPerSample*i+x]) << upShift);
                            else
                                temp = ((samplesIn[bytesPerSample*i+x] & lower8Mask) << upShift);
                            totalTemp = totalTemp | temp;
                        }
                        sampleData[i] = totalTemp;
                    }
                }
                if(framesRead > 0) {
                   flac.addSamples(sampleData, framesRead);
                   unencodedSamples += framesRead;
                }
                //if(unencodedSamples > blockSize*100) {
                    if(useThreads)//Thread.yield();//
                        unencodedSamples -= flac.t_encodeSamples(unencodedSamples, false, flac.getThreadCount());
                    else
                        unencodedSamples -= flac.encodeSamples(unencodedSamples, false);
                    totalSamples += unencodedSamples;
                    //unencodedSamples = 0;

                //}
                //System.err.println("read : "+ samplesRead);
            }
            totalSamples += unencodedSamples;
            if(useThreads)
                unencodedSamples -= flac.t_encodeSamples(unencodedSamples, true, flac.getThreadCount());
            else
                unencodedSamples -= flac.encodeSamples(unencodedSamples, true);
            //unencodedSamples = 0;
            lastTotalSamples = totalSamples;
        }
        catch(IOException e) {
            status = Status.FILE_IO_ERROR;
        }
        catch(Exception e) {
            status = Status.GENERAL_ERROR;
            String message = e.getMessage();
            if(message == null) {            
                e.printStackTrace();
            }
            else if(message.equals(Status.UNSUPPORTED_SAMPLE_SIZE.name()))
                status = Status.UNSUPPORTED_SAMPLE_SIZE;
        }

        //System.err.print("LastTotalSamples: "+lastTotalSamples);
        return status;
    }

    /**
     * Get the total number of samples encoded in last encode.  This is here
     * primarily for use as a sanity check during debugging.
     *
     * @return Total number of samples encoded in last encode attempt.
     */
    public int getLastTotalSamplesEncoded() {
        return this.lastTotalSamples;
    }
}
Grolier answered 16/3, 2012 at 6:47 Comment(8)
I don't quite understand your question; do you want to encode a audio file using your android or do you want to "playback" some previously encoded file?Goldshlag
I want to use ASR from google, and wish to use android to record the sound to wav, and using some encoder to convert from wav to flac, and upload to google, and get the recognized text back. but currently the android don't have a encoder for FLAC,only decoder for FLAC in 3+ / ICS4+Grolier
Here's a FLAC library in C with makefile for Android: github.com/pelya/commandergenius/tree/sdl_android/project/jni/… You can easily compile a "flac" commandline tool for Android, using this libJohnie
It looks like javax.sound which is required for this code, is not usable in Android. Is there a simple implementation (maybe with example) that works to convert WAV to FLAC?Linter
Nevermind. I got this to work by downloading the entire JavaFlacEncoder project and copying the source files into my project and replaced FLAC_FileEncoder.java with the code above.Linter
@temple, I'm experiencing a same problem, can you share your code with me ?Sondra
Thanks for the above post. I am able to grenerate a FLAC file with above mentioned steps on Android. But the generated FLAC file size is very small as compared to the actual wav file. In my case the generated FLAC file is of just 86 Bytes, where as the actual wav file is of 140KB. The status being received of the encode process is FULL_ENCODE. But there is some fault in the encoding part, which I am not able to find out. Please let me know if you have a faced a similar problem.Divine
@Grolier - could you please add it in git so that we can use in our own application. Please show a small example of how to include this in android project.Woodley
S
3

Temple, Thanks for the post and the response with the attached code for FLAC conversion. It works as you described with one minor issue.

The FLACFileOutputStream (fout) and the FileInputStream (sin) are not closed at the end of the function. Please update your post/code so others can benefit as well.

finally {
     try {
          if (sin != null) sin.close();
          if (fout != null) fout.close();
     } catch (IOException e) {}
}

PS: FLACFileOutputStream fout will need to be promoted to a class variable for this to work.

Sheltonshelty answered 22/10, 2012 at 20:46 Comment(0)
L
1

Change the following line in your code

unencodedSamples -= flac.t_encodeSamples(unencodedSamples, true);

to

unencodedSamples -= flac.t_encodeSamples(unencodedSamples, true,flac.getThreadCount());
Lewislewisite answered 19/7, 2015 at 7:9 Comment(1)
This requires formatting and a description, regardless of it's correctness.Selfoperating

© 2022 - 2024 — McMap. All rights reserved.