Spaces:
Running
Running
| /* | |
| Copyright 2015 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. | |
| */ | |
| uint8_t UlawCodec::Int16ToUlaw(int16_t pcm_val) const { | |
| int16_t mask; | |
| int16_t seg; | |
| uint8_t uval; | |
| // u-law inverts all bits | |
| // Get the sign and the magnitude of the value. | |
| if (pcm_val < 0) { | |
| pcm_val = -pcm_val; | |
| mask = 0x7f; | |
| } else { | |
| mask = 0xff; | |
| } | |
| if (pcm_val > kClip) { | |
| pcm_val = kClip; // clip the magnitude | |
| } | |
| pcm_val += (kBias >> 2); | |
| // Convert the scaled magnitude to segment number. | |
| seg = SegmentSearch(pcm_val, seg_uend, 8); | |
| // Combine the sign, segment, quantization bits, | |
| // and complement the code word. | |
| if (seg >= 8) { // out of range, return maximum value. | |
| return (uint8_t)(0x7f ^ mask); | |
| } else { | |
| uval = (uint8_t) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); | |
| return uval ^ mask; | |
| } | |
| } | |
| int16_t UlawCodec::SegmentSearch(int16_t val, const int16_t *table, | |
| int size) const { | |
| for (int i = 0; i < size; ++i) { | |
| if (val <= *table++) { | |
| return i; | |
| } | |
| } | |
| return size; | |
| } | |
| int16_t UlawCodec::UlawToInt16(uint8_t ulaw) const { | |
| // Complement to obtain normal u-law value. | |
| ulaw = ~ulaw; | |
| // Extract and bias the quantization bits. Then | |
| // shift up by the segment number and subtract out the bias. | |
| int16_t t = ((ulaw & kQuantMask) << 3) + kBias; | |
| t <<= ((unsigned)ulaw & kSegMask) >> kSegShift; | |
| return ((ulaw & kSignBit) ? (kBias - t) : (t - kBias)) >> 2; | |
| } | |
| // | |
| // Table and constants for mu-law coding and decoding: | |
| // | |
| const int16_t UlawCodec::seg_uend[8] = { 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, | |
| 0xfff, 0x1fff}; | |
| const int16_t UlawCodec::kBias = 0x84; // Bias for linear code. | |
| const int16_t UlawCodec::kClip = 8159; // max. linear value | |
| const int16_t UlawCodec::kSignBit = 0x80; // Sign bit for a A-law byte. | |
| const int16_t UlawCodec::kQuantMask = 0xf; // Quantization field mask. | |
| const int16_t UlawCodec::kSegShift = 4; // Left shift for segment number. | |
| const int16_t UlawCodec::kSegMask = 0x70; // Segment field mask. | |
| bool WavRiffCodec::ReadChunk(FileResource *fr, std::string *chunk) const { | |
| if (chunk == NULL) { | |
| return false; | |
| } | |
| char ch[5]; | |
| if (fread(static_cast<void *>(&ch), 1, 4, fr->fp()) != 4) { | |
| return false; | |
| } | |
| ch[4] = '\0'; | |
| (*chunk) = ch; | |
| return true; | |
| } | |
| bool WavRiffCodec::CheckChunk(FileResource *fr, const char *data) const { | |
| std::string chunk; | |
| if (ReadChunk(fr, &chunk)) { | |
| return chunk == data; | |
| } | |
| return false; | |
| } | |
| bool WavRiffCodec::ReadHeader(FileResource *fr, | |
| int32_t *num_samples_per_channel, | |
| int32_t *sampling_rate) { | |
| if (!CheckChunk(fr, "RIFF")) { | |
| fprintf(stderr, "Invalid file: Expected \"RIFF\" in header"); | |
| return false; | |
| } | |
| // File length minus first 8 bytes of RIFF description, we don't use it | |
| int32_t len = 0; | |
| if (fread(static_cast<void *>(&len), 4, 1, fr->fp()) != 1) { | |
| fprintf(stderr, "Failed to read file length"); | |
| return false; | |
| } | |
| if (!CheckChunk(fr, "WAVE")) { | |
| fprintf(stderr, "Invalid file: Expected \"WAVE\" in header"); | |
| return false; | |
| } | |
| std::string riff_type; | |
| if (!ReadChunk(fr, &riff_type)) { | |
| fprintf(stderr, "Invalid file: Missing RIFF type header"); | |
| return false; | |
| } | |
| if (riff_type != "fmt " && riff_type != "bext") { | |
| fprintf(stderr, | |
| "Invalid file: RIFF type must be fmt or bext"); | |
| return false; | |
| } | |
| // If there is a broadcast format extension, skip it. | |
| if (riff_type == "bext") { | |
| int32_t bext_length; | |
| fread(static_cast<void *>(&bext_length), sizeof(bext_length), 1, fr->fp()); | |
| fseek(fr->fp(), bext_length, SEEK_CUR); | |
| std::string sub_type; | |
| if (!CheckChunk(fr, "fmt ")) { | |
| fprintf(stderr, "Invalid file: fmt subtype not found in bext header"); | |
| return false; | |
| } | |
| } | |
| // Now skip wave format header only reading the number of channels and sample | |
| // rate: | |
| if (fread(static_cast<void *>(&len), sizeof(len), 1, fr->fp()) != 1) { | |
| fprintf(stderr, "failed to skip file length"); | |
| return false; | |
| } | |
| if (len < 16) { // bad format chunk length | |
| fprintf(stderr, "Invalid of the wave format buffer"); | |
| return false; | |
| } | |
| int16_t n_channels = 0; | |
| fseek(fr->fp(), sizeof(n_channels), SEEK_CUR); | |
| if (fread(static_cast<void *>(&n_channels), sizeof(n_channels), 1, fr->fp()) != 1) { | |
| fprintf(stderr, "Failed to read number of channels"); | |
| return false; | |
| } | |
| if (n_channels != 1) { | |
| fprintf(stderr, "Attempt to load multi channel audio"); | |
| return false; | |
| } | |
| int32_t sample_rate; | |
| if (fread(&sample_rate, sizeof(sample_rate), 1, fr->fp()) != 1) { | |
| fprintf(stderr, "Failed to read sample rate"); | |
| return false; | |
| } | |
| fseek(fr->fp(), 8, SEEK_CUR); | |
| // advance in the stream to skip the wave format block | |
| fseek(fr->fp(), len - 16, SEEK_CUR); | |
| // now go to the end of "data" section, if found | |
| while (!fr->eof()) { | |
| if (fgetc(fr->fp()) == 'd' && | |
| fgetc(fr->fp()) == 'a' && | |
| fgetc(fr->fp()) == 't' && | |
| fgetc(fr->fp()) == 'a') { | |
| break; | |
| } | |
| } | |
| if (fr->eof()) { | |
| fprintf(stderr, "Unexpected end of file: no data"); | |
| return false; | |
| } | |
| int32_t num_bytes = 0; | |
| if (fread(&num_bytes, sizeof(num_bytes), 1, fr->fp()) != 1) { | |
| fprintf(stderr, "Failed to read number of bytes"); | |
| return false; | |
| } | |
| *sampling_rate = sample_rate; | |
| *num_samples_per_channel = num_bytes / 2; | |
| return true; | |
| } | |
| bool WavRiffCodec::ReadAudioData(int32_t wave_start, | |
| int32_t num_samples, | |
| std::vector<int16_t> *samples, | |
| FileResource *fr) { | |
| samples->resize(num_samples); | |
| bool status = true; | |
| switch (coding_type_) { | |
| case PCM16: { | |
| status &= (fseek(fr->fp(), | |
| wave_start * sizeof((*samples)[0]), SEEK_CUR) == 0); | |
| uint32_t read = fread(&(*samples)[0], sizeof(int16_t), num_samples, fr->fp()); | |
| if (read != num_samples) { | |
| fprintf(stderr, "WaveIO::ReadSamples: only %d out of %d values read", | |
| read, num_samples); | |
| status = false; | |
| } | |
| } | |
| break; | |
| case ULAW8: { | |
| UlawCodec sample_codec; | |
| uint8_t *buffer = new uint8_t[num_samples]; | |
| status &= (fseek(fr->fp(), wave_start * sizeof(*buffer), SEEK_CUR) == 0); | |
| uint32_t read = fread(buffer, sizeof(uint8_t), num_samples, fr->fp()); | |
| if (read != num_samples) { | |
| fprintf(stderr, "WaveIO::ReadSamples: only %d out of %d values read", | |
| read, num_samples); | |
| status = false; | |
| } | |
| for (int i = 0 ; i < num_samples; ++i) { | |
| (*samples)[i] = sample_codec.UlawToInt16(buffer[i]); | |
| } | |
| delete [] buffer; | |
| } | |
| break; | |
| default: | |
| fprintf(stderr, "WaveIO::ReadSamples: Unsupported coding type (%d)", | |
| coding_type_); | |
| status = false; | |
| } | |
| return status; | |
| } | |
| bool WavRiffCodec::ReadAudioContainer(int container_size_in_bytes, | |
| std::vector<int16_t> *samples, | |
| FileResource *fr) { | |
| int number_samples; | |
| switch (coding_type_) { | |
| case PCM16: | |
| number_samples = container_size_in_bytes / 2; | |
| break; | |
| case PCM8: | |
| case ULAW8: | |
| number_samples = container_size_in_bytes; | |
| break; | |
| default: | |
| return false; | |
| } | |
| return ReadAudioData(0, number_samples, samples, fr); | |
| } | |
| bool WavRiffCodec::Initialize(WaveCodingType coding_type) { | |
| if ((coding_type != PCM16) && (coding_type != PCM8) && | |
| (coding_type != ULAW8)) { | |
| return false; | |
| } | |
| coding_type_ = coding_type; | |
| return true; | |
| } | |