| /********** |
| 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 3 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| 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 |
| **********/ |
| // "liveMedia" |
| // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| // Filters for converting between raw PCM audio and uLaw |
| // Implementation |
| |
| #include "uLawAudioFilter.hh" |
| |
| ////////// 16-bit PCM (in various byte orders) -> 8-bit u-Law ////////// |
| |
| uLawFromPCMAudioSource* uLawFromPCMAudioSource |
| ::createNew(UsageEnvironment& env, FramedSource* inputSource, int byteOrdering) { |
| // "byteOrdering" must be 0, 1, or 2: |
| if (byteOrdering < 0 || byteOrdering > 2) { |
| env.setResultMsg("uLawFromPCMAudioSource::createNew(): bad \"byteOrdering\" parameter"); |
| return NULL; |
| } |
| return new uLawFromPCMAudioSource(env, inputSource, byteOrdering); |
| } |
| |
| uLawFromPCMAudioSource |
| ::uLawFromPCMAudioSource(UsageEnvironment& env, FramedSource* inputSource, |
| int byteOrdering) |
| : FramedFilter(env, inputSource), |
| fByteOrdering(byteOrdering), fInputBuffer(NULL), fInputBufferSize(0) { |
| } |
| |
| uLawFromPCMAudioSource::~uLawFromPCMAudioSource() { |
| delete[] fInputBuffer; |
| } |
| |
| void uLawFromPCMAudioSource::doGetNextFrame() { |
| // Figure out how many bytes of input data to ask for, and increase |
| // our input buffer if necessary: |
| unsigned bytesToRead = fMaxSize*2; // because we're converting 16 bits->8 |
| if (bytesToRead > fInputBufferSize) { |
| delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead]; |
| fInputBufferSize = bytesToRead; |
| } |
| |
| // Arrange to read samples into the input buffer: |
| fInputSource->getNextFrame(fInputBuffer, bytesToRead, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void uLawFromPCMAudioSource |
| ::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| uLawFromPCMAudioSource* source = (uLawFromPCMAudioSource*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| #define BIAS 0x84 // the add-in bias for 16 bit samples |
| #define CLIP 32635 |
| |
| static unsigned char uLawFrom16BitLinear(u_int16_t sample) { |
| static int const exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, |
| 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, |
| 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, |
| 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, |
| 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, |
| 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, |
| 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, |
| 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
| 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; |
| unsigned char sign = (sample >> 8) & 0x80; |
| if (sign != 0) sample = -sample; // get the magnitude |
| |
| if (sample > CLIP) sample = CLIP; // clip the magnitude |
| sample += BIAS; |
| |
| unsigned char exponent = exp_lut[(sample>>7) & 0xFF]; |
| unsigned char mantissa = (sample >> (exponent+3)) & 0x0F; |
| unsigned char result = ~(sign | (exponent << 4) | mantissa); |
| if (result == 0 ) result = 0x02; // CCITT trap |
| |
| return result; |
| } |
| |
| void uLawFromPCMAudioSource |
| ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Translate raw 16-bit PCM samples (in the input buffer) |
| // into uLaw samples (in the output buffer). |
| unsigned numSamples = frameSize/2; |
| switch (fByteOrdering) { |
| case 0: { // host order |
| u_int16_t* inputSample = (u_int16_t*)fInputBuffer; |
| for (unsigned i = 0; i < numSamples; ++i) { |
| fTo[i] = uLawFrom16BitLinear(inputSample[i]); |
| } |
| break; |
| } |
| case 1: { // little-endian order |
| for (unsigned i = 0; i < numSamples; ++i) { |
| u_int16_t const newValue = (fInputBuffer[2*i+1]<<8)|fInputBuffer[2*i]; |
| fTo[i] = uLawFrom16BitLinear(newValue); |
| } |
| break; |
| } |
| case 2: { // network (i.e., big-endian) order |
| for (unsigned i = 0; i < numSamples; ++i) { |
| u_int16_t const newValue = (fInputBuffer[2*i]<<8)|fInputBuffer[2*i+i]; |
| fTo[i] = uLawFrom16BitLinear(newValue); |
| } |
| break; |
| } |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numSamples; |
| fNumTruncatedBytes = numTruncatedBytes; |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |
| |
| |
| ////////// u-Law -> 16-bit PCM (in host order) ////////// |
| |
| PCMFromuLawAudioSource* PCMFromuLawAudioSource |
| ::createNew(UsageEnvironment& env, FramedSource* inputSource) { |
| return new PCMFromuLawAudioSource(env, inputSource); |
| } |
| |
| PCMFromuLawAudioSource |
| ::PCMFromuLawAudioSource(UsageEnvironment& env, |
| FramedSource* inputSource) |
| : FramedFilter(env, inputSource), |
| fInputBuffer(NULL), fInputBufferSize(0) { |
| } |
| |
| PCMFromuLawAudioSource::~PCMFromuLawAudioSource() { |
| delete[] fInputBuffer; |
| } |
| |
| void PCMFromuLawAudioSource::doGetNextFrame() { |
| // Figure out how many bytes of input data to ask for, and increase |
| // our input buffer if necessary: |
| unsigned bytesToRead = fMaxSize/2; // because we're converting 8 bits->16 |
| if (bytesToRead > fInputBufferSize) { |
| delete[] fInputBuffer; fInputBuffer = new unsigned char[bytesToRead]; |
| fInputBufferSize = bytesToRead; |
| } |
| |
| // Arrange to read samples into the input buffer: |
| fInputSource->getNextFrame(fInputBuffer, bytesToRead, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void PCMFromuLawAudioSource |
| ::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| PCMFromuLawAudioSource* source = (PCMFromuLawAudioSource*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| static u_int16_t linear16FromuLaw(unsigned char uLawByte) { |
| static int const exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; |
| uLawByte = ~uLawByte; |
| |
| Boolean sign = (uLawByte & 0x80) != 0; |
| unsigned char exponent = (uLawByte>>4) & 0x07; |
| unsigned char mantissa = uLawByte & 0x0F; |
| |
| u_int16_t result = exp_lut[exponent] + (mantissa << (exponent+3)); |
| if (sign) result = -result; |
| return result; |
| } |
| |
| void PCMFromuLawAudioSource |
| ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Translate uLaw samples (in the input buffer) |
| // into 16-bit PCM samples (in the output buffer), in host order. |
| unsigned numSamples = frameSize; |
| u_int16_t* outputSample = (u_int16_t*)fTo; |
| for (unsigned i = 0; i < numSamples; ++i) { |
| outputSample[i] = linear16FromuLaw(fInputBuffer[i]); |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numSamples*2; |
| fNumTruncatedBytes = numTruncatedBytes; |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |
| |
| |
| ////////// 16-bit values (in host order) -> 16-bit network order ////////// |
| |
| NetworkFromHostOrder16* NetworkFromHostOrder16 |
| ::createNew(UsageEnvironment& env, FramedSource* inputSource) { |
| return new NetworkFromHostOrder16(env, inputSource); |
| } |
| |
| NetworkFromHostOrder16 |
| ::NetworkFromHostOrder16(UsageEnvironment& env, |
| FramedSource* inputSource) |
| : FramedFilter(env, inputSource) { |
| } |
| |
| NetworkFromHostOrder16::~NetworkFromHostOrder16() { |
| } |
| |
| void NetworkFromHostOrder16::doGetNextFrame() { |
| // Arrange to read data directly into the client's buffer: |
| fInputSource->getNextFrame(fTo, fMaxSize, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void NetworkFromHostOrder16 |
| ::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| NetworkFromHostOrder16* source = (NetworkFromHostOrder16*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| void NetworkFromHostOrder16 |
| ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Translate the 16-bit values that we have just read from host |
| // to network order (in-place) |
| unsigned numValues = frameSize/2; |
| u_int16_t* value = (u_int16_t*)fTo; |
| for (unsigned i = 0; i < numValues; ++i) { |
| value[i] = htons(value[i]); |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numValues*2; |
| fNumTruncatedBytes = numTruncatedBytes; |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |
| |
| |
| ////////// 16-bit values (in network order) -> 16-bit host order ////////// |
| |
| HostFromNetworkOrder16* HostFromNetworkOrder16 |
| ::createNew(UsageEnvironment& env, FramedSource* inputSource) { |
| return new HostFromNetworkOrder16(env, inputSource); |
| } |
| |
| HostFromNetworkOrder16 |
| ::HostFromNetworkOrder16(UsageEnvironment& env, |
| FramedSource* inputSource) |
| : FramedFilter(env, inputSource) { |
| } |
| |
| HostFromNetworkOrder16::~HostFromNetworkOrder16() { |
| } |
| |
| void HostFromNetworkOrder16::doGetNextFrame() { |
| // Arrange to read data directly into the client's buffer: |
| fInputSource->getNextFrame(fTo, fMaxSize, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void HostFromNetworkOrder16 |
| ::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| HostFromNetworkOrder16* source = (HostFromNetworkOrder16*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| void HostFromNetworkOrder16 |
| ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Translate the 16-bit values that we have just read from network |
| // to host order (in-place): |
| unsigned numValues = frameSize/2; |
| u_int16_t* value = (u_int16_t*)fTo; |
| for (unsigned i = 0; i < numValues; ++i) { |
| value[i] = ntohs(value[i]); |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numValues*2; |
| fNumTruncatedBytes = numTruncatedBytes; |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |
| |
| |
| ////////// 16-bit values: little-endian <-> big-endian ////////// |
| |
| EndianSwap16* |
| EndianSwap16::createNew(UsageEnvironment& env, FramedSource* inputSource) { |
| return new EndianSwap16(env, inputSource); |
| } |
| |
| EndianSwap16::EndianSwap16(UsageEnvironment& env, |
| FramedSource* inputSource) |
| : FramedFilter(env, inputSource) { |
| } |
| |
| EndianSwap16::~EndianSwap16() { |
| } |
| |
| void EndianSwap16::doGetNextFrame() { |
| // Arrange to read data directly into the client's buffer: |
| fInputSource->getNextFrame(fTo, fMaxSize, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void EndianSwap16::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| EndianSwap16* source = (EndianSwap16*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| void EndianSwap16::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Swap the byte order of the 16-bit values that we have just read (in place): |
| unsigned numValues = frameSize/2; |
| u_int16_t* value = (u_int16_t*)fTo; |
| for (unsigned i = 0; i < numValues; ++i) { |
| u_int16_t const orig = value[i]; |
| value[i] = ((orig&0xFF)<<8) | ((orig&0xFF00)>>8); |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numValues*2; |
| fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize); |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |
| |
| |
| ////////// 24-bit values: little-endian <-> big-endian ////////// |
| |
| EndianSwap24* |
| EndianSwap24::createNew(UsageEnvironment& env, FramedSource* inputSource) { |
| return new EndianSwap24(env, inputSource); |
| } |
| |
| EndianSwap24::EndianSwap24(UsageEnvironment& env, |
| FramedSource* inputSource) |
| : FramedFilter(env, inputSource) { |
| } |
| |
| EndianSwap24::~EndianSwap24() { |
| } |
| |
| void EndianSwap24::doGetNextFrame() { |
| // Arrange to read data directly into the client's buffer: |
| fInputSource->getNextFrame(fTo, fMaxSize, |
| afterGettingFrame, this, |
| FramedSource::handleClosure, this); |
| } |
| |
| void EndianSwap24::afterGettingFrame(void* clientData, unsigned frameSize, |
| unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| EndianSwap24* source = (EndianSwap24*)clientData; |
| source->afterGettingFrame1(frameSize, numTruncatedBytes, |
| presentationTime, durationInMicroseconds); |
| } |
| |
| void EndianSwap24::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, |
| struct timeval presentationTime, |
| unsigned durationInMicroseconds) { |
| // Swap the byte order of the 24-bit values that we have just read (in place): |
| unsigned const numValues = frameSize/3; |
| u_int8_t* p = fTo; |
| for (unsigned i = 0; i < numValues; ++i) { |
| u_int8_t tmp = p[0]; |
| p[0] = p[2]; |
| p[2] = tmp; |
| p += 3; |
| } |
| |
| // Complete delivery to the client: |
| fFrameSize = numValues*3; |
| fNumTruncatedBytes = numTruncatedBytes + (frameSize - fFrameSize); |
| fPresentationTime = presentationTime; |
| fDurationInMicroseconds = durationInMicroseconds; |
| afterGetting(this); |
| } |