Menu

Converting from raw pcm to flac

Help
2017-08-02
2017-08-11
  • Muhammad Moemin Ali

    Hi, I am using android to capture audio from phone's microphone via android's AudioRecord class. This gives me raw pcm data in an byte[] array. I then convert the byte[] array to int[] array. I am then using this code to encode the pcm data to flac but I get noise only.

    FLACEncoder flacEncoder = new FLACEncoder();
    StreamConfiguration streamConfiguration = new StreamConfiguration();
    streamConfiguration.setBitsPerSample(16);
    streamConfiguration.setChannelCount(1);
    streamConfiguration.setSampleRate(32000);
    //            streamConfiguration.setMinBlockSize(5292);
    //            streamConfiguration.setMaxBlockSize(10584);
    // 1) Set StreamConfiguration to appropriate values. After a stream is opened, this must not // be altered until the stream is closed.
                flacEncoder.setStreamConfiguration(streamConfiguration);
    FLACFileOutputStream flacOutputStream = new FLACFileOutputStream(new File(recordingdirplusname));
    // 2) Set FLACOutputStream, object to write results to.
    flacEncoder.setOutputStream(flacOutputStream);
    // 3) Open FLAC Stream
    flacEncoder.openFLACStream();
    // 4) Set EncodingConfiguration(if defaults are insufficient).
    // EncodingConfiguration encodingConfiguration = new EncodingConfiguration();
    // flacEncoder.setEncodingConfiguration(encodingConfiguration);
    int[] intbuffer = convertToIntArray(buffer); // buffer is the raw pcm data byte[] buffer
    flacEncoder.addSamples(intbuffer, (intbuffer.length/2));
    int encoded = flacEncoder.encodeSamples((intbuffer.length/2), false);
    

    and this is the method I use to convert byte[] to int[]:

    public static int[] convertToIntArray(byte[] input)
        {
            int[] ret = new int[input.length];
            int count = 0;
            for (int i = 0; i < input.length; i++)
            {
                ret[i] = input[i];
            }
            return ret;
        }
    

    What am I doing wrong here? Why do I only get noise and no sound in the FLAC file that is produced? Please help me on this. Thanks.

     

    Last edit: Muhammad Moemin Ali 2017-08-02
  • Preston Lacey

    Preston Lacey - 2017-08-02

    Greetings!

    I can't be certain without more information on your source format and how you're reading them from the AudioRecord instance, but It looks like your problem will be with the source format conversion to int. If you're reading 16-bit PCM from the AudioRecord object, make sure you're reading those into a short[] and not a byte[] https://developer.android.com/reference/android/media/AudioRecord.html#read(short[],%20int,%20int)

    If you use a byte[] for 16-bit(which is deprecated but should work), it will split the samples across two bytes and those must be combined into a single value when converting to int. You can either do that conversion, or read directly into a 16-bit short from AudioRecord.
    If this doesn't solve the problem, I'd be happy to look at it deeper if you provide the code for reading your samples, and any information you have on what format is used on the source side.

    Good luck

     
  • Muhammad Moemin Ali

    Hi, I did what you said and now I am able to successfully record in flac format. Thanks very much for the help!

     

    Last edit: Muhammad Moemin Ali 2017-08-02
  • Muhammad Moemin Ali

    I have another question, how can I resume recording at the end of a previously saved flac file. When I try to add encoded data at the end of previously saved flac file, the recording resumes but there is an audible gap in between when the last recording was stopped and then resumed. What is the correct way to do this in javaFlacEncoder? Thanks

     
  • Preston Lacey

    Preston Lacey - 2017-08-11

    Unfortunately, there's not a built-in way to properly append new data directly to a previously saved file using this encoder. Information about the encoded data(md5-hash, length, number of frames, etc) is kept while encoding, and this information is written out to the header space of the FLAC stream when the stream is closed. Currently, there is no method of restoring the encoder state necessary to write a proper header to the existing stream if appending new data.

     
  • Muhammad Moemin Ali

    Hi, FlacEncoder worked before but now it is not working properly. Some media players are unable to play the recording and the others that play do not display duration and provide seek options. Java code and the recording is attached. Can you tell me where is the problem?

    boolean isRecording = false;
    private void startRecording() { 
        FLACEncoder flacEncoder = new FLACEncoder();
        StreamConfiguration streamConfiguration = new StreamConfiguration();
        streamConfiguration.setBitsPerSample(16);
        streamConfiguration.setChannelCount(1);
        streamConfiguration.setSampleRate(8000);
        flacEncoder.setStreamConfiguration(streamConfiguration);
        FLACFileOutputStream flacOutputStream = new FLACFileOutputStream(new File(recordingdirplusname));
        flacEncoder.setOutputStream(flacOutputStream);
        flacEncoder.openFLACStream();
        int bufferSize = (2*(AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)));
        short[] buffershort = new short[bufferSize];
        AudioRecord audioRecorder = new AudioRecord(MediaRecorder.AudioSource.UNPROCESSED, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
        audioRecorder.startRecording();
        while (isRecording) {
            int numRead = audioRecorder.read(buffershort, 0, buffershort.length);
            int[] intbuffer = convertShortToIntArray(buffershort);
            flacEncoder.addSamples(intbuffer, (intbuffer.length)); 
            try {
                int encoded = flacEncoder.encodeSamples((intbuffer.length), false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        int encoded =  flacEncoder.encodeSamples(flacEncoder.samplesAvailableToEncode(), true);
    }
    
    public static int[] convertShortToIntArray(short[] input) {
        int[] ret = new int[input.length];
        for (int i = 0; i < input.length; i++) {
            ret[i] = (int)input[i];
        }
        return ret;
    }
    
     
  • Preston Lacey

    Preston Lacey - 2017-09-07

    I'm not actually certain at the moment what might be the problem. It sounds like the FLAC stream is not being finalized by the line, "flacEncoder.encodeSamples(flacEncoder.samplesAvailableToEncode(), true)"
    When the flac stream is finalized, various details needed for seeking(including length of stream and min/max encoded frame size) are written to the front of the stream if possible(this step will be ignored if the stream isn't seekable, but yours should be since you're writing to a file).

    First thing I'd check is that the return value from the above line is equal to the value given to encodeSamples, "flacEncoder.samplesAvailableToEncode()". Based on your code it should be equal, but being less than the number of samples requested to encode would indicate the stream is not being finalized. I've loaded the flac file you posted into a hex editor and it looks like the header is indeed not being updated; checking the return value would give more information on what the encoder is actually attempting to do. In any case, the file should remain playable even if the stream isn't finalized. There's one value in the written header(min frame size), that I noticed may not be set to the correct default. I'll double check the FLACEncoder source code tomorrow when I get a chance; in the meantime, feel free to post back if you check that return value or otherwise make progress.

    Also, while I don't see it as part of this problem, you should probably be adding samples using the returned value numRead from AudioRecord.read() in place of (int buffer.length), as there may be some cases where AudioRecord doesn't return a full block(this shouldn't prevent the file from playing or seeking, but could introduce random noise into the stream).

     
  • Muhammad Moemin Ali

    I just checked the value returned by '"flacEncoder.samplesAvailableToEncode()" and "flacEncoder.encodeSamples(flacEncoder.samplesAvailableToEncode(), true);" are the same. I check again the value of '"flacEncoder.samplesAvailableToEncode()" and it is equal to 0.
    I am using (int buffer.length) to give sample count parameter and not numRead.
    I have attached a recording that was recorded a while back from the same source code. I haven't changed anything in the source code that I know of. This recording is playing normally in all media players.

     
  • Preston Lacey

    Preston Lacey - 2017-09-07

    Interesting. The header for the #277 file is properly finalized, while the header for #388 is not. Does your code only occasionaly fail to produce a proper file now, or always fail? For example, did the run that verified the two values were equal also produce a good file, or a bad file? My point is I'm curious if something is only occasionally interrupting that code before it reaches the point of finalizing the header.

    I did check the library source though, and there is indeed a bug that might cause some players to fail to play the file outright if the header isn't finalized(it must've caused problems for others, I'm glad you've posted here). I'll post an update to fix this as soon as I'm able. The update won't fully fix your problem though, as something is still preventing the stream headers from being finalized in the first place. The bug-fix should allow files to remain playable, but they won't be seekable or have a known length.

     
  • Muhammad Moemin Ali

    I have fixed my problem. I had imported javaFlacEncoder source code and not the jar file. So, I deleted the source code and imported the jar file as a library. Before this, my code was failing everytime on all occassions but now it is encoding properly and producing good files that are seekable and have a known length.
    I will be waiting for the update though.

     
  • Muhammad Moemin Ali

    Hi, sometimes while closing the stream and finalizing using

    flacEncoder.encodeSamples(flacEncoder.samplesAvailableToEncode(), true);
    

    This Fatal Exception is thrown

     java.lang.NullPointerException: Attempt to read from field 'int javaFlacEncoder.BlockEncodeRequest.count' on a null object reference
                                                                                           at javaFlacEncoder.FLACEncoder.samplesAvailableToEncode(FLACEncoder.java:681)
    

    This happens sometimes and not all the times. What can I do to properly finalize?

     

    Last edit: Muhammad Moemin Ali 2017-09-22
  • Preston Lacey

    Preston Lacey - 2017-09-22

    I haven't seen that before but it sounds like an internal bug in the flac library. I'll check it out. In the meantime, can you tell me the conditions of the failure(which javaFlacEncoder version, encode settings used, is this still 16-bit mono as above?)

     
  • Muhammad Moemin Ali

    Version is 0.3.1 , 16-bit mono. I reproduced these errors on the following sampling rates:
    48 kHz, 44.1 kHz, 32 kHz, 22.05 kHz, 16 kHz. 11.025 kHz. Haven't seen it on 8 kHz till now.

     
  • Preston Lacey

    Preston Lacey - 2017-09-22

    I've been looking at that section of code and I think I found the bug. There's an unchecked access on a potentially null object in the method samplesAvailableToEncode(). If it happens that the number of samples added up to the point you call samplesAvailableToEncode() is a perfect multiple of the block-size used by the encoder, it should trigger the exception(or if no samples were ever added). I'll create a test case and verify this later tonight. Hopefully I will get the update posted tomorrow to fix it.

     
  • Preston Lacey

    Preston Lacey - 2017-09-25

    I just posted an update that should fix these two bugs.

     
  • Muhammad Moemin Ali

    I confirm that the update fixed the bugs.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.