2

I found a library that helps to convert WAV file to Flac: https://github.com/jhurt/wav_to_flac

Also succeed to compile Flac to the platform and it works fine.

I've been using this library after capturing the audio on wav format to convert it to Flac and then send to my server.

Problem is that the audio file could be long and then precious time is wasted.

The thing is that I want to encode the audio as Flac format and send that to server on the same time when capturing and not after capturing stops, So, I need a help here on how to do that (encode Flac directly from the audio so I could send it to my server)...

Idan
  • 9,880
  • 10
  • 47
  • 76

3 Answers3

5

In my library called libsprec, you can see an example of both recording a WAV file (here) and converting it to FLAC (here). (Credits: the audio recording part heavily relies on Erica Sadun's work, for the record.)

Now if you want to do this in one step, you can do that as well. The trick is that you have to do the initialization of both the Audio Queues and the FLAC library first, then "interleave" the calls to them, i. e. when you get some audio data in the callback function of the Audio Queue, you immediately FLAC-encode it.

I don't think, however, that this would be much faster than recording and encoding in two separate steps. The heavy part of the processing is the recording and the maths in the encoding itself, so re-reading the same buffer (or I dare you, even a file!) won't add much to the processing time.

That said, you may want to do something like this:

// First, we initialize the Audio Queue

AudioStreamBasicDescription desc;
desc.mFormatID = kAudioFormatLinearPCM;
desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
desc.mReserved = 0;
desc.mSampleRate = SAMPLE_RATE;
desc.mChannelsPerFrame = 2; // stereo (?)
desc.mBitsPerChannel = BITS_PER_SAMPLE;
desc.mBytesPerFrame = BYTES_PER_FRAME;
desc.mFramesPerPacket = 1;
desc.mBytesPerPacket = desc.mFramesPerPacket * desc.mBytesPerFrame;

AudioQueueRef queue;

status = AudioQueueNewInput(
    &desc,
    audio_queue_callback, // our custom callback function
    NULL,
    NULL,
    NULL,
    0,
    &queue
);

if (status)
    return status;

AudioQueueBufferRef buffers[NUM_BUFFERS];

for (i = 0; i < NUM_BUFFERS; i++) {
    status = AudioQueueAllocateBuffer(
        queue,
        0x5000, // max buffer size
        &buffers[i]
    );
    if (status)
        return status;

    status = AudioQueueEnqueueBuffer(
        queue,
        buffers[i],
        0,
        NULL
    );
    if (status)
        return status;
}

// Then, we initialize the FLAC encoder:
FLAC__StreamEncoder *encoder;
FLAC__StreamEncoderInitStatus status;
FILE *infile;
const char *dataloc;
uint32_t rate;      /* sample rate */
uint32_t total;     /* number of samples in file */
uint32_t channels;  /* number of channels */
uint32_t bps;       /* bits per sample */
uint32_t dataoff;   /* offset of PCM data within the file */
int err;

/*
 * BUFFSIZE samples * 2 bytes per sample * 2 channels
 */
FLAC__byte buffer[BUFSIZE * 2 * 2];

/*
 * BUFFSIZE samples * 2 channels
 */
FLAC__int32 pcm[BUFSIZE * 2];


/*
 * Create and initialize the FLAC encoder
 */
encoder = FLAC__stream_encoder_new();
if (!encoder)
    return -1;


FLAC__stream_encoder_set_verify(encoder, true);
FLAC__stream_encoder_set_compression_level(encoder, 5);
FLAC__stream_encoder_set_channels(encoder, NUM_CHANNELS); // 2 for stereo
FLAC__stream_encoder_set_bits_per_sample(encoder, BITS_PER_SAMPLE); // 32 for stereo 16 bit per channel
FLAC__stream_encoder_set_sample_rate(encoder, SAMPLE_RATE);

status = FLAC__stream_encoder_init_stream(encoder, flac_callback, NULL, NULL, NULL, NULL);
if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
    return -1;


// We now start the Audio Queue...
status = AudioQueueStart(queue, NULL);

// And when it's finished, we clean up the FLAC encoder...
FLAC__stream_encoder_finish(encoder);
FLAC__stream_encoder_delete(encoder);

// and the audio queue and its belongings too
AudioQueueFlush(queue);
AudioQueueStop(queue, false);

for (i = 0; i < NUM_BUFFERS; i++)
    AudioQueueFreeBuffer(queue, buffers[i]);

AudioQueueDispose(queue, true);

// In the audio queue callback function, we do the encoding:

void audio_queue_callback(
    void *data,
    AudioQueueRef inAQ,
    AudioQueueBufferRef buffer,
    const AudioTimeStamp *start_time,
    UInt32 num_packets,
    const AudioStreamPacketDescription *desc
)
{
    unsigned char *buf = buffer->mAudioData;

    for (size_t i = 0; i < num_packets * channels; i++) {
        uint16_t msb = *(uint8_t *)(buf + i * 2 + 1);
        uint16_t usample = (msb << 8) | lsb;

        union {
            uint16_t usample;
            int16_t  ssample;
        } u;

        u.usample = usample;
        pcm[i] = u.ssample;
    }

    FLAC__bool succ = FLAC__stream_encoder_process_interleaved(encoder, pcm, num_packets);
    if (!succ)
        // handle_error();
}

// Finally, in the FLAC stream encoder callback:

FLAC__StreamEncoderWriteStatus flac_callback(
    const FLAC__StreamEncoder *encoder,
    const FLAC__byte buffer[],
    size_t bytes,
    unsigned samples,
    unsigned current_frame,
    void *client_data
)
{
    // Here process `buffer' and stuff,
    // then:

    return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
}

You are welcome.

  • 1
    It may not add much to processing time to first record, then encode, in the same way batch jobs don't, but it adds latency (the length of the WAV file is the latency) *and* puts an upper bound on the length of the recording. +1 tho for the answer. – Prof. Falken Aug 07 '13 at 06:31
  • @Prof.Falken Yes, you are absolutely right - I didn't think about that, honestly. Thank you! –  Aug 07 '13 at 06:32
  • Can you provide code I can compile? This has many unknown values and lack of info. – Idan Aug 07 '13 at 14:03
  • @Idan SO is not a "gimme teh codez" site. AFAIK all variable names are resolved (or symbols in ALL_CAPS are named sensibly and should be defined accordingly)... But you have to make some effort yourself too. Also, what is "lack of information"? It doesn't include free beer and unicorns or what? –  Aug 07 '13 at 14:05
  • @H2CO3 This is not what I meant. The sample code you gave looks good but it is mixed up. The initialisation the processing, all mixed and it's really hard to get from that what I want. A sample working code is always the best. I'm investing hours in fixing and understanding your code without the promise that all this would work as I expect... – Idan Aug 07 '13 at 15:15
  • @H2CO3 When using FLAC__bool succ = FLAC__stream_encoder_process_interleaved((FLAC__StreamEncoder *)data, pcm, num_packets); the app crash. Can you help please? I'm sending the encoder on the 'data'. – Idan Aug 08 '13 at 13:48
  • @Idan I'm pretty confident that it's a buffer overflow error. Check the number of channels and number of samples per frame, maybe you forgot a division by two somewhere? –  Aug 08 '13 at 13:50
  • @H2CO3 Solved this using a very good ref code as explain in link but I have issues with sample rate. I would really appreciate if you could check it out: http://stackoverflow.com/questions/18176505/setpreferredhardwaresamplerate-doesnt-work – Idan Aug 11 '13 at 20:47
  • @Idan I don't think that calling that method is necessary at all. –  Aug 11 '13 at 21:00
  • @H2CO3 What do you mean? Server needs the Flac to be 16kHz. Do you know any other method I can make the Flac samples be with this rate (not sampling in this rate and use the code on the link)? – Idan Aug 12 '13 at 12:50
  • @Idan: you can set the recording sample rate with the AudioQueue API. –  Aug 12 '13 at 16:47
  • @H2CO3 I've tried this also but that doesn't work. According to Apple doc here(http://developer.apple.com/library/ios/samplecode/AVCaptureToAudioUnit/Listings/CaptureSessionController_mm.html) there is no way to do that. If you know how to solve it please help. If you don't please don't send me to any more dead ends. Thanks! – Idan Aug 13 '13 at 13:24
1

Your question is not very specific, but you need to use Audio Recording Services, which will let you get access to the audio data in chunks, and then move the data you get from there into the streaming interface of the FLAC encoder. You can not use the WAV to FLAC program you linked to, you have to tap into the FLAC library yourself. API docs here.

Example on how to use a callback here.

TechFanatic
  • 1,218
  • 4
  • 13
  • 31
Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
0

can't you record your audio in wav using audio queue services and process output packets with your lib ?

edit from apple dev doc : "Applications writing AIFF and WAV files must either update the data header’s size field at the end of recording—which can result in an unusable file if recording is interrupted before the header is finalized—or they must update the size field after recording each packet of data, which is inefficient."

apparently it seems quite hard to encode a wav file on the fly

HaneTV
  • 916
  • 1
  • 7
  • 24
  • 1
    Maybe I could but couldn't find how to do that. Thought it's a common problem that someone could just ref me to an example... – Idan Aug 02 '13 at 09:29