session-ios/Signal/src/audio/incall_audio/SpeexCodec.m

176 lines
6.0 KiB
Objective-C

#import "Constraints.h"
#import "Environment.h"
#import "SpeexCodec.h"
@implementation SpeexCodec
#define MAX_FRAMES 512
#define DECODED_SAMPLE_SIZE_IN_BYTES 2
#define FRAME_SIZE_IN_SAMPLES 160
#define NUMBER_OF_CHANNELS 1
#define PERPETUAL_ENHANCER_PARAMETER 1
#define VARIABLE_BIT_RATE_PARAMETER 0
#define QUALITY_PARAMETER 3
#define COMPLEXITY_PARAMETER 1
+ (SpeexCodec *)speexCodec {
SpeexCodec *c = [SpeexCodec new];
c->logging = [Environment.logging getConditionLoggerForSender:self];
[c openSpeex];
return c;
}
- (void)dealloc {
[self closeSpeex];
}
+ (NSUInteger)frameSizeInSamples {
return FRAME_SIZE_IN_SAMPLES;
}
- (NSUInteger)encodedFrameSizeInBytes {
requireState(cachedEncodedLength != 0);
return cachedEncodedLength;
}
- (NSUInteger)decodedFrameSizeInBytes {
return FRAME_SIZE_IN_SAMPLES * DECODED_SAMPLE_SIZE_IN_BYTES;
}
- (void)determineDecodedLength {
NSData *encoded = [self encode:[NSMutableData dataWithLength:[self decodedFrameSizeInBytes]]];
cachedEncodedLength = encoded.length;
}
- (NSData *)encode:(NSData *)rawData {
ows_require(rawData != nil);
ows_require(rawData.length == FRAME_SIZE_IN_SAMPLES * DECODED_SAMPLE_SIZE_IN_BYTES);
speex_bits_reset(&encodingBits);
speex_encode_int(encodingState, (spx_int16_t *)[rawData bytes], &encodingBits);
NSMutableData *outputBuffer = [NSMutableData dataWithLength:encodingBufferSizeInBytes];
int outputSizeInBytes =
speex_bits_write(&encodingBits, [outputBuffer mutableBytes], (int)encodingBufferSizeInBytes);
checkOperation(outputSizeInBytes > 0);
[outputBuffer setLength:(NSUInteger)outputSizeInBytes];
return outputBuffer;
}
- (NSData *)decode:(NSData *)potentiallyMissingEncodedData {
NSUInteger encodedDataLength = potentiallyMissingEncodedData.length;
if (potentiallyMissingEncodedData == nil) {
encodedDataLength = [self decodedFrameSizeInBytes]; // size for infering audio data
}
if (encodedDataLength == 0)
return nil;
SpeexBits *dbits = [self getSpeexBitsFromData:potentiallyMissingEncodedData andDataLength:(int)encodedDataLength];
int decodedLength = [self decodeSpeexBits:dbits withLength:(int)encodedDataLength];
return [NSData dataWithBytes:decodingBuffer length:(NSUInteger)decodedLength * sizeof(spx_int16_t)];
}
- (NSUInteger)encodedDataLengthFromData:(NSData *)potentiallyMissingEncodedData {
if (potentiallyMissingEncodedData != nil) {
return potentiallyMissingEncodedData.length;
}
return [self decodedFrameSizeInBytes];
}
- (SpeexBits *)getSpeexBitsFromData:(NSData *)encodedData andDataLength:(int)encodedDataLength {
SpeexBits *dbits = NULL;
char *encodingStream;
if ([encodedData bytes] != NULL) {
encodingStream = (char *)[encodedData bytes];
speex_bits_read_from(&decodingBits, encodingStream, encodedDataLength);
dbits = &decodingBits;
}
return dbits;
}
- (int)decodeSpeexBits:(SpeexBits *)dbits withLength:(int)encodedDataLength {
int decodingBufferIndex = 0;
int decodingBufferLength = (int)[self decodedFrameSizeInBytes];
int count = 0;
while (0 == speex_decode_int(decodingState, dbits, decodingBuffer + decodingBufferIndex)) {
count++;
decodingBufferIndex += decodingFrameSize;
if (decodingBufferIndex + decodingFrameSize > decodingBufferLength) {
[logging
logWarning:[NSString
stringWithFormat:@"out of space in the decArr buffer, idx=%d, frameSize=%d, length=%d",
decodingBufferIndex,
decodingFrameSize,
decodingBufferLength]];
break;
}
if (decodingBufferIndex + decodingFrameSize > decodingFrameSize * MAX_FRAMES) {
[logging logWarning:[NSString stringWithFormat:@"out of space in the dec_buffer buffer, idx=%d",
decodingBufferIndex]];
break;
}
if (dbits == NULL) {
break;
}
}
return decodingBufferIndex;
}
- (void)openSpeex {
[self initiateEncoderAndDecoder];
[self applySpeexSettings];
[self initiateSpeexBuffers];
[self determineDecodedLength];
}
- (void)initiateEncoderAndDecoder {
encodingState = speex_encoder_init(&speex_nb_mode);
decodingState = speex_decoder_init(&speex_nb_mode);
checkOperationDescribe(encodingState != NULL, @"speex encoder init failed");
checkOperationDescribe(decodingState != NULL, @"speex decoder init failed");
}
- (void)applySpeexSettings {
spx_int32_t tmp;
tmp = PERPETUAL_ENHANCER_PARAMETER;
speex_decoder_ctl(decodingState, SPEEX_SET_ENH, &tmp);
tmp = VARIABLE_BIT_RATE_PARAMETER;
speex_encoder_ctl(encodingState, SPEEX_SET_VBR, &tmp);
tmp = QUALITY_PARAMETER;
speex_encoder_ctl(encodingState, SPEEX_SET_QUALITY, &tmp);
tmp = COMPLEXITY_PARAMETER;
speex_encoder_ctl(encodingState, SPEEX_SET_COMPLEXITY, &tmp);
speex_encoder_ctl(encodingState, SPEEX_GET_FRAME_SIZE, &encodingFrameSize);
speex_decoder_ctl(decodingState, SPEEX_GET_FRAME_SIZE, &decodingFrameSize);
int sampleRate = (int)SAMPLE_RATE;
speex_encoder_ctl(encodingState, SPEEX_SET_SAMPLING_RATE, &sampleRate);
speex_decoder_ctl(decodingState, SPEEX_SET_SAMPLING_RATE, &sampleRate);
}
- (void)initiateSpeexBuffers {
speex_bits_init(&encodingBits);
speex_bits_init(&decodingBits);
encodingBufferSizeInBytes = (NSUInteger)encodingFrameSize * MAX_FRAMES;
decodingBuffer = (spx_int16_t *)malloc(sizeof(spx_int16_t) * (NSUInteger)decodingFrameSize * MAX_FRAMES);
checkOperationDescribe(decodingBuffer != NULL, @"buffer allocation failed");
}
- (void)closeSpeex {
terminated = true;
speex_encoder_destroy(encodingState);
speex_decoder_destroy(decodingState);
speex_bits_destroy(&encodingBits);
speex_bits_destroy(&decodingBits);
free(decodingBuffer);
}
@end