blob: 16c5640baf576544e4b9601bcd7772f19a3edf3f [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/speech/audio_encoder_flac.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_number_conversions.h"
namespace cobalt {
namespace speech {
namespace {
const char kContentTypeFLAC[] = "audio/x-flac; rate=";
const int kFLACCompressionLevel = 0; // 0 for speed
const int kBitsPerSample = 16;
const float kMaxInt16AsFloat32 = 32767.0f;
} // namespace
AudioEncoderFlac::AudioEncoderFlac(int sample_rate)
: encoder_(FLAC__stream_encoder_new()) {
DCHECK(encoder_);
// Set the number of channels to be encoded.
FLAC__stream_encoder_set_channels(encoder_, 1);
// Set the sample resolution of the input to be encoded.
FLAC__stream_encoder_set_bits_per_sample(encoder_, kBitsPerSample);
// Set the sample rate (in Hz) of the input to be encoded.
FLAC__stream_encoder_set_sample_rate(encoder_,
static_cast<uint32>(sample_rate));
// Set the compression level. A higher level usually means more computation
// but higher compression.
FLAC__stream_encoder_set_compression_level(encoder_, kFLACCompressionLevel);
// Initialize the encoder instance to encode native FLAC stream.
FLAC__StreamEncoderInitStatus encoder_status =
FLAC__stream_encoder_init_stream(encoder_, WriteCallback, NULL, NULL,
NULL, this);
DCHECK_EQ(encoder_status, FLAC__STREAM_ENCODER_INIT_STATUS_OK);
}
AudioEncoderFlac::~AudioEncoderFlac() {
DCHECK(thread_checker_.CalledOnValidThread());
FLAC__stream_encoder_delete(encoder_);
}
void AudioEncoderFlac::Encode(const ShellAudioBus* audio_bus) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(audio_bus->channels(), 1);
uint32 frames = static_cast<uint32>(audio_bus->frames());
scoped_array<FLAC__int32> flac_samples(new FLAC__int32[frames]);
for (uint32 i = 0; i < frames; ++i) {
if (audio_bus->sample_type() == ShellAudioBus::kFloat32) {
flac_samples[i] = static_cast<FLAC__int32>(
audio_bus->GetFloat32Sample(0, i) * kMaxInt16AsFloat32);
} else {
DCHECK_EQ(audio_bus->sample_type(), ShellAudioBus::kInt16);
flac_samples[i] =
static_cast<FLAC__int32>(audio_bus->GetInt16Sample(0, i));
}
}
FLAC__int32* flac_samples_ptr = flac_samples.get();
// Submit data for encoding.
FLAC__bool success =
FLAC__stream_encoder_process(encoder_, &flac_samples_ptr, frames);
DCHECK(success);
}
void AudioEncoderFlac::Finish() {
DCHECK(thread_checker_.CalledOnValidThread());
// Finish the encoding. It causes the encoder to encode any data still in
// its input pipe, and finally reset the encoder to the unintialized state.
FLAC__stream_encoder_finish(encoder_);
}
std::string AudioEncoderFlac::GetMimeType() const {
DCHECK(thread_checker_.CalledOnValidThread());
return std::string(kContentTypeFLAC) +
base::UintToString(FLAC__stream_encoder_get_sample_rate(encoder_));
}
std::string AudioEncoderFlac::GetAndClearAvailableEncodedData() {
DCHECK(thread_checker_.CalledOnValidThread());
std::string result = encoded_data_;
encoded_data_.clear();
return result;
}
// A write callback which will be called anytime there is a raw encoded data to
// write. The call to FLAC__stream_encoder_init_stream() currently will also
// immediately call the write callback several times, once with the FLAC
// signature, and once for each encoded metadata block.
FLAC__StreamEncoderWriteStatus AudioEncoderFlac::WriteCallback(
const FLAC__StreamEncoder* encoder, const FLAC__byte buffer[], size_t bytes,
unsigned int samples, unsigned int current_frame, void* client_data) {
UNREFERENCED_PARAMETER(encoder);
UNREFERENCED_PARAMETER(samples);
UNREFERENCED_PARAMETER(current_frame);
AudioEncoderFlac* audio_encoder =
reinterpret_cast<AudioEncoderFlac*>(client_data);
DCHECK(audio_encoder);
DCHECK(audio_encoder->thread_checker_.CalledOnValidThread());
audio_encoder->encoded_data_.append(reinterpret_cast<const char*>(buffer),
bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
} // namespace speech
} // namespace cobalt