Remove all external dependencies and md380 vocoder

This commit is contained in:
Doug McLain committed 2026-10-29 15:01:38 -04:00
1 parent 31317ec39f
commit a37a025626
103 files changed
+28368 -99

No files matched your search

+15 -17
View File
@@ -23,7 +23,6 @@
#include <fstream>
#include <thread>
#include <queue>
#include <md380_vocoder.h>
#include "TranscoderPacket.h"
#include "Controller.h"
#include "Configure.h"
@@ -54,7 +53,7 @@ bool CController::Start()
imbeFuture = std::async(std::launch::async, &CController::ProcessIMBEThread, this);
ambeFuture = std::async(std::launch::async, &CController::ProcessAMBEThread, this);
usrpFuture = std::async(std::launch::async, &CController::ProcessUSRPThread, this);
swambe2Future = std::async(std::launch::async, &CController::ProcessSWAMBE2Thread,this);
ambe2Future = std::async(std::launch::async, &CController::ProcessAMBE2Thread, this);
return false;
}
@@ -80,7 +79,6 @@ bool CController::InitVocoders()
c2_32[c] = std::unique_ptr<CCodec2>(new CCodec2(true));
}
md380_init();
ambe_in_num = calcNumerator(g_Conf.GetGain(EGainType::dmrin));
ambe_out_num = calcNumerator(g_Conf.GetGain(EGainType::dmrout));
dstar_in_num = calcNumerator(g_Conf.GetGain(EGainType::dstarin));
@@ -112,7 +110,7 @@ void CController::ReadReflectorThread()
ambe_queue.push(packet);
break;
case ECodecType::dmr:
swambe2_queue.push(packet);
ambe2_queue.push(packet);
break;
case ECodecType::p25:
imbe_queue.push(packet);
@@ -213,7 +211,7 @@ void CController::Codec2toAudio(std::shared_ptr<CTranscoderPacket> packet)
}
// the only thing left is to encode the two ambe, so push the packet onto both AMBE queues
ambe_queue.push(packet);
swambe2_queue.push(packet);
ambe2_queue.push(packet);
imbe_queue.push(packet);
usrp_queue.push(packet);
}
@@ -279,7 +277,7 @@ void CController::AMBEtoAudio(std::shared_ptr<CTranscoderPacket> packet)
}
packet->SetAudioSamples(tmp, false);
codec2_queue.push(packet);
swambe2_queue.push(packet);
ambe2_queue.push(packet);
imbe_queue.push(packet);
usrp_queue.push(packet);
}
@@ -307,7 +305,7 @@ void CController::ProcessAMBEThread()
}
}
void CController::AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> packet)
void CController::AudiotoAMBE2(std::shared_ptr<CTranscoderPacket> packet)
{
uint8_t ambe[9];
const int16_t *p = packet->GetAudioSamples();
@@ -317,10 +315,10 @@ void CController::AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> packet)
int16_t tmp[160];
for(int i = 0; i < 160; ++i)
tmp[i] = int16_t((p[i] * ambe_in_num) >> 8);
md380_encode_fec(ambe, tmp);
ambevocoder.encode_2450x1150(tmp, ambe);
}
else
md380_encode_fec(ambe, (int16_t *)p);
ambevocoder.encode_2450x1150((int16_t *)p, ambe);
packet->SetDMRData(ambe);
@@ -330,11 +328,11 @@ void CController::AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> packet)
send_mux.unlock();
}
void CController::SWAMBE2toAudio(std::shared_ptr<CTranscoderPacket> packet)
void CController::AMBE2toAudio(std::shared_ptr<CTranscoderPacket> packet)
{
int16_t tmp[160];
uint8_t ambe[9];
md380_decode_fec((uint8_t *)packet->GetDMRData(), tmp);
ambevocoder.decode_2450x1150(tmp, (uint8_t *)packet->GetDMRData());
if (ambe_out_num != 256)
{
for (int i=0; i<160; i++)
@@ -347,11 +345,11 @@ void CController::SWAMBE2toAudio(std::shared_ptr<CTranscoderPacket> packet)
usrp_queue.push(packet);
}
void CController::ProcessSWAMBE2Thread()
void CController::ProcessAMBE2Thread()
{
while (keep_running)
{
auto packet = swambe2_queue.pop();
auto packet = ambe2_queue.pop();
switch (packet->GetCodecIn())
{
@@ -360,11 +358,11 @@ void CController::ProcessSWAMBE2Thread()
case ECodecType::dstar:
case ECodecType::p25:
case ECodecType::usrp:
AudiotoSWAMBE2(packet);
AudiotoAMBE2(packet);
break;
case ECodecType::dmr:
SWAMBE2toAudio(packet);
AMBE2toAudio(packet);
break;
}
}
@@ -389,7 +387,7 @@ void CController::IMBEtoAudio(std::shared_ptr<CTranscoderPacket> packet)
packet->SetAudioSamples(tmp, false);
ambe_queue.push(packet);
codec2_queue.push(packet);
swambe2_queue.push(packet);
ambe2_queue.push(packet);
usrp_queue.push(packet);
}
@@ -453,7 +451,7 @@ void CController::USRPtoAudio(std::shared_ptr<CTranscoderPacket> packet)
codec2_queue.push(packet);
ambe_queue.push(packet);
swambe2_queue.push(packet);
ambe2_queue.push(packet);
imbe_queue.push(packet);
}
+5 -6
View File
@@ -24,7 +24,6 @@
#include <mutex>
#include <list>
#include <utility>
#include <imbe_vocoder_api.h>
#include "PacketQueue.h"
#include "codec2.h"
#include "TCSocket.h"
@@ -42,7 +41,7 @@ public:
protected:
std::atomic<bool> keep_running;
std::future<void> reflectorFuture, c2Future, imbeFuture, ambeFuture, swambe2Future, usrpFuture;
std::future<void> reflectorFuture, c2Future, imbeFuture, ambeFuture, ambe2Future, usrpFuture;
std::unordered_map<char, int16_t[160]> audio_store;
std::unordered_map<char, uint8_t[8]> data_store;
CTCClient tcClient;
@@ -51,7 +50,7 @@ protected:
CPacketQueue codec2_queue;
CPacketQueue imbe_queue;
CPacketQueue ambe_queue;
CPacketQueue swambe2_queue;
CPacketQueue ambe2_queue;
CPacketQueue usrp_queue;
std::mutex send_mux;
int32_t dstar_in_num, dstar_out_num, ambe_in_num, ambe_out_num, usrp_rx_num, usrp_tx_num;
@@ -67,7 +66,7 @@ protected:
void ProcessIMBEThread();
void ProcessAMBEThread();
void ProcessSWAMBE2Thread();
void ProcessAMBE2Thread();
void ProcessUSRPThread();
void Codec2toAudio(std::shared_ptr<CTranscoderPacket> packet);
void AudiotoCodec2(std::shared_ptr<CTranscoderPacket> packet);
@@ -78,6 +77,6 @@ protected:
void USRPtoAudio(std::shared_ptr<CTranscoderPacket> packet);
void AudiotoUSRP(std::shared_ptr<CTranscoderPacket> packet);
void SendToReflector(std::shared_ptr<CTranscoderPacket> packet);
void SWAMBE2toAudio(std::shared_ptr<CTranscoderPacket> packet);
void AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> packet);
void AMBE2toAudio(std::shared_ptr<CTranscoderPacket> packet);
void AudiotoAMBE2(std::shared_ptr<CTranscoderPacket> packet);
};
+2 -2
View File
@@ -10,9 +10,9 @@ else
CFLAGS = -W -Werror -Icodec2 -MMD -MD -std=c++17
endif
LDFLAGS = -limbe_vocoder -lmd380_vocoder -pthread
LDFLAGS = -pthread
SRCS = $(wildcard *.cpp) $(wildcard codec2/*.cpp) $(wildcard mbe/*.cpp)
SRCS = $(wildcard *.cpp) $(wildcard codec2/*.cpp) $(wildcard imbe_vocoder/*.cpp) $(wildcard mbe/*.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
EXE = tcd
+1 -5
View File
@@ -44,11 +44,7 @@ Use your favorite text editor to edit the following files:
## Transcoding options
The md380_vocoder library for arm32 linux OS is located here:
https://github.com/nostar/md380_vocoder
The md380_vocoder library for all Intel/AMD 64-bit and ARM 64-bit platforms (including Android) is located here:
https://github.com/nostar/md380_vocoder_dynarmic
This transcoder is all software with no external library requirements, and no hardware requirements. Since there are no hardware based module restrictions, all 26 modules can be transcoded.
## Installing *tcd* when the transcoder is local
+184
View File
@@ -0,0 +1,184 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "aux_sub.h"
#include "tbls.h"
//-----------------------------------------------------------------------------
// PURPOSE:
// Return pointer to bit allocation array
// according to the number of harmonics
//
// INPUT:
// num_harms - The number of harmonics
//
// OUTPUT:
// None
//
// RETURN:
// Pointer to bits allocation array
//
//-----------------------------------------------------------------------------
const UWord16 *get_bit_allocation_arr(Word16 num_harms)
{
UWord16 offset_in_word;
Word16 index;
if(num_harms == NUM_HARMS_MIN)
return &bit_allocation_tbl[0];
else
{
index = num_harms - NUM_HARMS_MIN - 1;
offset_in_word = bit_allocation_offset_tbl[index >> 2] + ((3 + (index >> 2)) * (index & 0x3));
return &bit_allocation_tbl[offset_in_word];
}
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Unpack bit allocation table's item
//
// INPUT:
// num_harms - The number of harmonics
// ptr - Pointer to buffer to place bit allocation data
//
// OUTPUT:
// Unpacked bit allocation table
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void get_bit_allocation(Word16 num_harms, Word16 *ptr)
{
const UWord16 *bat_ptr;
UWord16 tmp;
Word16 i;
bat_ptr = get_bit_allocation_arr(num_harms);
for(i = 0; i < num_harms - 1; i += 4)
{
tmp = *bat_ptr++;
ptr[3] = tmp & 0xF; tmp >>= 4;
ptr[2] = tmp & 0xF; tmp >>= 4;
ptr[1] = tmp & 0xF; tmp >>= 4;
ptr[0] = tmp & 0xF;
ptr += 4;
}
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Set the elements of a 16 bit input vector to zero.
//
// INPUT:
// vec - Pointer to vector
// n - size of vec
//
// OUTPUT:
// None
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_zap(Word16 *vec, Word16 n)
{
while(n--)
*vec++ = 0;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Copy the contents of one 16 bit input vector to another
//
// INPUT:
// vec1 - Pointer to the destination vector
// vec2 - Pointer to the source vector
// n - size of data should be copied
//
// OUTPUT:
// Copy of the source vector
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_equ(Word16 *vec1, Word16 *vec2, Word16 n)
{
while(n--)
*vec1++ = *vec2++;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Compute the sum of square magnitude of a 16 bit input vector
// with saturation and truncation. Output is a 32 bit number.
//
// INPUT:
// vec - Pointer to the vector
// n - size of input vectors
//
// OUTPUT:
// none
//
// RETURN:
// 32 bit long signed integer result
//
//-----------------------------------------------------------------------------
Word32 L_v_magsq(Word16 *vec, Word16 n)
{
Word32 L_magsq = 0;
while(n--)
{
L_magsq = L_mac(L_magsq, *vec, *vec);
vec++;
}
return L_magsq;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Copy the contents of one 16 bit input vector to another with shift
//
// INPUT:
// vec1 - Pointer to the destination vector
// vec2 - Pointer to the source vector
// scale - right shift factor
// n - size of data should be copied
//
// OUTPUT:
// Copy of the source vector
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_equ_shr(Word16 *vec1, Word16 *vec2, Word16 scale, Word16 n)
{
while(n--)
*vec1++ = shr(*vec2++,scale);
}
+133
View File
@@ -0,0 +1,133 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _AUX_SUB
#define _AUX_SUB
//-----------------------------------------------------------------------------
// PURPOSE:
// Return pointer to bit allocation array
// according to the number of harmonics
//
// INPUT:
// num_harms - The number of harmonics
//
// OUTPUT:
// None
//
// RETURN:
// Pointer to bits allocation array
//
//-----------------------------------------------------------------------------
const UWord16 *get_bit_allocation_arr(Word16 num_harms);
//-----------------------------------------------------------------------------
// PURPOSE:
// Unpack bit allocation table's item
//
// INPUT:
// num_harms - The number of harmonics
// ptr - Pointer to buffer to place bit allocation data
//
// OUTPUT:
// Unpacked bit allocation table
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void get_bit_allocation(Word16 num_harms, Word16 *ptr);
//-----------------------------------------------------------------------------
// PURPOSE:
// Set the elements of a 16 bit input vector to zero.
//
// INPUT:
// vec - Pointer to vector
// n - size of vec
//
// OUTPUT:
// None
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_zap(Word16 *vec, Word16 n);
//-----------------------------------------------------------------------------
// PURPOSE:
// Copy the contents of one 16 bit input vector to another
//
// INPUT:
// vec1 - Pointer to the destination vector
// vec2 - Pointer to the source vector
// n - size of data should be copied
//
// OUTPUT:
// Copy of the source vector
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_equ(Word16 *vec1, Word16 *vec2, Word16 n);
//-----------------------------------------------------------------------------
// PURPOSE:
// Compute the sum of square magnitude of a 16 bit input vector
// with saturation and truncation. Output is a 32 bit number.
//
// INPUT:
// vec - Pointer to the vector
// n - size of input vectors
//
// OUTPUT:
// none
//
// RETURN:
// 32 bit long signed integer result
//
//-----------------------------------------------------------------------------
Word32 L_v_magsq(Word16 *vec, Word16 n);
//-----------------------------------------------------------------------------
// PURPOSE:
// Copy the contents of one 16 bit input vector to another with shift
//
// INPUT:
// vec1 - Pointer to the destination vector
// vec2 - Pointer to the source vector
// scale - right shift factor
// n - size of data should be copied
//
// OUTPUT:
// Copy of the source vector
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void v_equ_shr(Word16 *vec1, Word16 *vec2, Word16 scale, Word16 n);
#endif
+85
View File
@@ -0,0 +1,85 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
/*___________________________________________________________________________
| |
| Constants and Globals |
| |
| $Id $
|___________________________________________________________________________|
*/
extern Flag Overflow;
extern Flag Carry;
#define MAX_32 (Word32)0x7fffffffL
#define MIN_32 (Word32)0x80000000L
#define MAX_16 (Word16)0x7fff
#define MIN_16 (Word16)0x8000
/*___________________________________________________________________________
| |
| Prototypes for basic arithmetic operators |
|___________________________________________________________________________|
*/
Word16 add (Word16 var1, Word16 var2); /* Short add, 1 */
Word16 sub (Word16 var1, Word16 var2); /* Short sub, 1 */
Word16 abs_s (Word16 var1); /* Short abs, 1 */
Word16 shl (Word16 var1, Word16 var2); /* Short shift left, 1 */
Word16 shr (Word16 var1, Word16 var2); /* Short shift right, 1 */
Word16 mult (Word16 var1, Word16 var2); /* Short mult, 1 */
Word32 L_mult (Word16 var1, Word16 var2); /* Long mult, 1 */
Word16 negate (Word16 var1); /* Short negate, 1 */
Word16 extract_h (Word32 L_var1); /* Extract high, 1 */
Word16 extract_l (Word32 L_var1); /* Extract low, 1 */
Word16 round (Word32 L_var1); /* Round, 1 */
Word32 L_mac (Word32 L_var3, Word16 var1, Word16 var2); /* Mac, 1 */
Word32 L_msu (Word32 L_var3, Word16 var1, Word16 var2); /* Msu, 1 */
Word32 L_macNs (Word32 L_var3, Word16 var1, Word16 var2); /* Mac without
sat, 1 */
Word32 L_msuNs (Word32 L_var3, Word16 var1, Word16 var2); /* Msu without
sat, 1 */
Word32 L_add (Word32 L_var1, Word32 L_var2); /* Long add, 2 */
Word32 L_sub (Word32 L_var1, Word32 L_var2); /* Long sub, 2 */
Word32 L_add_c (Word32 L_var1, Word32 L_var2); /* Long add with c, 2 */
Word32 L_sub_c (Word32 L_var1, Word32 L_var2); /* Long sub with c, 2 */
Word32 L_negate (Word32 L_var1); /* Long negate, 2 */
Word16 mult_r (Word16 var1, Word16 var2); /* Mult with round, 2 */
Word32 L_shl (Word32 L_var1, Word16 var2); /* Long shift left, 2 */
Word32 L_shr (Word32 L_var1, Word16 var2); /* Long shift right, 2*/
Word16 shr_r (Word16 var1, Word16 var2); /* Shift right with
round, 2 */
Word16 mac_r (Word32 L_var3, Word16 var1, Word16 var2); /* Mac with
rounding,2 */
Word16 msu_r (Word32 L_var3, Word16 var1, Word16 var2); /* Msu with
rounding,2 */
Word32 L_deposit_h (Word16 var1); /* 16 bit var1 -> MSB, 2 */
Word32 L_deposit_l (Word16 var1); /* 16 bit var1 -> LSB, 2 */
Word32 L_shr_r (Word32 L_var1, Word16 var2); /* Long shift right with
round, 3 */
Word32 L_abs (Word32 L_var1); /* Long abs, 3 */
Word32 L_sat (Word32 L_var1); /* Long saturation, 4 */
Word16 norm_s (Word16 var1); /* Short norm, 15 */
Word16 div_s (Word16 var1, Word16 var2); /* Short division, 18 */
Word16 norm_l (Word32 L_var1); /* Long norm, 30 */
File diff suppressed because it is too large. Load diff
+185
View File
@@ -0,0 +1,185 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "ch_decode.h"
#include "aux_sub.h"
void decode_frame_vector(IMBE_PARAM *imbe_param, Word16 *frame_vector)
{
Word16 bit_stream[BIT_STREAM_LEN];
Word16 i, vec_num, tmp, tmp1, tmp2, bit_thr, shift;
Word16 *b_ptr, *ba_ptr, index0;
Word32 L_tmp;
imbe_param->b_vec[0] = (shr(frame_vector[0], 4) & 0xFC) | (shr(frame_vector[7], 1) & 0x3);
if (imbe_param->b_vec[0] < 0 || imbe_param->b_vec[0] > 207)
return; // If we return here IMBE parameters from previous frame will be used (frame repeating)
tmp = ((imbe_param->b_vec[0] & 0xFF) << 1) + 0x4F; // Convert b_vec[0] to unsigned Q15.1 format and add 39.5
//imbe_param->ff = 4./((double)imbe_param->b_vec[0] + 39.5);
// Calculate fundamental frequency with higher precession
shift = norm_s(tmp);
tmp1 = tmp << shift;
tmp2 = div_s(0x4000, tmp1);
imbe_param->fund_freq = L_shr(L_deposit_h(tmp2), 11 - shift);
L_tmp = L_sub(0x40000000, L_mult(tmp1, tmp2));
tmp2 = div_s(extract_l(L_shr(L_tmp, 2)), tmp1);
L_tmp = L_shr(L_deposit_l(tmp2), 11 - shift - 2);
imbe_param->fund_freq = L_add(imbe_param->fund_freq, L_tmp);
//printf("%X %X \n", imbe_param->fund_freq, (Word32)(imbe_param->ff * (double)((UWord32)1<<31)));
tmp = (tmp + 0x2) >> 3; // Calculate (b0 + 39.5 + 1)/4
imbe_param->num_harms = ((UWord32)CNST_0_9254_Q0_16 * tmp) >> 16;
if(imbe_param->num_harms <= 36)
imbe_param->num_bands = extract_h((UWord32)(imbe_param->num_harms + 2) * CNST_0_33_Q0_16); // fix((L+2)/3)
else
imbe_param->num_bands = NUM_BANDS_MAX;
// Convert input vector (from b_3 to b_L+1) to bit stream
bit_stream[0] = (frame_vector[0] & 0x4)?1:0;
bit_stream[1] = (frame_vector[0] & 0x2)?1:0;
bit_stream[2] = (frame_vector[0] & 0x1)?1:0;
bit_stream[BIT_STREAM_LEN - 3] = (frame_vector[7] & 0x40)?1:0;
bit_stream[BIT_STREAM_LEN - 2] = (frame_vector[7] & 0x20)?1:0;
bit_stream[BIT_STREAM_LEN - 1] = (frame_vector[7] & 0x10)?1:0;
index0 = 3 + 3 * 12 - 1;
for(vec_num = 3; vec_num >= 1; vec_num--)
{
tmp = frame_vector[vec_num];
for(i = 0; i < 12; i++)
{
bit_stream[index0] = (tmp & 0x1)?1:0;
tmp >>= 1;
index0--;
}
}
index0 = 3 + 3 * 12 + 3 * 11 - 1;
for(vec_num = 6; vec_num >= 4; vec_num--)
{
tmp = frame_vector[vec_num];
for(i = 0; i < 11; i++)
{
bit_stream[index0] = (tmp & 0x1)?1:0;
tmp >>= 1;
index0--;
}
}
// Rebuild b1
index0 = 3 + 3 * 12;
tmp = 0;
for(i = 0; i < imbe_param->num_bands; i++)
tmp = (tmp << 1) | bit_stream[index0++];
imbe_param->b_vec[1] = tmp;
// Rebuild b2
tmp = 0;
tmp |= bit_stream[index0++] << 1;
tmp |= bit_stream[index0++];
imbe_param->b_vec[2] = (frame_vector[0] & 0x38) | (tmp << 1) | (shr(frame_vector[7], 3) & 0x01);
// Shift the rest of sequence
tmp = imbe_param->num_bands + 2; // shift
for(; index0 < BIT_STREAM_LEN; index0++)
bit_stream[index0 - tmp] = bit_stream[index0];
// Priority ReScanning
b_ptr = &imbe_param->b_vec[3];
ba_ptr = imbe_param->bit_alloc;
for(i = 0; i < B_NUM; i++)
ba_ptr[i] = b_ptr[i] = 0;
// Unpack bit allocation table's item
get_bit_allocation(imbe_param->num_harms, imbe_param->bit_alloc);
index0 = 0;
bit_thr = (imbe_param->num_harms == 0xb)?9:ba_ptr[0];
while(index0 < BIT_STREAM_LEN - imbe_param->num_bands - 2)
{
for(i = 0; i < imbe_param->num_harms - 1; i++)
if(bit_thr && bit_thr <= ba_ptr[i])
b_ptr[i] = (b_ptr[i] << 1) | bit_stream[index0++];
bit_thr--;
}
// Synchronization Bit Decoding
imbe_param->b_vec[imbe_param->num_harms + 2] = frame_vector[7] & 1;
}
void v_uv_decode(IMBE_PARAM *imbe_param)
{
Word16 num_harms;
Word16 num_bands;
Word16 vu_vec, *p_v_uv_dsn, mask, i, uv_cnt;
num_harms = imbe_param->num_harms;
num_bands = imbe_param->num_bands;
vu_vec = imbe_param->b_vec[1];
p_v_uv_dsn = imbe_param->v_uv_dsn;
mask = 1 << (num_bands - 1);
v_zap(p_v_uv_dsn, NUM_HARMS_MAX);
i = 0; uv_cnt = 0;
while(num_harms--)
{
if(vu_vec & mask)
*p_v_uv_dsn++ = 1;
else
{
*p_v_uv_dsn++ = 0;
uv_cnt++;
}
if(++i == 3)
{
if(num_bands > 1)
{
num_bands--;
mask >>= 1;
}
i = 0;
}
}
imbe_param->l_uv = uv_cnt;
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _CH_DECODE
#define _CH_DECODE
#include "typedef.h"
#define BIT_STREAM_LEN (3 + 3*12 + 3*11 + 3)
void decode_frame_vector(IMBE_PARAM *imbe_param, Word16 *frame_vector);
void v_uv_decode(IMBE_PARAM *imbe_param);
#endif
+110
View File
@@ -0,0 +1,110 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "ch_encode.h"
#include "aux_sub.h"
void encode_frame_vector(IMBE_PARAM *imbe_param, Word16 *frame_vector)
{
Word16 bit_stream[EN_BIT_STREAM_LEN], index0, bit_thr, bit_mask, i;
Word16 vec_num, num_harms, num_bands, tmp;
Word16 *ba_ptr, *b_ptr;
num_harms = imbe_param->num_harms;
num_bands = imbe_param->num_bands;
v_zap(frame_vector, 8);
// Unpack bit allocation table's item
get_bit_allocation(num_harms, imbe_param->bit_alloc);
// Priority Scanning
index0 = 0;
ba_ptr = imbe_param->bit_alloc;
bit_thr = (num_harms == 0xb)?9:ba_ptr[0];
bit_mask = shl(1, bit_thr - 1);
while(index0 < EN_BIT_STREAM_LEN - (num_bands - 3))
{
b_ptr = &imbe_param->b_vec[3];
for(i = 0; i < num_harms - 1; i++)
if(bit_thr && bit_thr <= ba_ptr[i])
bit_stream[index0++] = (b_ptr[i] & bit_mask)?1:0;
bit_thr--;
bit_mask = shr(bit_mask, 1);
}
frame_vector[0] = shl(imbe_param->b_vec[0] & 0xFC, 4) | (imbe_param->b_vec[2] & 0x38);
index0 = 0;
frame_vector[0] |= (bit_stream[index0++])?4:0;
frame_vector[0] |= (bit_stream[index0++])?2:0;
frame_vector[0] |= (bit_stream[index0++])?1:0;
for(vec_num = 1; vec_num <= 3; vec_num++)
{
tmp = 0;
for(i = 0; i < 12; i++)
{
tmp <<= 1;
tmp |= bit_stream[index0++];
}
frame_vector[vec_num] = tmp;
}
index0 -= num_bands + 2;
bit_mask = shl(1, num_bands - 1);
for(i = 0; i < num_bands; i++)
{
bit_stream[index0++] = (imbe_param->b_vec[1] & bit_mask)?1:0;
bit_mask >>= 1;
}
bit_stream[index0++] = (imbe_param->b_vec[2] & 0x04)?1:0;
bit_stream[index0++] = (imbe_param->b_vec[2] & 0x02)?1:0;
index0 -= num_bands + 2;
for(vec_num = 4; vec_num <= 6; vec_num++)
{
tmp = 0;
for(i = 0; i < 11; i++)
{
tmp <<= 1;
tmp |= bit_stream[index0++];
}
frame_vector[vec_num] = tmp;
}
frame_vector[7] = shl(imbe_param->b_vec[0] & 0x03, 1) | shl(imbe_param->b_vec[2] & 0x01, 3);
frame_vector[7] |= (bit_stream[index0++])?0x40:0;
frame_vector[7] |= (bit_stream[index0++])?0x20:0;
frame_vector[7] |= (bit_stream[index0++])?0x10:0;
frame_vector[7] |= (imbe_param->b_vec[num_harms + 2])?0x01:0;
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _CH_ENCODE
#define _CH_ENCODE
#define EN_BIT_STREAM_LEN (3 + 3*12 + 6 + 2*11 + 3)
void encode_frame_vector(IMBE_PARAM *imbe_param, Word16 *frame_vector);
#endif
+66
View File
@@ -0,0 +1,66 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "aux_sub.h"
#include "basic_op.h"
#include "math_sub.h"
#include "dc_rmv.h"
#define CNST_0_99_Q1_15 0x7EB8
//-----------------------------------------------------------------------------
// PURPOSE:
// High-pass filter to remove DC
//
//
// INPUT:
// *sigin - pointer to input signal buffer
// *sigout - pointer to output signal buffer
// *mem - pointer to filter's memory element
// len - number of input signal samples
//
// OUTPUT:
// None
//
// RETURN:
// Saved filter state in mem
//
//-----------------------------------------------------------------------------
void dc_rmv(Word16 *sigin, Word16 *sigout, Word32 *mem, Word16 len)
{
Word32 L_tmp, L_mem;
L_mem = *mem;
while(len--)
{
L_tmp = L_deposit_h(*sigin++);
L_mem = L_add(L_mem, L_tmp);
*sigout++ = round(L_mem);
L_mem = L_mpy_ls(L_mem, CNST_0_99_Q1_15);
L_mem = L_sub(L_mem, L_tmp);
}
*mem = L_mem;
}
+46
View File
@@ -0,0 +1,46 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _DC_RMV
#define _DC_RMV
//-----------------------------------------------------------------------------
// PURPOSE:
// High-pass filter to remove DC
//
//
// INPUT:
// *sigin - pointer to input signal buffer
// *sigout - pointer to output signal buffer
// *mem - pointer to filter's memory element
// len - number of input signal samples
//
// OUTPUT:
// None
//
// RETURN:
// Saved filter state in mem
//
//-----------------------------------------------------------------------------
void dc_rmv(Word16 *sigin, Word16 *sigout, Word32 *mem, Word16 len);
#endif
+112
View File
@@ -0,0 +1,112 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "ch_decode.h"
#include "sa_decode.h"
#include "sa_enh.h"
#include "v_synt.h"
#include "uv_synt.h"
#include "basic_op.h"
#include "aux_sub.h"
#include "encode.h"
#include "dsp_sub.h"
#include "imbe_vocoder.h"
#include <cstring>
const uint8_t BIT_MASK_TABLE8[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE8[(i)&7])
void imbe_vocoder::decode_init(IMBE_PARAM *imbe_param)
{
v_synt_init();
uv_synt_init();
sa_decode_init();
// Make previous frame for the first frame
memset((char *)imbe_param, 0, sizeof(IMBE_PARAM));
imbe_param->fund_freq = 0x0cf6474a;
imbe_param->num_harms = 9;
imbe_param->num_bands = 3;
}
void imbe_vocoder::decode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd)
{
Word16 snd_tmp[FRAME];
Word16 j;
decode_frame_vector(imbe_param, frame_vector);
v_uv_decode(imbe_param);
sa_decode(imbe_param);
sa_enh(imbe_param);
v_synt(imbe_param, snd);
uv_synt(imbe_param, snd_tmp);
for(j = 0; j < FRAME; j++)
snd[j] = add(snd[j], snd_tmp[j]);
}
void imbe_vocoder::decode_4400(int16_t *snd, uint8_t *imbe)
{
int16_t frame[8U] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
unsigned int offset = 0U;
int16_t mask = 0x0800;
for (unsigned int i = 0U; i < 12U; i++, mask >>= 1, offset++)
frame[0U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0800;
for (unsigned int i = 0U; i < 12U; i++, mask >>= 1, offset++)
frame[1U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0800;
for (unsigned int i = 0U; i < 12U; i++, mask >>= 1, offset++)
frame[2U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0800;
for (unsigned int i = 0U; i < 12U; i++, mask >>= 1, offset++)
frame[3U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0400;
for (unsigned int i = 0U; i < 11U; i++, mask >>= 1, offset++)
frame[4U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0400;
for (unsigned int i = 0U; i < 11U; i++, mask >>= 1, offset++)
frame[5U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0400;
for (unsigned int i = 0U; i < 11U; i++, mask >>= 1, offset++)
frame[6U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
mask = 0x0040;
for (unsigned int i = 0U; i < 7U; i++, mask >>= 1, offset++)
frame[7U] |= READ_BIT(imbe, offset) != 0x00U ? mask : 0x0000;
imbe_decode(frame, snd);
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _DECODE
#define _DECODE
#if 0
void decode_init(IMBE_PARAM *imbe_param);
void decode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd);
void decode_4400(int16_t *pcm, uint8_t *imbe);
#endif
#endif
+278
View File
@@ -0,0 +1,278 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "tbls.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include "encode.h"
#include "imbe_vocoder.h"
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform inverse DCT
//
//
// INPUT:
// in - pointer to input data
// m_lim - input data's size
// i_lim - result's size
// out - pointer to save result
//
// OUTPUT:
// None
//
// RETURN:
// Saved in out result of conversion
//
//-----------------------------------------------------------------------------
void imbe_vocoder::idct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out)
{
UWord16 angl_step, angl_intl, angl_intl_2;
UWord16 angl_acc;
Word32 sum;
Word16 i, m;
if(m_lim == 1)
{
angl_intl = CNST_0_5_Q1_15;
angl_intl_2 = CNST_1_0_Q1_15;
}
else
{
angl_intl = div_s ((Word16) CNST_0_5_Q5_11, m_lim << 11); // calculate 0.5/m_lim
angl_intl_2 = shl(angl_intl, 1);
}
angl_step = angl_intl;
for(i = 0; i < i_lim; i++)
{
sum = 0;
angl_acc = angl_step;
for(m = 1; m < m_lim; m++)
{
sum = L_add(sum, L_shr( L_mult(in[m], cos_fxp(angl_acc)), 7));
angl_acc += angl_step;
}
sum = L_add(sum, L_shr( L_deposit_h(in[0]), 8));
out[i] = extract_l(L_shr_r (sum, 8));
angl_step += angl_intl_2;
}
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform DCT
//
//
// INPUT:
// in - pointer to input data
// m_lim - input data's size
// i_lim - result's size
// out - pointer to save result
//
// OUTPUT:
// None
//
// RETURN:
// Saved in out result of conversion
//
//-----------------------------------------------------------------------------
void imbe_vocoder::dct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out)
{
UWord16 angl_step, angl_intl, angl_intl_2, angl_begin;
UWord16 angl_acc;
Word32 sum;
Word16 i, m;
if(m_lim == 1)
{
angl_intl = CNST_0_5_Q1_15;
angl_intl_2 = CNST_1_0_Q1_15;
}
else
{
angl_intl = div_s ((Word16) CNST_0_5_Q5_11, m_lim << 11); // calculate 0.5/m_lim
angl_intl_2 = shl(angl_intl, 1);
}
// Calculate first coefficient
sum = 0;
for(m = 0; m < m_lim; m++)
sum = L_add(sum, L_deposit_l(in[m]));
out[0] = extract_l(L_mpy_ls(sum, angl_intl_2));
// Calculate the others coefficients
angl_begin = angl_intl;
angl_step = angl_intl_2;
for(i = 1; i < i_lim; i++)
{
sum = 0;
angl_acc = angl_begin;
for(m = 0; m < m_lim; m++)
{
sum = L_add(sum, L_deposit_l(mult(in[m], cos_fxp(angl_acc))));
angl_acc += angl_step;
}
out[i] = extract_l(L_mpy_ls(sum, angl_intl_2));
angl_step += angl_intl_2;
angl_begin += angl_intl;
}
}
void imbe_vocoder::fft_init(void)
{
Word16 i, fft_len2, shift, step, theta;
fft_len2 = shr(FFTLENGTH, 1);
shift = norm_s(fft_len2);
step = shl(2, shift);
theta = 0;
for(i = 0; i <= fft_len2; i++)
{
wr_array[i] = cos_fxp(theta);
wi_array[i] = sin_fxp(theta);
if(i >= (fft_len2 - 1))
theta = ONE_Q15;
else
theta = add(theta, step);
}
}
// Subroutine FFT: Fast Fourier Transform
// ***************************************************************
// * Replaces data by its DFT, if isign is 1, or replaces data *
// * by inverse DFT times nn if isign is -1. data is a complex *
// * array of length nn, input as a real array of length 2*nn. *
// * nn MUST be an integer power of two. This is not checked *
// * The real part of the number should be in the zeroeth *
// * of data , and the imaginary part should be in the next *
// * element. Hence all the real parts should have even indeces *
// * and the imaginary parts, odd indeces. *
// * *
// * Data is passed in an array starting in position 0, but the *
// * code is copied from Fortran so uses an internal pointer *
// * which accesses position 0 as position 1, etc. *
// * *
// * This code uses e+jwt sign convention, so isign should be *
// * reversed for e-jwt. *
// ***************************************************************
//
// Q values:
// datam1 - Q14
// isign - Q15
#define SWAP(a,b) temp1 = (a);(a) = (b); (b) = temp1
void imbe_vocoder::fft(Word16 *datam1, Word16 nn, Word16 isign)
{
Word16 n, mmax, m, j, istep, i;
Word16 wr, wi, temp1;
Word32 L_tempr, L_tempi;
Word16 *data;
Word32 L_temp1, L_temp2;
Word16 index, index_step;
// Use pointer indexed from 1 instead of 0
data = &datam1[-1];
n = shl(nn,1);
j = 1;
for( i = 1; i < n; i+=2 )
{
if ( j > i)
{
SWAP(data[j],data[i]);
SWAP(data[j+1],data[i+1]);
}
m = nn;
while ( m >= 2 && j > m )
{
j = sub(j,m);
m = shr(m,1);
}
j = add(j,m);
}
mmax = 2;
// initialize index step
index_step = nn;
while ( n > mmax)
{
istep = shl(mmax,1); // istep = 2 * mmax
index = 0;
index_step = shr(index_step,1);
wr = ONE_Q15;
wi = 0;
for ( m = 1; m < mmax; m+=2)
{
for ( i = m; i <= n; i += istep)
{
j = i + mmax;
// tempr = wr * data[j] - wi * data[j+1]
L_temp1 = L_shr(L_mult(wr,data[j]),1);
L_temp2 = L_shr(L_mult(wi,data[j+1]),1);
L_tempr = L_sub(L_temp1,L_temp2);
// tempi = wr * data[j+1] + wi * data[j]
L_temp1 = L_shr(L_mult(wr,data[j+1]),1);
L_temp2 = L_shr(L_mult(wi,data[j]),1);
L_tempi = L_add(L_temp1,L_temp2);
// data[j] = data[i] - tempr
L_temp1 = L_shr(L_deposit_h(data[i]),1);
data[j] = round(L_sub(L_temp1,L_tempr));
// data[i] += tempr
data[i] = round(L_add(L_temp1,L_tempr));
// data[j+1] = data[i+1] - tempi
L_temp1 = L_shr(L_deposit_h(data[i+1]),1);
data[j+1] = round(L_sub(L_temp1,L_tempi));
// data[i+1] += tempi
data[i+1] = round(L_add(L_temp1,L_tempi));
}
index = add(index,index_step);
wr = wr_array[index];
if (isign < 0)
wi = negate(wi_array[index]);
else
wi = wi_array[index];
}
mmax = istep;
}
}
+81
View File
@@ -0,0 +1,81 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _DSP_SUB
#define _DSP_SUB
#define CNST_0_5_Q1_15 0x4000
#define CNST_0_5_Q5_11 0x0400
#define CNST_1_0_Q1_15 0x7FFF
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform inverse DCT
//
//
// INPUT:
// in - pointer to input data
// m_lim - input data's size
// i_lim - result's size
// out - pointer to save result
//
// OUTPUT:
// None
//
// RETURN:
// Saved in out result of conversion
//
//-----------------------------------------------------------------------------
void idct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out);
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform DCT
//
//
// INPUT:
// in - pointer to input data
// m_lim - input data's size
// i_lim - result's size
// out - pointer to save result
//
// OUTPUT:
// None
//
// RETURN:
// Saved in out result of conversion
//
//-----------------------------------------------------------------------------
void dct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out);
#define FFTLENGTH 256
void fft_init(void);
void fft(Word16 *datam1, Word16 nn, Word16 isign);
void c_fft(Word16 * farray_ptr);
#endif
+142
View File
@@ -0,0 +1,142 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "dsp_sub.h"
#include "aux_sub.h"
#include "tbls.h"
#include "encode.h"
#include "dc_rmv.h"
#include "pe_lpf.h"
#include "pitch_est.h"
#include "pitch_ref.h"
#include "v_uv_det.h"
#include "sa_encode.h"
#include "ch_encode.h"
#include "imbe_vocoder.h"
const uint8_t BIT_MASK_TABLE8[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE8[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE8[(i)&7])
void imbe_vocoder::encode_init(void)
{
v_zap(pitch_est_buf, PITCH_EST_BUF_SIZE);
v_zap(pitch_ref_buf, PITCH_EST_BUF_SIZE);
v_zap(pe_lpf_mem, PE_LPF_ORD);
pitch_est_init();
fft_init();
dc_rmv_mem = 0;
sa_encode_init();
pitch_ref_init();
}
void imbe_vocoder::encode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd)
{
Word16 i;
Word16 *wr_ptr, *sig_ptr;
for(i = 0; i < PITCH_EST_BUF_SIZE - FRAME; i++)
{
pitch_est_buf[i] = pitch_est_buf[i + FRAME];
pitch_ref_buf[i] = pitch_ref_buf[i + FRAME];
}
dc_rmv(snd, &pitch_ref_buf[PITCH_EST_BUF_SIZE - FRAME], &dc_rmv_mem, FRAME);
pe_lpf(&pitch_ref_buf[PITCH_EST_BUF_SIZE - FRAME], &pitch_est_buf[PITCH_EST_BUF_SIZE - FRAME], pe_lpf_mem, FRAME);
pitch_est(imbe_param, pitch_est_buf);
//
// Speech windowing and FFT calculation
//
wr_ptr = (Word16 *)wr;
sig_ptr = &pitch_ref_buf[40];
for(i = 146; i < 256; i++)
{
fft_buf[i].re = mult(*sig_ptr++, *wr_ptr++);
fft_buf[i].im = 0;
}
fft_buf[0].re = *sig_ptr++;
fft_buf[0].im = 0;
wr_ptr--;
for(i = 1; i < 111; i++)
{
fft_buf[i].re = mult(*sig_ptr++, *wr_ptr--);
fft_buf[i].im = 0;
}
for(i = 111; i < 146; i++)
fft_buf[i].re = fft_buf[i].im = 0;
fft((Word16 *)&fft_buf, FFTLENGTH, 1);
pitch_ref(imbe_param, fft_buf);
v_uv_det(imbe_param, fft_buf);
sa_encode(imbe_param);
encode_frame_vector(imbe_param, frame_vector);
}
void imbe_vocoder::encode_4400(int16_t *pcm, uint8_t *imbe)
{
int16_t frame_vector[8];
memset(imbe, 0, 11);
imbe_encode(frame_vector, pcm);
uint32_t offset = 0U;
int16_t mask = 0x0800;
for (uint32_t i = 0U; i < 12U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[0U] & mask) != 0);
mask = 0x0800;
for (uint32_t i = 0U; i < 12U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[1U] & mask) != 0);
mask = 0x0800;
for (uint32_t i = 0U; i < 12U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[2U] & mask) != 0);
mask = 0x0800;
for (uint32_t i = 0U; i < 12U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[3U] & mask) != 0);
mask = 0x0400;
for (uint32_t i = 0U; i < 11U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[4U] & mask) != 0);
mask = 0x0400;
for (uint32_t i = 0U; i < 11U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[5U] & mask) != 0);
mask = 0x0400;
for (uint32_t i = 0U; i < 11U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[6U] & mask) != 0);
mask = 0x0040;
for (uint32_t i = 0U; i < 7U; i++, mask >>= 1, offset++)
WRITE_BIT(imbe, offset, (frame_vector[7U] & mask) != 0);
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _ENCODE
#define _ENCODE
#define PITCH_EST_BUF_SIZE 621
#if 0
void encode_init(void);
void encode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd);
void encode_4400(int16_t *pcm, uint8_t *imbe);
#endif
#endif
+62
View File
@@ -0,0 +1,62 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _GLOBALS
#define _GLOBALS
//#define PI (double)3.1415926535897932384626433832795
#define CNST_0_9254_Q0_16 60647 // 0.9254 in unsigned Q0.16 format
#define CNST_0_33_Q0_16 0x5556 // 0.(3) = 1/3 in unsigned Q0.16 format
#define CNST_ONE_Q8_24 0x01000000
#define CNST_0_7_Q1_15 0x599A
#define CNST_0_4_Q1_15 0x3333
#define CNST_0_03_Q1_15 0x03D7
#define CNST_0_05_Q1_15 0x0666
#define CNST_1_125_Q8_8 0x0120
#define CNST_0_5_Q8_8 0x0080
#define CNST_0_125_Q8_8 0x0020
#define CNST_0_25_Q8_8 0x0040
#define CNST_0_8717_Q1_15 0x6F94
#define CNST_0_0031_Q1_15 0x0064
#define CNST_0_48_Q4_12 0x07AE
#define CNST_1_00_Q4_12 0x1000
#define CNST_0_85_Q4_12 0x0D9B
#define CNST_0_4_Q4_12 0x0666
#define CNST_0_05_Q4_12 0x00CD
#define CNST_0_5882_Q1_15 0x4B4B
#define CNST_0_2857_Q1_15 0x2492
#define HI_BYTE(a) ((a >> 8) & 0xFF)
#define LO_BYTE(a) (a & 0xFF);
#endif
+69
View File
@@ -0,0 +1,69 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _IMBE
#define _IMBE
#include "typedef.h"
#define FRAME 160 // Number samples in frame
#define NUM_HARMS_MAX 56 // Maximum number of harmonics
#define NUM_HARMS_MIN 9 // Minimum number of harmonics
#define NUM_BANDS_MAX 12 // Maximum number of bands
#define MAX_BLOCK_LEN 10 // Maximum length of block used during spectral amplitude encoding
#define NUM_PRED_RES_BLKS 6 // Number of Prediction Residual Blocks
#define PE_LPF_ORD 21 // Order of Pitch estimation LPF
#define PITCH_EST_FRAME 301 // Pitch estimation frame size
#define B_NUM (NUM_HARMS_MAX - 1)
typedef struct
{
Word16 e_p;
Word16 pitch; // Q14.2
Word16 ref_pitch; // Q8.8
Word32 fund_freq;
Word16 num_harms;
Word16 num_bands;
Word16 v_uv_dsn[NUM_HARMS_MAX];
Word16 b_vec[NUM_HARMS_MAX + 3];
Word16 bit_alloc[B_NUM + 4];
Word16 sa[NUM_HARMS_MAX];
Word16 l_uv;
Word16 div_one_by_num_harm;
Word16 div_one_by_num_harm_sh;
} IMBE_PARAM;
typedef struct
{
Word16 re;
Word16 im;
} Cmplx16;
#if 0
void decode_init(IMBE_PARAM *imbe_param);
void decode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd);
#endif
#endif
+49
View File
@@ -0,0 +1,49 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*/
#include <stdio.h>
#include <stdlib.h>
#include "imbe_vocoder.h"
static bool already_printed = false;
imbe_vocoder::imbe_vocoder (void) :
prev_pitch(0),
prev_prev_pitch(0),
prev_e_p(0),
prev_prev_e_p(0),
seed(1),
num_harms_prev1(0),
num_harms_prev2(0),
num_harms_prev3(0),
fund_freq_prev(0),
th_max(0),
dc_rmv_mem(0),
d_gain_adjust(0)
{
memset(wr_array, 0, sizeof(wr_array));
memset(wi_array, 0, sizeof(wi_array));
memset(pitch_est_buf, 0, sizeof(pitch_est_buf));
memset(pitch_ref_buf, 0, sizeof(pitch_ref_buf));
memset(pe_lpf_mem, 0, sizeof(pe_lpf_mem));
memset(fft_buf, 0, sizeof(fft_buf));
memset(sa_prev1, 0, sizeof(sa_prev1));
memset(sa_prev2, 0, sizeof(sa_prev2));
memset(uv_mem, 0, sizeof(uv_mem));
memset(ph_mem, 0, sizeof(ph_mem));
memset(vu_dsn_prev, 0, sizeof(vu_dsn_prev));
memset(sa_prev3, 0, sizeof(sa_prev3));
memset(v_uv_dsn, 0, sizeof(v_uv_dsn));
memset(&my_imbe_param, 0, sizeof(IMBE_PARAM));
decode_init(&my_imbe_param);
encode_init();
// (ambe2 patch: the start-up banner on stderr is gone from the library; the credit stays in this file and in lib/op25/README.md)
(void)already_printed;
}
+95
View File
@@ -0,0 +1,95 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*/
/* -*- c++ -*- */
#ifndef INCLUDED_IMBE_VOCODER_H
#define INCLUDED_IMBE_VOCODER_H
#include <stdint.h>
#include <string.h>
#include "imbe.h"
#include "dsp_sub.h"
#include "basic_op.h"
#include "math_sub.h"
#include "encode.h"
#include "decode.h"
class imbe_vocoder
{
public:
imbe_vocoder(void); // constructor
~imbe_vocoder() {} // destructor
// imbe_encode compresses 160 samples (in unsigned int format)
// outputs u[] vectors as frame_vector[]
void imbe_encode(int16_t *frame_vector, int16_t *snd) {
encode(&my_imbe_param, frame_vector, snd);
}
// imbe_decode decodes IMBE codewords (frame_vector),
// outputs the resulting 160 audio samples (snd)
void imbe_decode(int16_t *frame_vector, int16_t *snd) {
decode(&my_imbe_param, frame_vector, snd);
}
// hack to enable ambe encoder read access to speech parameters
const IMBE_PARAM* param(void) {return &my_imbe_param;}
void set_gain_adjust(float gain_adjust) {d_gain_adjust = gain_adjust;}
void encode_4400(int16_t *snd, uint8_t *imbe);
void decode_4400(int16_t *snd, uint8_t *imbe);
private:
IMBE_PARAM my_imbe_param;
/* data items originally static (moved from individual c++ sources) */
Word16 prev_pitch, prev_prev_pitch, prev_e_p, prev_prev_e_p;
UWord32 seed ;
Word16 num_harms_prev1;
Word32 sa_prev1[NUM_HARMS_MAX + 2];
Word16 num_harms_prev2;
Word32 sa_prev2[NUM_HARMS_MAX + 2];
Word16 uv_mem[105];
UWord32 ph_mem[NUM_HARMS_MAX];
Word16 num_harms_prev3;
Word32 fund_freq_prev;
Word16 vu_dsn_prev[NUM_HARMS_MAX];
Word16 sa_prev3[NUM_HARMS_MAX];
Word32 th_max;
Word16 v_uv_dsn[NUM_BANDS_MAX];
Word16 wr_array[FFTLENGTH / 2 + 1];
Word16 wi_array[FFTLENGTH / 2 + 1];
Word16 pitch_est_buf[PITCH_EST_BUF_SIZE];
Word16 pitch_ref_buf[PITCH_EST_BUF_SIZE];
Word32 dc_rmv_mem;
Cmplx16 fft_buf[FFTLENGTH];
Word16 pe_lpf_mem[PE_LPF_ORD];
float d_gain_adjust;
/* member functions */
void idct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out);
void dct(Word16 *in, Word16 m_lim, Word16 i_lim, Word16 *out);
void fft_init(void);
void fft(Word16 *datam1, Word16 nn, Word16 isign);
void encode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd);
void parse(int argc, char **argv);
void pitch_est_init(void);
Word32 autocorr(Word16 *sigin, Word16 shift, Word16 scale_shift);
void e_p(Word16 *sigin, Word16 *res_buf);
void pitch_est(IMBE_PARAM *imbe_param, Word16 *frames_buf);
void sa_decode_init(void);
void sa_decode(IMBE_PARAM *imbe_param);
void sa_encode_init(void);
void sa_encode(IMBE_PARAM *imbe_param);
void uv_synt_init(void);
void uv_synt(IMBE_PARAM *imbe_param, Word16 *snd);
void v_synt_init(void);
void v_synt(IMBE_PARAM *imbe_param, Word16 *snd);
void pitch_ref_init(void);
Word16 voiced_sa_calc(Word32 num, Word16 den);
Word16 unvoiced_sa_calc(Word32 num, Word16 den);
void v_uv_det(IMBE_PARAM *imbe_param, Cmplx16 *fft_buf);
void decode_init(IMBE_PARAM *imbe_param);
void decode(IMBE_PARAM *imbe_param, Word16 *frame_vector, Word16 *snd);
void encode_init(void);
};
#endif /* INCLUDED_IMBE_VOCODER_H */
+355
View File
@@ -0,0 +1,355 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "math_sub.h"
//-----------------------------------------------------------------------------
// Table for routine Pow2() table[] = 2^(-1...0)
//-----------------------------------------------------------------------------
static const Word16 pow2_table[33] =
{
16384, 16743, 17109, 17484, 17867, 18258, 18658, 19066, 19484, 19911,
20347, 20792, 21247, 21713, 22188, 22674, 23170, 23678, 24196, 24726,
25268, 25821, 26386, 26964, 27554, 28158, 28774, 29405, 30048, 30706,
31379, 32066, 32767
};
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes pow(2.0, x)
//
// INPUT:
// x - In signed Q10.22 format
//
// OUTPUT:
// None
//
// RETURN:
// Result in signed Q14.2 format
//
//-----------------------------------------------------------------------------
Word16 Pow2(Word32 x)
{
Word16 exp, i, a, tmp;
Word32 L_x;
Word16 exponent, fraction;
exponent = extract_h(L_shr(x, 6));
if(exponent < 0)
exponent = add(exponent, 1);
fraction = extract_l(L_shr(L_sub(x, L_shl(L_deposit_l(exponent),6 + 16)), 7));
if(x < 0)
fraction = negate(fraction);
L_x = L_mult(fraction, 32); // L_x = fraction<<6
i = extract_h(L_x); // Extract b10-b16 of fraction
L_x = L_shr(L_x, 1);
a = extract_l(L_x); // Extract b0-b9 of fraction
a = a & (Word16)0x7fff;
L_x = L_deposit_h (pow2_table[i]); // table[i] << 16
tmp = sub(pow2_table[i], pow2_table[i + 1]); // table[i] - table[i+1]
L_x = L_msu(L_x, tmp, a); // L_x -= tmp*a*2
if(x < 0)
{
L_x = L_deposit_h(div_s(0x4000, extract_h(L_x))); // calculate 1/fraction
exponent = sub(exponent, 1);
}
exp = sub(12, exponent);
L_x = L_shr_r(L_x, exp);
return extract_h(L_x);
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Multiply a 32 bit number (L_var2) and a 16 bit
// number (var1) returning a 32 bit result. L_var2
// is truncated to 31 bits prior to executing the
// multiply.
//
// INPUT:
// L_var2 - A Word32 input variable
// var1 - A Word16 input variable
//
// OUTPUT:
// None
//
// RETURN:
// A Word32 value
//
//-----------------------------------------------------------------------------
Word32 L_mpy_ls(Word32 L_var2, Word16 var1)
{
Word32 L_varOut;
Word16 swtemp;
swtemp = shr(extract_l(L_var2), 1);
swtemp = (Word16)32767 & (Word16) swtemp;
L_varOut = L_mult(var1, swtemp);
L_varOut = L_shr(L_varOut, 15);
L_varOut = L_mac(L_varOut, var1, extract_h(L_var2));
return (L_varOut);
}
//-----------------------------------------------------------------------------
// Table for routine cos_fxp()
//-----------------------------------------------------------------------------
static const Word16 cos_table[129] =
{
32767, 32766, 32758, 32746, 32729, 32706, 32679, 32647, 32610,
32568, 32522, 32470, 32413, 32352, 32286, 32214, 32138, 32058,
31972, 31881, 31786, 31686, 31581, 31471, 31357, 31238, 31114,
30986, 30853, 30715, 30572, 30425, 30274, 30118, 29957, 29792,
29622, 29448, 29269, 29086, 28899, 28707, 28511, 28311, 28106,
27897, 27684, 27467, 27246, 27020, 26791, 26557, 26320, 26078,
25833, 25583, 25330, 25073, 24812, 24548, 24279, 24008, 23732,
23453, 23170, 22884, 22595, 22302, 22006, 21706, 21403, 21097,
20788, 20475, 20160, 19841, 19520, 19195, 18868, 18538, 18205,
17869, 17531, 17190, 16846, 16500, 16151, 15800, 15447, 15091,
14733, 14373, 14010, 13646, 13279, 12910, 12540, 12167, 11793,
11417, 11039, 10660, 10279, 9896, 9512, 9127, 8740, 8351,
7962, 7571, 7180, 6787, 6393, 5998, 5602, 5205, 4808,
4410, 4011, 3612, 3212, 2811, 2411, 2009, 1608, 1206,
804, 402, 0
};
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes the cosine of x whose value is expressed in radians/PI.
//
// INPUT:
// x - argument in Q1.15
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q1.15
//
//-----------------------------------------------------------------------------
Word16 cos_fxp(Word16 x)
{
Word16 tx, ty;
Word16 sign;
Word16 index1,index2;
Word16 m;
Word16 temp;
sign = 0;
if(x < 0)
tx = negate(x);
else
tx = x;
// if angle > pi/2, cos(angle) = -cos(pi-angle)
if(tx > X05_Q15)
{
tx = sub(ONE_Q15,tx);
sign = -1;
}
// convert input to be within range 0-128
index1 = shr(tx,7);
index2 = add(index1,1);
if (index1 == 128)
return (Word16)0;
m = sub(tx,shl(index1,7));
// convert decimal part to Q15
m = shl(m,8);
temp = sub(cos_table[index2],cos_table[index1]);
temp = mult(m,temp);
ty = add(cos_table[index1],temp);
if(sign)
return negate(ty);
else
return ty;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes the sinus of x whose value is expressed in radians/PI.
//
// INPUT:
// x - argument in Q1.15
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q1.15
//
//-----------------------------------------------------------------------------
Word16 sin_fxp(Word16 x)
{
Word16 tx, ty;
Word16 sign;
sign = 0;
if(x < 0)
{
tx = negate(x);
sign = 1;
}
else
tx = x;
if(tx > X05_Q15)
tx = sub(tx, X05_Q15);
else
tx = sub(X05_Q15,tx);
ty = cos_fxp(tx);
if(sign)
return negate(ty);
else
return ty;
}
//-----------------------------------------------------------------------------
// Table for routine sqrt_l_exp()
// table[] = sqrt((i+16)*2^-6) * 2^15, i.e. sqrt(x) scaled Q15
//-----------------------------------------------------------------------------
static const Word16 sqrt_table[49] =
{
16384, 16888, 17378, 17854, 18318, 18770, 19212, 19644, 20066, 20480,
20886, 21283, 21674, 22058, 22435, 22806, 23170, 23530, 23884, 24232,
24576, 24915, 25249, 25580, 25905, 26227, 26545, 26859, 27170, 27477,
27780, 28081, 28378, 28672, 28963, 29251, 29537, 29819, 30099, 30377,
30652, 30924, 31194, 31462, 31727, 31991, 32252, 32511, 32767
};
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes sqrt(L_x), where L_x is positive.
//
// INPUT:
// L_x - argument in Q1.31
// *exp - pointer to save denormalization exponent
// OUTPUT:
// Right shift to be applied to result, Q16.0
//
// RETURN:
// Result in Q1.31
// Right shift should be applied to it!
//
//-----------------------------------------------------------------------------
Word32 sqrt_l_exp(Word32 L_x, Word16 *exp)
{
Word16 e, i, a, tmp;
Word32 L_y;
if(L_x <= (Word32)0)
{
*exp = 0;
return (Word32)0;
}
e = norm_l(L_x) & 0xFFFE; // get next lower EVEN norm. exp
L_x = L_shl(L_x, e); // L_x is normalized to [0.25..1)
*exp = e >> 1; // return 2*exponent (or Q1)
L_x = L_shr(L_x, 9);
i = extract_h(L_x); // Extract b25-b31, 16 <= i <= 63 because of normalization
L_x = L_shr(L_x, 1);
a = extract_l(L_x); // Extract b10-b24
a = a & (Word16)0x7fff;
i = sub(i, 16); // 0 <= i <= 47
L_y = L_deposit_h(sqrt_table[i]); // table[i] << 16
tmp = sub(sqrt_table[i], sqrt_table[i + 1]); // table[i] - table[i+1])
L_y = L_msu(L_y, tmp, a); // L_y -= tmp*a*2
return L_y;
}
//-----------------------------------------------------------------------------
// Table for routine Log2()
//-----------------------------------------------------------------------------
static const Word16 log_table[33] =
{
0, 1455, 2866, 4236, 5568, 6863, 8124, 9352, 10549, 11716,
12855, 13967, 15054, 16117, 17156, 18172, 19167, 20142, 21097, 22033,
22951, 23852, 24735, 25603, 26455, 27291, 28113, 28922, 29716, 30497,
31266, 32023, 32767
};
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes log2 of x
//
// INPUT:
// x - argument in Q14.2
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q10.22
//
//-----------------------------------------------------------------------------
Word32 Log2(Word16 x)
{
Word16 exp, i, a, tmp;
Word32 L_y;
if(x <= (Word16)0)
return 0;
exp = norm_s(x);
x = shl(x, exp);
i = shr(x, 9); // Extract b15-b9
a = shl(x & 0x1FF, 6); // Extract b8-b0
i = sub (i, 32);
L_y = L_deposit_h(log_table[i]); // table[i] << 16
tmp = sub(log_table[i], log_table[i + 1]); // table[i] - table[i+1]
L_y = L_msu(L_y, tmp, a); // L_y -= tmp*a*2
L_y = L_shr(L_y, 9);
exp = sub(12, exp);
L_y = L_add(L_y, L_deposit_h(shl(exp, 6)));
return L_y;
}
+130
View File
@@ -0,0 +1,130 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _MATH_SUB
#define _MATH_SUB
#define X05_Q15 16384 // (0.5*(1<<15))
#define ONE_Q15 32767 // ((1<<15)-1)
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes the cosine of x whose value is expressed in radians/PI.
//
// INPUT:
// x - argument in Q1.15
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q1.15
//
//-----------------------------------------------------------------------------
Word16 cos_fxp(Word16 x);
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes the sinus of x whose value is expressed in radians/PI.
//
// INPUT:
// x - argument in Q1.15
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q1.15
//
//-----------------------------------------------------------------------------
Word16 sin_fxp(Word16 x);
//-----------------------------------------------------------------------------
// PURPOSE:
// Multiply a 32 bit number (L_var2) and a 16 bit
// number (var1) returning a 32 bit result. L_var2
// is truncated to 31 bits prior to executing the
// multiply.
//
// INPUT:
// L_var2 - A Word32 input variable
// var1 - A Word16 input variable
//
// OUTPUT:
// None
//
// RETURN:
// A Word32 value
//
//-----------------------------------------------------------------------------
Word32 L_mpy_ls(Word32 L_var2, Word16 var1);
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes pow(2.0, x)
//
// INPUT:
// x - In signed Q10.22 format
//
// OUTPUT:
// None
//
// RETURN:
// Result in signed Q14.2 format
//
//-----------------------------------------------------------------------------
Word16 Pow2 (Word32 x);
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes sqrt(L_x), where L_x is positive.
//
// INPUT:
// L_x - argument in Q1.31
// *exp - pointer to save denormalization exponent
// OUTPUT:
// Right shift to be applied to result, Q16.0
//
// RETURN:
// Result in Q1.31
// Right shift should be applied to it!
//
//-----------------------------------------------------------------------------
Word32 sqrt_l_exp (Word32 L_x, Word16 *exp);
//-----------------------------------------------------------------------------
// PURPOSE:
// Computes log2 of x
//
// INPUT:
// x - argument in Q14.2
//
// OUTPUT:
// None
//
// RETURN:
// Result in Q10.22
//
//-----------------------------------------------------------------------------
Word32 Log2(Word16 x);
#endif
+74
View File
@@ -0,0 +1,74 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "aux_sub.h"
#include "basic_op.h"
#include "math_sub.h"
#include "pe_lpf.h"
static const Word16 lpf_coef[PE_LPF_ORD] =
{
-94, -92, 185, 543, 288, -883, -1834, -495, 3891, 9141, 11512,
9141, 3891, -495, -1834, -883, 288, 543, 185, -92, -94
};
//-----------------------------------------------------------------------------
// PURPOSE:
// Low-pass filter for pitch estimator
//
//
// INPUT:
// *sigin - pointer to input signal buffer
// *sigout - pointer to output signal buffer
// *mem - pointer to filter's memory element
// len - number of input signal samples
//
// OUTPUT:
// None
//
// RETURN:
// Saved filter state in mem
//
//-----------------------------------------------------------------------------
void pe_lpf(Word16 *sigin, Word16 *sigout, Word16 *mem, Word16 len)
{
Word16 i;
Word32 L_sum;
while(len--)
{
for(i = 0; i < PE_LPF_ORD - 1; i++)
mem[i] = mem[i + 1];
mem[PE_LPF_ORD - 1] = *sigin++;
L_sum = 0;
for(i = 0; i < PE_LPF_ORD; i++)
L_sum = L_mac(L_sum, mem[i], lpf_coef[i]);
*sigout++ = round(L_sum);
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _PE_LPF
#define _PE_LPF
//-----------------------------------------------------------------------------
// PURPOSE:
// Low-pass filter for pitch estimator
//
//
// INPUT:
// *sigin - pointer to input signal buffer
// *sigout - pointer to output signal buffer
// *mem - pointer to filter's memory element
// len - number of input signal samples
//
// OUTPUT:
// None
//
// RETURN:
// Saved filter state in mem
//
//-----------------------------------------------------------------------------
void pe_lpf(Word16 *sigin, Word16 *sigout, Word16 *mem, Word16 len);
#endif
+350
View File
@@ -0,0 +1,350 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "aux_sub.h"
#include "math_sub.h"
#include "tbls.h"
#include "pitch_est.h"
#include "encode.h"
#include "dsp_sub.h"
#include "imbe_vocoder.h"
static const UWord16 min_max_tbl[203] =
{
0x0008, 0x0009, 0x000a, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0012, 0x0013,
0x0014, 0x0115, 0x0216, 0x0218, 0x0319, 0x041a, 0x051b, 0x061c, 0x061e, 0x071f,
0x0820, 0x0921, 0x0a22, 0x0a24, 0x0b25, 0x0c26, 0x0d27, 0x0e28, 0x0e2a, 0x0f2b,
0x102c, 0x112d, 0x122e, 0x1230, 0x1331, 0x1432, 0x1533, 0x1634, 0x1636, 0x1737,
0x1838, 0x1939, 0x1a3a, 0x1a3c, 0x1b3d, 0x1c3e, 0x1d3f, 0x1e40, 0x1e42, 0x1f43,
0x2044, 0x2145, 0x2246, 0x2248, 0x2349, 0x244a, 0x254b, 0x264c, 0x264e, 0x274f,
0x2850, 0x2951, 0x2a52, 0x2a54, 0x2b55, 0x2c56, 0x2d57, 0x2e58, 0x2e5a, 0x2f5b,
0x305c, 0x315d, 0x325e, 0x3260, 0x3361, 0x3462, 0x3563, 0x3664, 0x3666, 0x3767,
0x3868, 0x3969, 0x3a6a, 0x3a6c, 0x3b6d, 0x3c6e, 0x3d6f, 0x3e70, 0x3e72, 0x3f73,
0x4074, 0x4175, 0x4276, 0x4278, 0x4379, 0x447a, 0x457b, 0x467c, 0x467e, 0x477f,
0x4880, 0x4981, 0x4a82, 0x4a84, 0x4b85, 0x4c86, 0x4d87, 0x4e88, 0x4e8a, 0x4f8b,
0x508c, 0x518d, 0x528e, 0x5290, 0x5391, 0x5492, 0x5593, 0x5694, 0x5696, 0x5797,
0x5898, 0x5999, 0x5a9a, 0x5a9c, 0x5b9d, 0x5c9e, 0x5d9f, 0x5ea0, 0x5ea2, 0x5fa3,
0x60a4, 0x61a5, 0x62a6, 0x62a8, 0x63a9, 0x64aa, 0x65ab, 0x66ac, 0x66ae, 0x67af,
0x68b0, 0x69b1, 0x6ab2, 0x6ab4, 0x6bb5, 0x6cb6, 0x6db7, 0x6eb8, 0x6eba, 0x6fbb,
0x70bc, 0x71bd, 0x72be, 0x72c0, 0x73c1, 0x74c2, 0x75c3, 0x76c4, 0x76c6, 0x77c7,
0x78c8, 0x79c9, 0x7aca, 0x7aca, 0x7bca, 0x7cca, 0x7dca, 0x7eca, 0x7eca, 0x7fca,
0x80ca, 0x81ca, 0x82ca, 0x82ca, 0x83ca, 0x84ca, 0x85ca, 0x86ca, 0x86ca, 0x87ca,
0x88ca, 0x89ca, 0x8aca, 0x8aca, 0x8bca, 0x8cca, 0x8dca, 0x8eca, 0x8eca, 0x8fca,
0x90ca, 0x91ca, 0x92ca, 0x92ca, 0x93ca, 0x94ca, 0x95ca, 0x96ca, 0x96ca, 0x97ca,
0x98ca, 0x99ca, 0x9aca
};
void imbe_vocoder::pitch_est_init(void)
{
prev_pitch = prev_prev_pitch = 158; // 100
prev_e_p = prev_prev_e_p = 0;
}
Word32 imbe_vocoder::autocorr(Word16 *sigin, Word16 shift, Word16 scale_shift)
{
Word32 L_sum;
Word16 i;
L_sum = 0;
for(i = 0; i < PITCH_EST_FRAME - shift; i++)
L_sum = L_add(L_sum, L_shr(L_mult(sigin[i], sigin[i + shift]), scale_shift) );
return L_sum;
}
void imbe_vocoder::e_p(Word16 *sigin, Word16 *res_buf)
{
Word16 i, j, den_part_acc, tmp;
Word32 L_sum, L_num, L_den, L_e0, L_tmp;
Word16 sig_wndwed[PITCH_EST_FRAME];
Word32 corr[259];
Word16 index_beg, index_step;
Word16 scale_shift;
// Windowing input signal s * wi^2
for(i = 0 ; i < PITCH_EST_FRAME; i++)
sig_wndwed[i] = mult_r(sigin[i], wi[i]);
L_sum = 0;
for(i = 0 ; i < PITCH_EST_FRAME; i++)
L_sum = L_add(L_sum, L_mpy_ls( L_mult(sigin[i], sigin[i]), wi[i]) ); // sum(s^2 * wi^2)
// Check for the overflow
if(L_sum == MAX_32)
{
// Recalculate with scaling
L_sum = 0;
for(i = 0 ; i < PITCH_EST_FRAME; i++)
L_sum = L_add(L_sum, L_mpy_ls( L_shr(L_mult(sigin[i], sigin[i]), 5), wi[i]));
scale_shift = 5;
}
else
scale_shift = 0;
L_e0 = 0;
for(i = 0 ; i < PITCH_EST_FRAME; i++)
L_e0 = L_add(L_e0, L_shr( L_mult(sig_wndwed[i], sig_wndwed[i]), scale_shift)); // sum(s^2 * wi^4)
// Calculate correlation for time shift in range 21...150 with step 0.5
// For integer shifts
for(tmp = 21, i = 0; tmp <= 150; tmp++, i += 2)
corr[i] = autocorr(sig_wndwed, tmp, scale_shift);
// For intermediate shifts
for(i = 1; i < 258; i += 2)
corr[i] = L_shr( L_add(corr[i - 1], corr[i + 1]), 1);
// variable to calculate 1 - P * sum(wi ^4) in denominator
den_part_acc = CNST_0_8717_Q1_15;
index_step = 42; // Note: 42 = 21 in Q15.1 format, so index_step will be used also as p in Q15.1 format
index_beg = 0;
L_e0 = L_shr(L_e0, 7); // divide by 64 to compensate wi scaling
// p = 21...122 by step 0.5
for(i = 0; i < 203; i++)
{
// Calculate sum( corr ( n * p) )
L_tmp = 0;
j = index_beg;
while(j <= 258)
{
L_tmp = L_add(L_tmp, corr[j]);
j += index_step;
}
L_tmp = L_shr(L_tmp, 6); // compensate wi scaling
L_tmp = L_add(L_tmp, L_e0); // For n = 0
L_tmp = L_tmp * index_step;
L_num = L_sub(L_sum, L_tmp);
index_beg++;
index_step++;
L_den = L_mpy_ls(L_sum, den_part_acc);
if(L_num < L_den && L_den != 0)
{
//res_buf[i] = (Word16)((double)L_num/(double)L_den * 4096.); // Q4.12
if(L_num <= 0)
res_buf[i] = 0;
else
{
tmp = norm_l(L_den);
tmp = div_s(extract_h(L_shl(L_num, tmp)), extract_h(L_shl(L_den, tmp)));
res_buf[i] = shr(tmp, 3); // convert to Q4.12
}
}
else if(L_num >= L_den)
{
res_buf[i] = CNST_1_00_Q4_12;
//res_buf[i] = (Word16)((double)L_num/(double)L_den * 4096.); // Q4.12
}
else
res_buf[i] = CNST_1_00_Q4_12;
den_part_acc = sub(den_part_acc, CNST_0_0031_Q1_15);
}
}
void imbe_vocoder::pitch_est(IMBE_PARAM *imbe_param, Word16 *frames_buf)
{
Word16 e_p_arr0[203], e_p_arr1[203], e_p_arr2[203], e1p1_e2p2_est_save[203];
Word16 min_index, max_index, p, i, p_index;
UWord16 tmp=0, p_fp;
UWord32 UL_tmp;
Word16 e_p_cur, pb, pf, ceb, s_tmp;
Word16 cef_est, cef, p0_est, p0, p1, p2, p1_max_index, p2_max_index, e1p1_e2p2_est, e1p1_e2p2;
Word16 e_p_arr2_min[203];
// Calculate E(p) function for current and two future frames
e_p(&frames_buf[0], e_p_arr0);
// Look-Back Pitch Tracking
min_index = HI_BYTE(min_max_tbl[prev_pitch]);
max_index = LO_BYTE(min_max_tbl[prev_pitch]);
p = pb = min_index;
e_p_cur = e_p_arr0[min_index];
while(++p <= max_index)
if(e_p_arr0[p] < e_p_cur)
{
e_p_cur = e_p_arr0[p];
pb = p;
}
ceb = add(e_p_cur, add(prev_e_p, prev_prev_e_p));
if(ceb <= CNST_0_48_Q4_12)
{
prev_prev_pitch = prev_pitch;
prev_pitch = pb;
prev_prev_e_p = prev_e_p;
prev_e_p = e_p_arr0[pb];
imbe_param->pitch = pb + 42; // Result in Q15.1 format
imbe_param->e_p = prev_e_p;
return;
}
// Look-Ahead Pitch Tracking
e_p(&frames_buf[FRAME], e_p_arr1);
e_p(&frames_buf[2 * FRAME], e_p_arr2);
p0_est = p0 = 0;
cef_est = e_p_arr0[p0] + e_p_arr1[p0] + e_p_arr2[p0];
e1p1_e2p2 = 1;
p1 = 0;
while(p1 < 203)
{
p2 = HI_BYTE(min_max_tbl[p1]);
p2_max_index = LO_BYTE(min_max_tbl[p1]);
s_tmp = e_p_arr2[p1];
while(p2 <= p2_max_index)
{
if(e_p_arr2[p2] < s_tmp)
s_tmp = e_p_arr2[p2];
p2++;
}
e_p_arr2_min[p1] = s_tmp;
p1++;
}
while(p0 < 203)
{
e1p1_e2p2_est = e_p_arr1[p0] + e_p_arr2_min[p0];
p1 = HI_BYTE(min_max_tbl[p0]);
p1_max_index = LO_BYTE(min_max_tbl[p0]);
while(p1 <= p1_max_index)
{
if(add(e_p_arr1[p1], e_p_arr2_min[p1]) < e1p1_e2p2_est)
e1p1_e2p2_est = add(e_p_arr1[p1], e_p_arr2_min[p1]);
p1++;
}
e1p1_e2p2_est_save[p0] = e1p1_e2p2_est;
cef = add(e_p_arr0[p0], e1p1_e2p2_est);
if(cef < cef_est)
{
cef_est = cef;
p0_est = p0;
}
p0++;
}
pf = p0_est;
// Sub-multiples analysis
if(pf >= 42) // Check if Sub-multiples are possible
{
if(pf < 84)
i = 1;
else if(pf < 126)
i = 2;
else if(pf < 168)
i = 3;
else
i = 4;
p_fp = (pf + 42) << 8; // Convert pitch estimation from array index to unsigned Q7.19 format
while(i--)
{
switch(i)
{
case 0:
tmp = p_fp >> 1; // P_est/2
break;
case 1:
UL_tmp = (UWord32)p_fp * 0x5555; // P_est/3
tmp = UL_tmp >> 16;
break;
case 2:
tmp = p_fp >> 2; // P_est/4
break;
case 3:
UL_tmp = (UWord32)p_fp * 0x3333; // P_est/5
tmp = UL_tmp >> 16;
break;
}
p_index = ((tmp + 0x0080) >> 8) - 42; // Convert fixed-point pitch value to integer array index with rounding
cef = add(e_p_arr0[p_index], e1p1_e2p2_est_save[p_index]);
if(cef <= CNST_0_85_Q4_12 && mult_r(cef, CNST_0_5882_Q1_15) <= cef_est) // 1/1.7 = 0.5882
{
pf = p_index;
break;
}
if(cef <= CNST_0_4_Q4_12 && mult_r(cef, CNST_0_2857_Q1_15) <= cef_est) // 1/3.5 = 0.2857
{
pf = p_index;
break;
}
if(cef <= CNST_0_05_Q4_12)
{
pf = p_index;
break;
}
}
}
cef = add(e_p_arr0[pf], e1p1_e2p2_est_save[pf]);
if(ceb <= cef)
p = pb;
else
p = pf;
prev_prev_pitch = prev_pitch;
prev_pitch = p;
prev_prev_e_p = prev_e_p;
prev_e_p = e_p_arr0[p];
imbe_param->pitch = p + 42; // Result in Q15.1 format
imbe_param->e_p = prev_e_p;
}
+29
View File
@@ -0,0 +1,29 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _PITCH_EST
#define _PITCH_EST
void pitch_est_init(void);
void pitch_est(IMBE_PARAM *imbe_param, Word16 *frames_buf);
#endif
+167
View File
@@ -0,0 +1,167 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "aux_sub.h"
#include "math_sub.h"
#include "dsp_sub.h"
#include "tbls.h"
#include "pitch_ref.h"
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define PITCH_REF_FRAME 221
#define MIN_INDEX 50
void pitch_ref(IMBE_PARAM *imbe_param, Cmplx16 *fft_buf)
{
Word16 i, j, index_a_save, pitch_est, tmp, shift, index_wr, up_lim;
Cmplx16 sp_rec[FFTLENGTH/2];
Word32 fund_freq, fund_freq_2, fund_freq_acc_a, fund_freq_acc_b, fund_freq_acc, L_tmp, amp_re_acc, amp_im_acc, L_sum, L_diff_min;
Word16 ha, hb, index_a, index_b, index_tbl[20], it_ind, re_tmp, im_tmp, pitch_cand=0;
Word32 fund_freq_cand=0;
pitch_est = shl(imbe_param->pitch, 7); // Convert to Q8.8
pitch_est = sub(pitch_est, CNST_1_125_Q8_8); // Sub 1.125 = 9/8
L_diff_min = MAX_32;
for(i = 0; i < 19; i++)
{
shift = norm_s(pitch_est);
tmp = shl(pitch_est, shift);
tmp = div_s(0x4000, tmp);
fund_freq = L_shl(tmp, shift + 11);
fund_freq_acc = fund_freq;
fund_freq_2 = L_shr(fund_freq, 1);
fund_freq_acc_a = L_sub(fund_freq, fund_freq_2);
fund_freq_acc_b = L_add(fund_freq, fund_freq_2);
// Calculate upper limit for spectrum reconstruction
up_lim = extract_h(L_shr((UWord32)CNST_0_9254_Q0_16 * pitch_est, 1)); // 0.9254/fund_freq
up_lim = sub(up_lim, CNST_0_5_Q8_8); // sub 0.5
up_lim = up_lim & 0xFF00; // extract fixed part
up_lim = mult(up_lim, extract_h(fund_freq));
up_lim = shr(up_lim, 1);
index_b = 0;
while(index_b <= up_lim)
{
ha = extract_h(fund_freq_acc_a);
hb = extract_h(fund_freq_acc_b);
index_a = (ha >> 8) + ((ha & 0xFF)?1:0);
index_b = (hb >> 8) + ((hb & 0xFF)?1:0);
if(index_b >= MIN_INDEX)
{
L_tmp = L_shl(L_deposit_h(index_a), 8);
L_tmp = L_sub(L_tmp, fund_freq_acc);
L_tmp = L_add(L_tmp, 0x00020000); // for rounding purpose
L_tmp = L_shr(L_tmp, 2);
index_a_save = index_a;
it_ind = 0;
amp_re_acc = amp_im_acc = 0;
while(index_a < index_b)
{
index_wr = extract_h(L_tmp);
if(index_wr < 0 && (L_tmp & 0xFFFF)) // truncating for negative number
index_wr = add(index_wr, 1);
index_wr = add(index_wr, 160);
index_tbl[it_ind++] = index_wr;
if(index_wr >= 0 && index_wr <= 320)
{
amp_re_acc = L_mac(amp_re_acc, fft_buf[index_a].re, wr_sp[index_wr]);
amp_im_acc = L_mac(amp_im_acc, fft_buf[index_a].im, wr_sp[index_wr]);
}
index_a++;
L_tmp = L_add(L_tmp, 0x400000);
}
it_ind = 0;
index_a = index_a_save;
while(index_a < index_b)
{
index_wr = index_tbl[it_ind++];
if(index_wr < 0 || index_wr > 320)
{
sp_rec[index_a].im = sp_rec[index_a].re = 0;
}
else
{
sp_rec[index_a].im = mult(mult(extract_h(amp_im_acc), wr_sp[index_wr]), 0x6666);
sp_rec[index_a].re = mult(mult(extract_h(amp_re_acc), wr_sp[index_wr]), 0x6666);
}
index_a++;
}
}
fund_freq_acc_a = L_add(fund_freq_acc_a, fund_freq);
fund_freq_acc_b = L_add(fund_freq_acc_b, fund_freq);
fund_freq_acc = L_add(fund_freq_acc, fund_freq);
}
L_sum = 0;
for(j = MIN_INDEX; j <= up_lim; j++)
{
re_tmp = sub(fft_buf[j].re, sp_rec[j].re);
im_tmp = sub(fft_buf[j].im, sp_rec[j].im);
L_sum = L_mac(L_sum, re_tmp, re_tmp);
L_sum = L_mac(L_sum, im_tmp, im_tmp);
}
if(L_sum < L_diff_min)
{
L_diff_min = L_sum;
pitch_cand = pitch_est;
fund_freq_cand = fund_freq;
}
pitch_est = add(pitch_est, CNST_0_125_Q8_8); // Add 0.125 = 1/8
}
imbe_param->ref_pitch = pitch_cand;
imbe_param->fund_freq = fund_freq_cand;
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _PITCH_REF
#define _PIRCH_REF
void pitch_ref(IMBE_PARAM *imbe_param, Cmplx16 *fft_buf);
#endif
+144
View File
@@ -0,0 +1,144 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "qnt_sub.h"
//-----------------------------------------------------------------------------
// PURPOSE:
// Dequantize by quantizer step size
//
//
// INPUT:
// qval - quantized value
// step_size - step size used to quantize in unsigned Q0.16 format
// bit_num - the number of bits
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in signed (bit_num).16 format
//
//-----------------------------------------------------------------------------
Word32 deqnt_by_step(Word16 qval, UWord16 step_size, Word16 bit_num)
{
Word32 res;
if(bit_num == 0)
return (Word32)0;
res = (Word32)step_size * (qval - (1 << (bit_num - 1)));
res = L_add(res, ((Word32)step_size * CNST_0_5_Q0_16) >> 16);
return res;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Quantize by quantizer step size
//
//
// INPUT:
// val - value to be quantized in Q5.10 format
// step_size - step size used to quantize in unsigned Q0.16 format
// bit_num - the number of bits
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in integer
//
//-----------------------------------------------------------------------------
Word16 qnt_by_step(Word16 val, UWord16 step_size, Word16 bit_num)
{
Word16 index, min_val, max_val;
Word16 q_index, shift, tmp;
shift = norm_s(step_size);
tmp = div_s(0x4000, shl(step_size, shift)); // Remark: To get result in Qxx.16 format it is necessary left shift tmp by (shift + 3)
q_index = shr_r(mult(val, tmp), sub(9, shift)); // q_index here is rounded to the nearest integer
max_val = 1 << (bit_num - 1);
min_val = negate(max_val);
if(q_index < min_val)
index = 0;
else if(q_index >= max_val)
index = (1 << bit_num) - 1;
else
index = max_val + q_index;
return index;
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Quantize by table
//
//
// INPUT:
// val - value to be quantized
// q_tbl - pointer to table
// q_tbl_size - size of table
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in integer
//
//-----------------------------------------------------------------------------
Word16 tbl_quant(Word16 val, Word16 *q_tbl, Word16 q_tbl_size)
{
Word16 min_index, max_index, index;
min_index = 0;
max_index = q_tbl_size - 1;
if(val >= q_tbl[max_index])
return max_index;
if(val <= q_tbl[min_index])
return min_index;
while(max_index - min_index != 1)
{
index = min_index + ((max_index - min_index) >> 1);
if(q_tbl[index] > val)
max_index = index;
else
min_index = index;
}
if(q_tbl[max_index] - val <= val - q_tbl[min_index])
return max_index;
else
return min_index;
}
+86
View File
@@ -0,0 +1,86 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _QNT_SUB
#define _QNT_SUB
#define CNST_0_5_Q0_16 0x8000
//-----------------------------------------------------------------------------
// PURPOSE:
// Dequantize by quantizer step size
//
//
// INPUT:
// qval - quantized value
// step_size - step size used to quantize in unsigned Q0.16 format
// bit_num - the number of bits
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in signed (bit_num).16 format
//
//-----------------------------------------------------------------------------
Word32 deqnt_by_step(Word16 qval, UWord16 step_size, Word16 bit_num);
//-----------------------------------------------------------------------------
// PURPOSE:
// Quantize by quantizer step size
//
//
// INPUT:
// val - value to be quantized
// step_size - step size used to quantize in unsigned Q0.16 format
// bit_num - the number of bits
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in integer
//
//-----------------------------------------------------------------------------
Word16 qnt_by_step(Word16 val, UWord16 step_size, Word16 bit_num);
//-----------------------------------------------------------------------------
// PURPOSE:
// Quantize by table
//
//
// INPUT:
// val - value to be quantized
// q_tbl - pointer to table
// q_tbl_size - size of table
//
// OUTPUT:
// None
//
// RETURN:
// Quantized Value in integer
//
//-----------------------------------------------------------------------------
Word16 tbl_quant(Word16 val, Word16 *q_tbl, Word16 q_tbl_size);
#endif
+61
View File
@@ -0,0 +1,61 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
static UWord32 seed = 1;
//-----------------------------------------------------------------------------
// PURPOSE:
// Generate pseudo-random numbers in range -1...1
//
//
// INPUT:
// None
//
// OUTPUT:
// None
//
// RETURN:
// Pseudo-random number in signed Q1.16 format
//
//-----------------------------------------------------------------------------
Word16 rand_gen(void)
{
UWord32 hi, lo;
lo = 16807 * (seed & 0xFFFF);
hi = 16807 * (seed >> 16);
lo += (Word32)(hi & 0x7FFF) << 16;
lo += (hi >> 15);
if(lo > 0x7FFFFFFF)
lo -= 0x7FFFFFFF;
seed = lo;
return (Word16)lo;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _RAND_GEN
#define _RAND_GEN
//-----------------------------------------------------------------------------
// PURPOSE:
// Generate pseudo-random numbers in range -1...1
//
//
// INPUT:
// None
//
// OUTPUT:
// None
//
// RETURN:
// Pseudo-random number in signed Q1.16 format
//
//-----------------------------------------------------------------------------
Word16 rand_gen(void);
#endif
+200
View File
@@ -0,0 +1,200 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "basic_op.h"
#include "imbe.h"
#include "tbls.h"
#include "qnt_sub.h"
#include "sa_decode.h"
#include "aux_sub.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include "encode.h"
#include "imbe_vocoder.h"
//-----------------------------------------------------------------------------
// PURPOSE:
// Initialization of Spectral Amplitude Decoder
//
//
// INPUT:
// None
//
// OUTPUT:
// None
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
void imbe_vocoder::sa_decode_init(void)
{
num_harms_prev1 = 30;
v_zap((Word16 *)sa_prev1, 2 * (NUM_HARMS_MAX + 2));
}
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform Spectral Amplitude Decoding
//
//
// INPUT:
// IMBE_PARAM *imbe_param - pointer to IMBE_PARAM structure with
// valid num_harms and b_vec items
//
// OUTPUT:
// None
//
// RETURN:
// Decoded Spectral Amplitudes
//
//-----------------------------------------------------------------------------
void imbe_vocoder::sa_decode(IMBE_PARAM *imbe_param)
{
Word16 gain_vec[6], gain_r[6];
UWord16 index, index_1, num_harms;
Word16 *gss_ptr, *ba_ptr, i, j, *t_vec_ptr, *b_vec_ptr;
Word16 t_vec[NUM_HARMS_MAX], c_vec[MAX_BLOCK_LEN];
UWord32 lmprbl_item;
Word16 bl_len, step_size, num_bits, tmp, ro_coef, si_coef, tmp1;
UWord32 k_coef, k_acc;
Word32 sum, tmp_word32, sa_tmp[NUM_HARMS_MAX];
Word16 *sa;
num_harms = imbe_param->num_harms;
index = num_harms - NUM_HARMS_MIN;
ba_ptr = imbe_param->bit_alloc;
b_vec_ptr = &imbe_param->b_vec[2];
sa = imbe_param->sa;
// Decoding the Gain Vector. gain_vec has signed Q5.11 format
gss_ptr = (Word16 *)&gain_step_size_tbl[index * 5];
gain_vec[0] = gain_qnt_tbl[*b_vec_ptr++];
for(i = 1; i < 6; i++)
gain_vec[i] = extract_l(L_shr(deqnt_by_step(*b_vec_ptr++, *gss_ptr++, *ba_ptr++), 5));
idct(gain_vec, NUM_PRED_RES_BLKS, NUM_PRED_RES_BLKS, gain_r);
lmprbl_item = lmprbl_tbl[index];
v_zap(t_vec, NUM_HARMS_MAX);
// Decoding the Higher Order DCT Coefficients
t_vec_ptr = t_vec;
for(i = 0; i < NUM_PRED_RES_BLKS; i++)
{
bl_len = (lmprbl_item >> 28) & 0xF; lmprbl_item <<= 4;
v_zap(c_vec, MAX_BLOCK_LEN);
c_vec[0] = gain_r[i];
for(j = 1; j < bl_len; j++)
{
num_bits = *ba_ptr++;
if(num_bits)
{
step_size = extract_h(((Word32)hi_ord_std_tbl[j - 1] * hi_ord_step_size_tbl[num_bits - 1]) << 1);
c_vec[j] = extract_l(L_shr(deqnt_by_step(*b_vec_ptr, step_size, num_bits), 5));
}
else
c_vec[j] = 0;
b_vec_ptr++;
}
idct(c_vec, bl_len, bl_len, t_vec_ptr);
t_vec_ptr += bl_len;
}
// Calculate num_harms_prev/num_harms. Result save in unsigned format Q8.24
if(num_harms == num_harms_prev1)
k_coef = (Word32)CNST_ONE_Q8_24;
else if(num_harms > num_harms_prev1)
k_coef = (Word32)div_s(num_harms_prev1 << 9, num_harms << 9) << 9;
else
{
// imbe_param->num_harms < num_harms_prev1
k_coef = 0;
tmp = num_harms_prev1;
while(tmp > num_harms)
{
tmp -= num_harms;
k_coef += (Word32)CNST_ONE_Q8_24;
}
k_coef += (Word32)div_s(tmp << 9, num_harms << 9) << 9;
}
if(num_harms <= 15)
ro_coef = CNST_0_4_Q1_15;
else if(num_harms <= 24)
ro_coef = num_harms * CNST_0_03_Q1_15 - CNST_0_05_Q1_15;
else
ro_coef = CNST_0_7_Q1_15;
k_acc = k_coef;
sum = 0;
for(i = num_harms_prev1 + 1; i < NUM_HARMS_MAX + 2; i++)
sa_prev1[i] = sa_prev1[num_harms_prev1];
for(i = 0; i < num_harms; i++)
{
index = (UWord16)(k_acc >> 24); // Get integer part
si_coef = (Word16)((k_acc - ((UWord32)index << 24)) >> 9); // Get fractional part
if(si_coef == 0)
{
tmp_word32 = L_mpy_ls(sa_prev1[index], ro_coef); // sa_prev1 here is in Q10.22 format
sa_tmp[i] = L_add(L_shr(L_deposit_h(t_vec[i]), 5), tmp_word32); // Convert t_vec to Q10.22 and add ...
sum = L_add(sum, sa_prev1[index]); // sum in Q10.22 format
}
else
{
index_1 = index + 1;
tmp_word32 = L_mpy_ls(sa_prev1[index], sub(0x7FFF, si_coef));
sum = L_add(sum, tmp_word32);
sa_tmp[i] = L_add(L_shr(L_deposit_h(t_vec[i]), 5), L_mpy_ls(tmp_word32, ro_coef));
tmp_word32 = L_mpy_ls(sa_prev1[index_1], si_coef);
sum = L_add(sum, tmp_word32);
sa_tmp[i] = L_add(sa_tmp[i], L_mpy_ls(tmp_word32, ro_coef));
}
k_acc += k_coef;
}
imbe_param->div_one_by_num_harm_sh = tmp = norm_s(num_harms);
imbe_param->div_one_by_num_harm = tmp1 = div_s(0x4000, num_harms << tmp); // calculate 1/num_harms with scaling for better pricision
// save result to use late
sum = L_shr(L_mpy_ls(L_mpy_ls(sum, ro_coef), tmp1), (14 - tmp));
for(i = 1; i <= num_harms; i++)
{
sa_prev1[i] = L_sub(sa_tmp[i - 1], sum);
sa[i - 1] = Pow2(sa_prev1[i]);
}
num_harms_prev1 = num_harms;
}
+64
View File
@@ -0,0 +1,64 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _SA_DECODE
#define _SA_DECODE
//-----------------------------------------------------------------------------
// PURPOSE:
// Initialization of Spectral Amplitude Decoder
//
//
// INPUT:
// None
//
// OUTPUT:
// None
//
// RETURN:
// None
//
//-----------------------------------------------------------------------------
#if 0
void sa_decode_init(void);
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform Spectral Amplitude Decoding
//
//
// INPUT:
// IMBE_PARAM *imbe_param - pointer to IMBE_PARAM structure with
// valid num_harms and b_vec items
//
// OUTPUT:
// None
//
// RETURN:
// Decoded Spectral Amplitudes
//
//-----------------------------------------------------------------------------
void sa_decode(IMBE_PARAM *imbe_param);
#endif
#endif
+296
View File
@@ -0,0 +1,296 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "basic_op.h"
#include "imbe.h"
#include "tbls.h"
#include "qnt_sub.h"
#include "sa_encode.h"
#include "aux_sub.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include <cstdio>
#include <cmath>
#include "encode.h"
#include "imbe_vocoder.h"
void imbe_vocoder::sa_encode_init(void)
{
Word16 i;
num_harms_prev2 = 30;
for(i = 0; i < NUM_HARMS_MAX + 2; i++)
sa_prev2[i] = 0;
}
void imbe_vocoder::sa_encode(IMBE_PARAM *imbe_param)
{
Word16 gain_vec[6], gain_r[6];
UWord16 index, i, j, num_harms;
Word16 *ba_ptr, *t_vec_ptr, *b_vec_ptr, *gss_ptr, *sa_ptr;
Word16 t_vec[NUM_HARMS_MAX], c_vec[MAX_BLOCK_LEN];
UWord32 lmprbl_item;
Word16 bl_len, step_size, num_bits, tmp, ro_coef, si_coef, tmp1;
UWord32 k_coef, k_acc;
Word32 sum, tmp_word32, vec32_tmp[NUM_HARMS_MAX], *vec32_ptr;
num_harms = imbe_param->num_harms;
// Calculate num_harms_prev2/num_harms. Result save in unsigned format Q8.24
if(num_harms == num_harms_prev2)
k_coef = (Word32)CNST_ONE_Q8_24;
else if(num_harms > num_harms_prev2)
k_coef = (Word32)div_s(num_harms_prev2 << 9, num_harms << 9) << 9;
else
{
// imbe_param->num_harms < num_harms_prev2
k_coef = 0;
tmp = num_harms_prev2;
while(tmp > num_harms)
{
tmp -= num_harms;
k_coef += (Word32)CNST_ONE_Q8_24;
}
k_coef += (Word32)div_s(tmp << 9, num_harms << 9) << 9;
}
// Calculate prediction coefficient
if(num_harms <= 15)
ro_coef = CNST_0_4_Q1_15;
else if(num_harms <= 24)
ro_coef = num_harms * CNST_0_03_Q1_15 - CNST_0_05_Q1_15;
else
ro_coef = CNST_0_7_Q1_15;
for(i = num_harms_prev2 + 1; i < NUM_HARMS_MAX + 2; i++)
sa_prev2[i] = sa_prev2[num_harms_prev2];
k_acc = k_coef;
sum = 0;
sa_ptr = imbe_param->sa;
vec32_ptr = vec32_tmp;
for(i = 0; i < num_harms; i++)
{
index = (UWord16)(k_acc >> 24); // Get integer part
si_coef = (Word16)((k_acc - ((UWord32)index << 24)) >> 9); // Get fractional part
if(si_coef == 0)
{
tmp_word32 = L_mpy_ls(sa_prev2[index], ro_coef); // sa_prev2 here is in Q10.22 format
*vec32_ptr++ = L_sub(Log2(*sa_ptr++), tmp_word32);
sum = L_add(sum, sa_prev2[index]); // sum in Q10.22 format
}
else
{
tmp_word32 = L_mpy_ls(sa_prev2[index], sub(0x7FFF, si_coef));
sum = L_add(sum, tmp_word32);
*vec32_ptr = L_sub(Log2(*sa_ptr++), L_mpy_ls(tmp_word32, ro_coef));
tmp_word32 = L_mpy_ls(sa_prev2[index + 1], si_coef);
sum = L_add(sum, tmp_word32);
*vec32_ptr = L_sub(*vec32_ptr, L_mpy_ls(tmp_word32, ro_coef));
vec32_ptr++;
}
k_acc += k_coef;
}
imbe_param->div_one_by_num_harm_sh = tmp = norm_s(num_harms);
imbe_param->div_one_by_num_harm = tmp1 = div_s(0x4000, num_harms << tmp); // calculate 1/num_harms with scaling for better pricision
// save result to use late
sum = L_shr(L_mpy_ls(L_mpy_ls(sum, ro_coef), tmp1), (14 - tmp));
for(i = 0; i < num_harms; i++)
t_vec[i] = extract_h(L_shl(L_add(vec32_tmp[i], sum), 5)); // t_vec has Q5.11 format
//////////////////////////////////////////////
//
// Encode T vector
//
//////////////////////////////////////////////
index = num_harms - NUM_HARMS_MIN;
// Unpack bit allocation table's item
get_bit_allocation(num_harms, imbe_param->bit_alloc);
lmprbl_item = lmprbl_tbl[index];
// Encoding the Higher Order DCT Coefficients
t_vec_ptr = t_vec;
b_vec_ptr = &imbe_param->b_vec[8];
ba_ptr = &imbe_param->bit_alloc[5];
for(i = 0; i < NUM_PRED_RES_BLKS; i++)
{
bl_len = (lmprbl_item >> 28) & 0xF; lmprbl_item <<= 4;
dct(t_vec_ptr, bl_len, bl_len, c_vec);
gain_vec[i] = c_vec[0];
/*
for(j = 0; j < bl_len; j++)
printf("%g ", (double)t_vec_ptr[j]/2048.);
printf("\n");
for(j = 0; j < bl_len; j++)
printf("%g ", (double)c_vec[j]/2048.);
printf("\n");
printf("\n");
*/
for(j = 1; j < bl_len; j++)
{
num_bits = *ba_ptr++;
if(num_bits)
{
step_size = extract_h(((Word32)hi_ord_std_tbl[j - 1] * hi_ord_step_size_tbl[num_bits - 1]) << 1);
*b_vec_ptr = qnt_by_step(c_vec[j], step_size, num_bits);
}
else
*b_vec_ptr = 0;
b_vec_ptr++;
}
t_vec_ptr += bl_len;
}
// Encoding the Gain Vector
dct(gain_vec, NUM_PRED_RES_BLKS, NUM_PRED_RES_BLKS, gain_r);
b_vec_ptr = &imbe_param->b_vec[2];
ba_ptr = &imbe_param->bit_alloc[0];
gss_ptr = (Word16 *)&gain_step_size_tbl[index * 5];
*b_vec_ptr++ = tbl_quant(gain_r[0], (Word16 *)&gain_qnt_tbl[0], GAIN_QNT_TBL_SIZE);
for(j = 1; j < 6; j++)
*b_vec_ptr++ = qnt_by_step(gain_r[j], *gss_ptr++, *ba_ptr++);
/*
for(j = 0; j < NUM_PRED_RES_BLKS; j++)
printf("%g ", (double)gain_vec[j]/2048.);
printf("\n");
for(j = 0; j < NUM_PRED_RES_BLKS; j++)
printf("%g ", (double)gain_r[j]/2048.);
printf("\n");
printf("\n");
*/
//////////////////////////////////////////////
//
// Decode T vector
//
//////////////////////////////////////////////
ba_ptr = imbe_param->bit_alloc;
b_vec_ptr = &imbe_param->b_vec[2];
// Decoding the Gain Vector. gain_vec has signed Q5.11 format
gss_ptr = (Word16 *)&gain_step_size_tbl[index * 5];
gain_vec[0] = gain_qnt_tbl[*b_vec_ptr++];
for(i = 1; i < 6; i++)
gain_vec[i] = extract_l(L_shr(deqnt_by_step(*b_vec_ptr++, *gss_ptr++, *ba_ptr++), 5));
/*
printf("gain deqnt\n");
for(j = 0; j < 6; j++)
printf("%g ", (double)gain_vec[j]/2048.);
printf("\n");
*/
idct(gain_vec, NUM_PRED_RES_BLKS, NUM_PRED_RES_BLKS, gain_r);
v_zap(t_vec, NUM_HARMS_MAX);
lmprbl_item = lmprbl_tbl[index];
// Decoding the Higher Order DCT Coefficients
t_vec_ptr = t_vec;
for(i = 0; i < NUM_PRED_RES_BLKS; i++)
{
bl_len = (lmprbl_item >> 28) & 0xF; lmprbl_item <<= 4;
v_zap(c_vec, MAX_BLOCK_LEN);
c_vec[0] = gain_r[i];
for(j = 1; j < bl_len; j++)
{
num_bits = *ba_ptr++;
if(num_bits)
{
step_size = extract_h(((Word32)hi_ord_std_tbl[j - 1] * hi_ord_step_size_tbl[num_bits - 1]) << 1);
c_vec[j] = extract_l(L_shr(deqnt_by_step(*b_vec_ptr, step_size, num_bits), 5));
}
else
c_vec[j] = 0;
b_vec_ptr++;
}
/*
printf("\n");
for(j = 0; j < bl_len; j++)
printf("%g ", (double)c_vec[j]/2048.);
printf("\n");
*/
idct(c_vec, bl_len, bl_len, t_vec_ptr);
t_vec_ptr += bl_len;
}
/*
printf("\n====t_vec_rec ===\n");
for(j = 0; j < num_harms; j++)
printf("%g ", (double)t_vec[j]/2048.);
printf("\n");
*/
//////////////////////////////////////////////
//
// Reconstruct Spectral Amplitudes
//
//////////////////////////////////////////////
k_acc = k_coef;
vec32_ptr = vec32_tmp;
for(i = num_harms_prev2 + 1; i < NUM_HARMS_MAX + 2; i++)
sa_prev2[i] = sa_prev2[num_harms_prev2];
for(i = 0; i < num_harms; i++)
{
index = (UWord16)(k_acc >> 24); // Get integer part
si_coef = (Word16)((k_acc - ((UWord32)index << 24)) >> 9); // Get fractional part
if(si_coef == 0)
{
tmp_word32 = L_mpy_ls(sa_prev2[index], ro_coef); // sa_prev2 here is in Q10.22 format
*vec32_ptr++ = L_add(L_shr(L_deposit_h(t_vec[i]), 5), tmp_word32); // Convert t_vec to Q10.22 and add ...
}
else
{
tmp_word32 = L_mpy_ls(sa_prev2[index], sub(0x7FFF, si_coef));
*vec32_ptr = L_add(L_shr(L_deposit_h(t_vec[i]), 5), L_mpy_ls(tmp_word32, ro_coef));
tmp_word32 = L_mpy_ls(sa_prev2[index + 1], si_coef);
*vec32_ptr = L_add(*vec32_ptr, L_mpy_ls(tmp_word32, ro_coef));
vec32_ptr++;
}
k_acc += k_coef;
}
for(i = 1; i <= num_harms; i++)
sa_prev2[i] = L_sub(vec32_tmp[i - 1], sum);
num_harms_prev2 = num_harms;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _SA_ENCODE
#define _SA_ENCODE
void sa_encode_init(void);
void sa_encode(IMBE_PARAM *imbe_param);
#endif
+202
View File
@@ -0,0 +1,202 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "qnt_sub.h"
#include "aux_sub.h"
#include "math_sub.h"
#include "sa_enh.h"
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform Spectral Amplitude Enhancement
//
//
// INPUT:
// IMBE_PARAM *imbe_param - pointer to IMBE_PARAM structure with
// valid num_harms, sa and fund_freq items
//
// OUTPUT:
// None
//
// RETURN:
// Enhanced Spectral Amplitudes
//
//-----------------------------------------------------------------------------
void sa_enh(IMBE_PARAM *imbe_param)
{
Word16 *sa, num_harm, sa_tmp[NUM_HARMS_MAX], nm;
Word16 cos_w[NUM_HARMS_MAX], i, tmp;
Word32 L_tmp, Rm0, Rm1;
Word32 w0, cos_acc;
Word32 L_den, L_num, L_Rm0_2, L_Rm1_2, L_sum_Rm02_Rm12, L_sum_mod;
Word16 Rm0Rm1, nm1, nm2, tot_nm;
Word16 Rm0_s, Rm1_s;
sa = imbe_param->sa;
num_harm = imbe_param->num_harms;
v_equ(sa_tmp, sa, num_harm);
Rm0 = L_v_magsq(sa, num_harm);
if(Rm0 == 0)
return;
nm = norm_l (Rm0);
if(Rm0 == MAX_32)
{
nm = 1;
v_equ_shr(sa_tmp, sa, nm, num_harm);
Rm0 = L_v_magsq(sa_tmp, num_harm);
}
else
{
if(nm > 2)
{
nm = -(nm >> 1);
v_equ_shr(sa_tmp, sa, nm, num_harm);
Rm0 = L_v_magsq(sa_tmp, num_harm);
}
}
w0 = imbe_param->fund_freq;
cos_acc = 0; Rm1 = 0;
for(i = 0; i < num_harm; i++)
{
cos_acc = L_add(cos_acc, w0);
cos_w[i] = cos_fxp(extract_h(cos_acc));
Rm1 = L_add(Rm1, L_mpy_ls(L_mult(sa_tmp[i], sa_tmp[i]), cos_w[i]));
}
Rm0_s = extract_h(Rm0);
Rm1_s = extract_h(Rm1);
L_Rm0_2 = L_mult(Rm0_s, Rm0_s);
L_Rm1_2 = L_mult(Rm1_s, Rm1_s);
L_den = L_sub(L_Rm0_2, L_Rm1_2);
L_den = L_mult(extract_h(L_den), Rm0_s);
nm1 = norm_l(L_den);
L_den = L_shl(L_den, nm1);
nm2 = norm_l(w0);
L_den = L_mpy_ls(L_den, extract_h(L_shl(w0, nm2))); // Calculate w0 * Rm0 * (Rm0^2 - Rm1^2)
nm1 += nm2; // total denominator shift
if (L_den < 1) return; // fix bug infinite loop due to invalid input
L_sum_Rm02_Rm12 = L_add(L_shr(L_Rm0_2, 2), L_shr(L_Rm1_2, 2));
Rm0Rm1 = shr(mult_r(Rm0_s, Rm1_s), 1);
for(i = 0; i < num_harm; i++)
{
if((((i + 1) << 3) > num_harm) && (sa_tmp[i] != 0x0000))
{
L_num = L_sub(L_sum_Rm02_Rm12, L_mult(Rm0Rm1, cos_w[i]));
tot_nm = norm_l(L_num);
L_num = L_shl(L_num, tot_nm);
while(L_num >= L_den)
{
L_num = L_shr(L_num, 1);
tot_nm -= 1;
}
tmp = div_s(extract_h(L_num), extract_h(L_den));
tot_nm -= nm1;
L_tmp = L_mult(sa_tmp[i], sa_tmp[i]);
nm2 = norm_l(L_tmp);
L_tmp = L_shl(L_tmp, nm2);
L_tmp = L_mult(extract_h(L_tmp), tmp);
tot_nm += nm2;
tot_nm -= 2;
if(tot_nm <= 0)
{
L_tmp = L_shr(L_tmp, add(8, tot_nm));
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm);
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm);
L_tmp = L_mult(extract_h(L_tmp), CNST_0_9898_Q1_15);
tmp = extract_h(L_shl(L_tmp, 1));
}
else
{
if(tot_nm <= 8)
{
L_tmp = L_shr(L_tmp, tot_nm);
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm);
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm + 1);
tmp = mult(extract_h(L_tmp), CNST_0_9898_Q1_15);
}
else
{
nm1 = tot_nm & 0xFFFE;
L_tmp = L_shr(L_tmp, tot_nm - nm1);
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm);
tot_nm = nm1 >> 1;
nm1 = tot_nm & 0xFFFE;
L_tmp = L_shr(L_tmp, tot_nm - nm1);
L_tmp = sqrt_l_exp(L_tmp, &tot_nm);
L_tmp = L_shr(L_tmp, tot_nm);
L_tmp = L_mult(extract_h(L_tmp), CNST_0_9898_Q1_15);
tot_nm = nm1 >> 1;
tmp = extract_h(L_shr(L_tmp, tot_nm + 1));
}
}
if(tmp > CNST_1_2_Q2_14)
sa[i] = extract_h(L_shl(L_mult(sa[i], CNST_1_2_Q2_14), 1));
else if(tmp < CNST_0_5_Q2_14)
sa[i] = shr(sa[i], 1);
else
sa[i] = extract_h(L_shl(L_mult(sa[i], tmp), 1));
}
}
// Compute the correct scale factor
v_equ_shr(sa_tmp, sa, nm, num_harm);
L_sum_mod = L_v_magsq(sa_tmp, num_harm);
if(L_sum_mod > Rm0)
{
tmp = div_s(extract_h(Rm0), extract_h(L_sum_mod));
L_tmp = sqrt_l_exp(L_deposit_h(tmp), &tot_nm);
tmp = shr(extract_h(L_tmp), tot_nm);
for(i = 0; i < num_harm; i++)
sa[i] = mult_r(sa[i], tmp);
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _SA_ENH
#define _SA_ENH
#define CNST_0_9898_Q1_15 0x7EB3
#define CNST_0_5_Q2_14 0x2000
#define CNST_1_2_Q2_14 0x4CCC
//-----------------------------------------------------------------------------
// PURPOSE:
// Perform Spectral Amplitude Enhancement
//
//
// INPUT:
// IMBE_PARAM *imbe_param - pointer to IMBE_PARAM structure with
// valid num_harms, sa and fund_freq items
//
// OUTPUT:
// None
//
// RETURN:
// Enhanced Spectral Amplitudes
//
//-----------------------------------------------------------------------------
void sa_enh(IMBE_PARAM *imbe_param);
#endif
+368
View File
@@ -0,0 +1,368 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "tbls.h"
//-----------------------------------------------------------------------------
//
// Bit allocation and ancillary tables
//
//-----------------------------------------------------------------------------
const UWord16 bit_allocation_tbl[] =
{
0xA999, 0x9987,
0x9988, 0x8976, 0x5000,
0x8887, 0x7976, 0x5400,
0x8777, 0x7876, 0x5430,
0x7776, 0x6776, 0x5433,
0x7666, 0x6775, 0x4434, 0x3000,
0x7666, 0x5675, 0x4433, 0x3300,
0x6665, 0x5665, 0x4433, 0x3320,
0x6655, 0x5555, 0x4443, 0x3232,
0x6555, 0x5545, 0x5433, 0x3322, 0x2000,
0x6554, 0x4545, 0x4433, 0x3323, 0x2100,
0x6554, 0x4545, 0x4433, 0x2321, 0x3210,
0x5554, 0x4445, 0x4433, 0x2232, 0x1321,
0x5544, 0x4444, 0x4432, 0x3223, 0x2122, 0x1000,
0x5444, 0x4434, 0x4343, 0x2322, 0x2212, 0x2100,
0x5444, 0x4433, 0x4333, 0x3232, 0x1221, 0x2210,
0x5444, 0x3433, 0x4333, 0x3232, 0x1221, 0x2111,
0x5443, 0x3433, 0x4333, 0x2232, 0x1221, 0x1221, 0x1000,
0x5443, 0x3432, 0x4323, 0x2232, 0x2122, 0x1122, 0x1100,
0x4443, 0x3432, 0x4323, 0x2223, 0x2112, 0x2112, 0x1110,
0x4443, 0x3332, 0x4322, 0x3222, 0x3211, 0x2111, 0x2111,
0x4443, 0x3332, 0x2332, 0x2322, 0x1321, 0x1211, 0x1211, 0x1000,
0x4433, 0x3332, 0x2332, 0x2322, 0x1221, 0x1211, 0x1211, 0x1100,
0x4433, 0x3332, 0x2332, 0x2322, 0x1221, 0x1211, 0x1121, 0x1100,
0x4333, 0x3332, 0x2332, 0x2321, 0x1221, 0x1121, 0x1112, 0x1111,
0x4333, 0x3322, 0x2322, 0x2322, 0x1122, 0x1112, 0x1111, 0x2111, 0x0000,
0x4333, 0x3322, 0x2322, 0x2232, 0x1112, 0x2111, 0x2111, 0x0211, 0x1000,
0x4333, 0x3322, 0x2132, 0x2213, 0x2111, 0x2211, 0x1211, 0x1021, 0x1100,
0x4333, 0x2322, 0x2132, 0x2223, 0x2111, 0x2111, 0x1211, 0x1021, 0x1110,
0x4333, 0x2322, 0x2132, 0x2213, 0x2111, 0x2111, 0x1211, 0x1102, 0x1111, 0x0000,
0x4333, 0x2322, 0x2132, 0x2213, 0x2111, 0x2211, 0x1021, 0x1110, 0x2111, 0x0000,
0x4333, 0x2322, 0x2132, 0x2113, 0x2111, 0x1221, 0x1102, 0x1111, 0x0211, 0x1000,
0x4332, 0x2322, 0x1132, 0x2211, 0x3211, 0x1122, 0x1110, 0x2111, 0x1021, 0x1100,
0x4332, 0x2322, 0x2113, 0x2221, 0x1221, 0x1112, 0x2111, 0x0211, 0x1002, 0x1110, 0x0000,
0x4332, 0x2322, 0x2113, 0x2221, 0x1221, 0x1112, 0x1111, 0x0211, 0x1002, 0x1111, 0x0000,
0x4332, 0x2322, 0x1113, 0x2221, 0x1221, 0x1112, 0x1111, 0x0211, 0x1100, 0x2111, 0x1000,
0x4332, 0x2322, 0x1113, 0x2211, 0x1221, 0x1112, 0x2111, 0x0021, 0x1110, 0x0211, 0x1100,
0x3332, 0x2322, 0x1113, 0x2211, 0x1221, 0x1111, 0x2211, 0x1002, 0x1111, 0x0021, 0x1110, 0x0000,
0x3332, 0x2322, 0x1113, 0x2211, 0x1122, 0x1111, 0x1221, 0x1100, 0x2111, 0x1002, 0x1110, 0x0000,
0x3332, 0x2322, 0x1111, 0x3221, 0x1112, 0x2111, 0x1122, 0x1110, 0x0211, 0x1000, 0x2111, 0x0000,
0x3332, 0x2322, 0x1111, 0x3221, 0x1112, 0x2111, 0x1022, 0x1110, 0x0211, 0x1000, 0x2111, 0x1000,
0x3332, 0x2322, 0x1111, 0x3221, 0x1112, 0x2111, 0x1022, 0x1110, 0x0211, 0x1100, 0x0211, 0x1000, 0x0000,
0x3332, 0x2322, 0x1111, 0x3211, 0x1112, 0x2111, 0x1022, 0x1110, 0x0021, 0x1110, 0x0021, 0x1110, 0x0000,
0x3322, 0x2321, 0x1111, 0x3221, 0x1112, 0x2111, 0x1102, 0x2111, 0x0002, 0x1111, 0x0002, 0x1111, 0x0000,
0x3322, 0x2321, 0x1111, 0x3221, 0x1111, 0x2211, 0x1110, 0x2211, 0x1000, 0x2111, 0x1000, 0x2111, 0x0000,
0x3322, 0x2322, 0x1111, 0x0322, 0x1111, 0x0221, 0x1111, 0x0221, 0x1100, 0x0211, 0x1100, 0x0211, 0x1000, 0x0000,
0x3322, 0x2322, 0x1111, 0x0322, 0x1111, 0x0221, 0x1111, 0x0221, 0x1100, 0x0211, 0x1000, 0x0211, 0x1100, 0x0000,
0x3322, 0x2322, 0x1111, 0x0322, 0x1111, 0x0221, 0x1111, 0x0221, 0x1100, 0x0211, 0x1100, 0x0021, 0x1100, 0x0000
};
const UWord16 bit_allocation_offset_tbl[] =
{
0x0002, 0x000E, 0x001E, 0x0032, 0x004A, 0x0066, 0x0086, 0x00AA, 0x00D2, 0x00FE, 0x012E, 0x0162
};
//-----------------------------------------------------------------------------
//
// Log Magnitude Prediction Residual Block Length
//
//-----------------------------------------------------------------------------
const UWord32 lmprbl_tbl[] =
{
0x11122200,
0x11222200,
0x12222200,
0x22222200,
0x22222300,
0x22223300,
0x22233300,
0x22333300,
0x23333300,
0x33333300,
0x33333400,
0x33334400,
0x33344400,
0x33444400,
0x34444400,
0x44444400,
0x44444500,
0x44445500,
0x44455500,
0x44555500,
0x45555500,
0x55555500,
0x55555600,
0x55556600,
0x55566600,
0x55666600,
0x56666600,
0x66666600,
0x66666700,
0x66667700,
0x66677700,
0x66777700,
0x67777700,
0x77777700,
0x77777800,
0x77778800,
0x77788800,
0x77888800,
0x78888800,
0x88888800,
0x88888900,
0x88889900,
0x88899900,
0x88999900,
0x89999900,
0x99999900,
0x99999A00,
0x9999AA00
};
//-----------------------------------------------------------------------------
//
// Gain Quantizer Levels in signed Q5.11 format
//
//-----------------------------------------------------------------------------
const Word16 gain_qnt_tbl[] =
{
-5821, -5518, -5239, -4880, -4549, -4292, -4057, -3760, -3370, -2903,
-2583, -2305, -1962, -1601, -1138, -711, -302, 57, 433, 795,
1132, 1510, 1909, 2333, 2705, 3038, 3376, 3689, 3979, 4339,
4754, 5129, 5435, 5695, 5991, 6300, 6596, 6969, 7342, 7751,
8101, 8511, 8835, 9102, 9375, 9698, 10055, 10415, 10762, 11084,
11403, 11752, 12123, 12468, 12863, 13239, 13615, 13997, 14382, 14770,
15301, 15849, 16640, 17809
};
//-----------------------------------------------------------------------------
//
// Gain Ste Size in unsigned Q0.16 format
//
//-----------------------------------------------------------------------------
const UWord16 gain_step_size_tbl[] =
{
203, 263, 220, 190, 173,
406, 263, 440, 380, 346,
813, 527, 440, 760, 692,
813, 1054, 881, 760, 692,
1625, 1054, 881, 1425, 1298,
1625, 1976, 1652, 1425, 1298,
1625, 1976, 1652, 1425, 2422,
3047, 1976, 1652, 2661, 2422,
3047, 1976, 3083, 2661, 2422,
3047, 3688, 3083, 2661, 2422,
3047, 3688, 3083, 3801, 3460,
3047, 3688, 3083, 3801, 3460,
5689, 3688, 3083, 3801, 3460,
5689, 3688, 4404, 3801, 3460,
5689, 5269, 4404, 3801, 3460,
5689, 5269, 4404, 3801, 3460,
5689, 5269, 4404, 3801, 5623,
5689, 5269, 4404, 6177, 5623,
5689, 5269, 4404, 6177, 5623,
8126, 5269, 4404, 6177, 5623,
8126, 5269, 4404, 6177, 5623,
8126, 5269, 4404, 6177, 5623,
8126, 5269, 7157, 6177, 5623,
8126, 5269, 7157, 6177, 5623,
8126, 8562, 7157, 6177, 5623,
8126, 8562, 7157, 6177, 5623,
8126, 8562, 7157, 6177, 5623,
8126, 8562, 7157, 6177, 5623,
8126, 8562, 7157, 6177, 7353,
8126, 8562, 7157, 6177, 7353,
8126, 8562, 7157, 6177, 7353,
8126, 8562, 7157, 6177, 7353,
8126, 8562, 7157, 8077, 7353,
8126, 8562, 7157, 8077, 7353,
8126, 8562, 7157, 8077, 7353,
8126, 8562, 7157, 8077, 7353,
8126, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 7157, 8077, 7353,
13206, 8562, 9359, 8077, 7353,
13206, 8562, 9359, 8077, 7353,
13206, 8562, 9359, 8077, 7353,
13206, 8562, 9359, 8077, 7353,
13206, 8562, 9359, 8077, 7353
};
//-----------------------------------------------------------------------------
//
// Standard Deviation of Higher Order DCT Coefficients in unsigned Q0.16 format
//
//-----------------------------------------------------------------------------
const UWord16 hi_ord_std_tbl[] =
{
20120, 15794, 13566, 12452, 11731, 11338, 10813, 11141, 11141
};
//-----------------------------------------------------------------------------
//
// Quantizer Step for Higher Order DCT Coefficients in unsigned Q1.15 format
//
//-----------------------------------------------------------------------------
const UWord16 hi_ord_step_size_tbl[] =
{
39322, 27853, 21299, 13107, 9175, 4915, 2621, 1311, 655, 328
};
//-----------------------------------------------------------------------------
//
// Speech Synthesis Window
//
//-----------------------------------------------------------------------------
const Word16 ws[49] =
{
655, 1310, 1966, 2621, 3276, 3932, 4587, 5242, 5898, 6553,
7208, 7864, 8519, 9175, 9830, 10485, 11141, 11796, 12451, 13107,
13762, 14417, 15073, 15728, 16384, 17039, 17694, 18350, 19005, 19660,
20316, 20971, 21626, 22282, 22937, 23592, 24248, 24903, 25559, 26214,
26869, 27525, 28180, 28835, 29491, 30146, 30801, 31457, 32112
};
/*
const Word16 ws_ws[49] =
{
17052, 17746, 18467, 19212, 19980, 20770, 21580, 22407, 23246, 24094,
24945, 25793, 26631, 27453, 28248, 29008, 29724, 30385, 30983, 31507,
31950, 32302, 32559, 32715, 32767, 32715, 32559, 32302, 31950, 31507,
30983, 30385, 29724, 29008, 28248, 27453, 26631, 25793, 24945, 24094,
23246, 22407, 21580, 20770, 19980, 19212, 18467, 17746, 17052
};
*/
//-----------------------------------------------------------------------------
//
// Squared Pitch Estimation Window 64*wi^2
//
//-----------------------------------------------------------------------------
const Word16 wi[301] =
{
15, 18, 21, 25, 29, 34, 40, 46, 52, 59,
67, 76, 86, 96, 108, 120, 133, 148, 164, 180,
199, 218, 239, 261, 285, 311, 338, 367, 398, 431,
465, 502, 541, 582, 625, 671, 719, 769, 822, 878,
936, 997, 1061, 1128, 1198, 1271, 1347, 1426, 1508, 1594,
1683, 1775, 1871, 1970, 2073, 2179, 2289, 2403, 2520, 2641,
2766, 2894, 3026, 3162, 3301, 3445, 3592, 3742, 3897, 4055,
4216, 4382, 4551, 4723, 4899, 5078, 5261, 5447, 5636, 5828,
6023, 6221, 6423, 6626, 6833, 7042, 7253, 7467, 7683, 7900,
8120, 8341, 8564, 8788, 9014, 9241, 9468, 9696, 9925, 10154,
10383, 10613, 10842, 11070, 11298, 11525, 11752, 11977, 12200, 12422,
12642, 12860, 13076, 13289, 13500, 13708, 13912, 14114, 14312, 14506,
14696, 14882, 15064, 15242, 15414, 15582, 15745, 15903, 16055, 16202,
16343, 16478, 16608, 16731, 16848, 16958, 17062, 17159, 17250, 17333,
17410, 17480, 17542, 17597, 17646, 17686, 17720, 17746, 17764, 17776,
17779, 17776, 17764, 17746, 17720, 17686, 17646, 17597, 17542, 17480,
17410, 17333, 17250, 17159, 17062, 16958, 16848, 16731, 16608, 16478,
16343, 16202, 16055, 15903, 15745, 15582, 15414, 15242, 15064, 14882,
14696, 14506, 14312, 14114, 13912, 13708, 13500, 13289, 13076, 12860,
12642, 12422, 12200, 11977, 11752, 11525, 11298, 11070, 10842, 10613,
10383, 10154, 9925, 9696, 9468, 9241, 9014, 8788, 8564, 8341,
8120, 7900, 7683, 7467, 7253, 7042, 6833, 6626, 6423, 6221,
6023, 5828, 5636, 5447, 5261, 5078, 4899, 4723, 4551, 4382,
4216, 4055, 3897, 3742, 3592, 3445, 3301, 3162, 3026, 2894,
2766, 2641, 2520, 2403, 2289, 2179, 2073, 1970, 1871, 1775,
1683, 1594, 1508, 1426, 1347, 1271, 1198, 1128, 1061, 997,
936, 878, 822, 769, 719, 671, 625, 582, 541, 502,
465, 431, 398, 367, 338, 311, 285, 261, 239, 218,
199, 180, 164, 148, 133, 120, 108, 96, 86, 76,
67, 59, 52, 46, 40, 34, 29, 25, 21, 18,
15
};
//-----------------------------------------------------------------------------
//
// Pitch Refinement Window
//
//-----------------------------------------------------------------------------
const Word16 wr[] =
{
487, 570, 658, 753, 854, 962, 1076, 1197, 1325, 1460,
1602, 1752, 1909, 2074, 2246, 2426, 2614, 2810, 3014, 3227,
3447, 3675, 3912, 4157, 4410, 4671, 4941, 5219, 5505, 5799,
6101, 6411, 6729, 7054, 7388, 7728, 8077, 8432, 8795, 9164,
9541, 9923, 10312, 10707, 11108, 11515, 11927, 12344, 12766, 13192,
13622, 14056, 14494, 14935, 15379, 15825, 16273, 16724, 17175, 17628,
18081, 18534, 18988, 19440, 19892, 20342, 20790, 21237, 21680, 22120,
22557, 22990, 23419, 23842, 24261, 24673, 25080, 25480, 25874, 26259,
26638, 27008, 27370, 27722, 28066, 28400, 28724, 29037, 29340, 29632,
29912, 30181, 30438, 30683, 30915, 31135, 31341, 31535, 31715, 31881,
32033, 32172, 32296, 32406, 32502, 32583, 32649, 32701, 32738, 32760,
};
//-----------------------------------------------------------------------------
//
// Real Part Spectrum of Pitch Refinement Window * 256
//
//-----------------------------------------------------------------------------
const Word16 wr_sp[] =
{
16, 43, 71, 102, 135, 170, 207, 247, 289, 333,
380, 430, 483, 538, 596, 657, 721, 787, 857, 930,
1006, 1085, 1168, 1254, 1343, 1435, 1531, 1630, 1733, 1840,
1950, 2063, 2181, 2302, 2426, 2555, 2687, 2823, 2962, 3106,
3253, 3404, 3559, 3718, 3881, 4047, 4218, 4392, 4570, 4751,
4937, 5126, 5319, 5516, 5716, 5920, 6128, 6339, 6554, 6772,
6994, 7219, 7447, 7679, 7914, 8152, 8393, 8637, 8884, 9134,
9387, 9642, 9900, 10160, 10423, 10688, 10956, 11225, 11497, 11770,
12045, 12322, 12601, 12881, 13162, 13444, 13728, 14012, 14298, 14584,
14871, 15158, 15445, 15733, 16020, 16308, 16595, 16882, 17168, 17454,
17738, 18022, 18305, 18586, 18866, 19145, 19421, 19696, 19969, 20240,
20508, 20774, 21037, 21298, 21556, 21811, 22062, 22310, 22555, 22796,
23033, 23267, 23496, 23722, 23943, 24159, 24372, 24579, 24782, 24980,
25172, 25360, 25542, 25719, 25891, 26057, 26217, 26372, 26520, 26663,
26800, 26930, 27055, 27173, 27285, 27390, 27489, 27581, 27667, 27746,
27818, 27884, 27942, 27994, 28039, 28078, 28109, 28133, 28151, 28161,
28165, 28161, 28151, 28133, 28109, 28078, 28039, 27994, 27942, 27884,
27818, 27746, 27667, 27581, 27489, 27390, 27285, 27173, 27055, 26930,
26800, 26663, 26520, 26372, 26217, 26057, 25891, 25719, 25542, 25360,
25172, 24980, 24782, 24579, 24372, 24159, 23943, 23722, 23496, 23267,
23033, 22796, 22555, 22310, 22062, 21811, 21556, 21298, 21037, 20774,
20508, 20240, 19969, 19696, 19421, 19145, 18866, 18586, 18305, 18022,
17738, 17454, 17168, 16882, 16595, 16308, 16020, 15733, 15445, 15158,
14871, 14584, 14298, 14012, 13728, 13444, 13162, 12881, 12601, 12322,
12045, 11770, 11497, 11225, 10956, 10688, 10423, 10160, 9900, 9642,
9387, 9134, 8884, 8637, 8393, 8152, 7914, 7679, 7447, 7219,
6994, 6772, 6554, 6339, 6128, 5920, 5716, 5516, 5319, 5126,
4937, 4751, 4570, 4392, 4218, 4047, 3881, 3718, 3559, 3404,
3253, 3106, 2962, 2823, 2687, 2555, 2426, 2302, 2181, 2063,
1950, 1840, 1733, 1630, 1531, 1435, 1343, 1254, 1168, 1085,
1006, 930, 857, 787, 721, 657, 596, 538, 483, 430,
380, 333, 289, 247, 207, 170, 135, 102, 71, 43,
16
};
+99
View File
@@ -0,0 +1,99 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _TBLS
#define _TBLS
//-----------------------------------------------------------------------------
//
// Bit allocation and ancillary tables
//
//-----------------------------------------------------------------------------
extern const UWord16 bit_allocation_tbl[];
extern const UWord16 bit_allocation_offset_tbl[];
//-----------------------------------------------------------------------------
//
// Log Magnitude Prediction Residual Block Length
//
//-----------------------------------------------------------------------------
extern const UWord32 lmprbl_tbl[];
//-----------------------------------------------------------------------------
//
// Gain Quantizer Levels in Q5.11 format
//
//-----------------------------------------------------------------------------
extern const Word16 gain_qnt_tbl[];
#define GAIN_QNT_TBL_SIZE 64
//-----------------------------------------------------------------------------
//
// Gain Ste Size in unsigned Q0.16 format
//
//-----------------------------------------------------------------------------
extern const UWord16 gain_step_size_tbl[];
//-----------------------------------------------------------------------------
//
// Standard Deviation of Higher Order DCT Coefficients in unsigned Q0.16 format
//
//-----------------------------------------------------------------------------
extern const UWord16 hi_ord_std_tbl[];
//-----------------------------------------------------------------------------
//
// Quantizer Step for Higher Order DCT Coefficients in unsigned Q1.15 format
//
//-----------------------------------------------------------------------------
extern const UWord16 hi_ord_step_size_tbl[];
//-----------------------------------------------------------------------------
//
// Speech Synthesis Window
//
//-----------------------------------------------------------------------------
extern const Word16 ws[];
//-----------------------------------------------------------------------------
//
// Squared Pitch Estimation Window 64*wi^2
//
//-----------------------------------------------------------------------------
extern const Word16 wi[];
//-----------------------------------------------------------------------------
//
// Pitch Refinement Window
//
//-----------------------------------------------------------------------------
extern const Word16 wr[];
//-----------------------------------------------------------------------------
//
// Real Part Spectrum of Pitch Refinement Window * 256
//
//-----------------------------------------------------------------------------
extern const Word16 wr_sp[];
#endif
+74
View File
@@ -0,0 +1,74 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
/*
********************************************************************************
*
* GSM AMR speech codec Version 7.4.0 January 31, 2000
*
********************************************************************************
*
* File : typedef.c
* Purpose : Basic types.
*
********************************************************************************
*/
#ifndef typedef_h
#define typedef_h "$Id $"
#undef ORIGINAL_TYPEDEF_H /* define to get "original" ETSI version
of typedef.h */
#ifdef ORIGINAL_TYPEDEF_H
/*
* this is the original code from the ETSI file typedef.h
*/
#if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(_MSC_VER) || defined(__ZTC__)
typedef signed char Word8;
typedef short Word16;
typedef long Word32;
typedef int Flag;
#elif defined(__sun)
typedef signed char Word8;
typedef short Word16;
typedef long Word32;
typedef int Flag;
#elif defined(__unix__) || defined(__unix)
typedef signed char Word8;
typedef short Word16;
typedef int Word32;
typedef int Flag;
#endif
#else /* not original typedef.h */
/*
* use (improved) type definition file typdefs.h and add a "Flag" type
*/
#include "typedefs.h"
typedef int Flag;
#endif
#endif
+201
View File
@@ -0,0 +1,201 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
/*
********************************************************************************
*
* GSM AMR speech codec Version 7.4.0 January 31, 2000
*
********************************************************************************
*
* File : typedefs.h
* Description : Definition of platform independent data
* types and constants
*
*
* The following platform independent data types and corresponding
* preprocessor (#define) constants are defined:
*
* defined type meaning corresponding constants
* ----------------------------------------------------------
* Char character (none)
* Bool boolean true, false
* Word8 8-bit signed minWord8, maxWord8
* UWord8 8-bit unsigned minUWord8, maxUWord8
* Word16 16-bit signed minWord16, maxWord16
* UWord16 16-bit unsigned minUWord16, maxUWord16
* Word32 32-bit signed minWord32, maxWord32
* UWord32 32-bit unsigned minUWord32, maxUWord32
* Float floating point minFloat, maxFloat
*
*
* The following compile switches are #defined:
*
* PLATFORM string indicating platform progam is compiled on
* possible values: "OSF", "PC", "SUN"
*
* OSF only defined if the current platform is an Alpha
* PC only defined if the current platform is a PC
* SUN only defined if the current platform is a Sun
*
* LSBFIRST is defined if the byte order on this platform is
* "least significant byte first" -> defined on DEC Alpha
* and PC, undefined on Sun
*
********************************************************************************
*/
#ifndef typedefs_h
#define typedefs_h "$Id $"
/*
********************************************************************************
* INCLUDE FILES
********************************************************************************
*/
#include <cfloat>
#include <climits>
#define __MSDOS__
/*
********************************************************************************
* DEFINITION OF CONSTANTS
********************************************************************************
*/
/*
********* define char type
*/
typedef char Char;
/*
********* define 8 bit signed/unsigned types & constants
*/
#if SCHAR_MAX == 127
typedef signed char Word8;
#define minWord8 SCHAR_MIN
#define maxWord8 SCHAR_MAX
typedef unsigned char UWord8;
#define minUWord8 0
#define maxUWord8 UCHAR_MAX
#else
#error cannot find 8-bit type
#endif
/*
********* define 16 bit signed/unsigned types & constants
*/
#if INT_MAX == 32767
typedef int Word16;
#define minWord16 INT_MIN
#define maxWord16 INT_MAX
typedef unsigned int UWord16;
#define minUWord16 0
#define maxUWord16 UINT_MAX
#elif SHRT_MAX == 32767
typedef short Word16;
#define minWord16 SHRT_MIN
#define maxWord16 SHRT_MAX
typedef unsigned short UWord16;
#define minUWord16 0
#define maxUWord16 USHRT_MAX
#else
#error cannot find 16-bit type
#endif
/*
********* define 32 bit signed/unsigned types & constants
*/
#if INT_MAX == 2147483647
typedef int Word32;
#define minWord32 INT_MIN
#define maxWord32 INT_MAX
typedef unsigned int UWord32;
#define minUWord32 0
#define maxUWord32 UINT_MAX
#elif LONG_MAX == 2147483647
typedef long Word32;
#define minWord32 LONG_MIN
#define maxWord32 LONG_MAX
typedef unsigned long UWord32;
#define minUWord32 0
#define maxUWord32 ULONG_MAX
#else
#error cannot find 32-bit type
#endif
/*
********* define floating point type & constants
*/
/* use "#if 0" below if Float should be double;
use "#if 1" below if Float should be float
*/
#if 0
typedef float Float;
#define maxFloat FLT_MAX
#define minFloat FLT_MIN
#else
typedef double Float;
#define maxFloat DBL_MAX
#define minFloat DBL_MIN
#endif
/*
********* define complex type
*/
typedef struct {
Float r; /* real part */
Float i; /* imaginary part */
} CPX;
/*
********* define boolean type
*/
typedef int Bool;
#define false 0
#define true 1
/*
********* Check current platform
*/
#if defined(__MSDOS__)
#define PC
#define PLATFORM "PC"
#define LSBFIRST
#elif defined(__osf__)
#define OSF
#define PLATFORM "OSF"
#define LSBFIRST
#elif defined(__sun__) || defined(__sun)
#define SUN
#define PLATFORM "SUN"
#undef LSBFIRST
#elif defined(linux) && defined(i386)
#define PC
#define PLATFORM "PC"
#define LSBFIRST
#else
#error "can't determine architecture; adapt typedefs.h to your platform"
#endif
#endif
+147
View File
@@ -0,0 +1,147 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "aux_sub.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include "uv_synt.h"
#include "rand_gen.h"
#include "tbls.h"
#include "encode.h"
#include "imbe_vocoder.h"
void imbe_vocoder::uv_synt_init(void)
{
fft_init();
v_zap(uv_mem, 105);
}
void imbe_vocoder::uv_synt(IMBE_PARAM *imbe_param, Word16 *snd)
{
Cmplx16 Uw[FFTLENGTH];
Word16 i, index_a, index_b, index_aux, ha, hb, *v_uv_dsn_ptr, *sa_ptr, sa;
Word32 fund_freq, fund_freq_2, fund_freq_acc_a, fund_freq_acc_b;
sa_ptr = imbe_param->sa;
v_uv_dsn_ptr = imbe_param->v_uv_dsn;
fund_freq = imbe_param->fund_freq;
fund_freq_2 = L_shr(fund_freq, 1);
v_zap((Word16 *)&Uw, 2 * FFTLENGTH);
fund_freq_acc_a = L_sub(fund_freq, fund_freq_2);
fund_freq_acc_b = L_add(fund_freq, fund_freq_2);
for(i = 0; i < imbe_param->num_harms; i++)
{
ha = extract_h(fund_freq_acc_a);
hb = extract_h(fund_freq_acc_b);
index_a = (ha >> 8) + ((ha & 0xFF)?1:0);
index_b = (hb >> 8) + ((hb & 0xFF)?1:0);
index_aux = 256 - index_a;
sa = shl(*sa_ptr, 3);
if(*v_uv_dsn_ptr++)
{
while(index_a < index_b)
{
Uw[index_a].re = Uw[index_a].im = 0;
Uw[index_aux].re = Uw[index_aux].im = 0;
index_a++;
index_aux--;
}
}
else
{
while(index_a < index_b)
{
Uw[index_a].re = mult(sa, rand_gen());
Uw[index_a].im = mult(sa, rand_gen());
//Uw[index_a].re = sa;
//Uw[index_a].im = sa;
Uw[index_aux].re = Uw[index_a].re;
Uw[index_aux].im = negate(Uw[index_a].im);
index_a++;
index_aux--;
}
}
fund_freq_acc_a = L_add(fund_freq_acc_a, fund_freq);
fund_freq_acc_b = L_add(fund_freq_acc_b, fund_freq);
sa_ptr++;
}
/*
j = 128;
for(i = 0; i < 128; i++)
Uw_tmp[j++] = Uw[i];
j = 128;
for(i = 0; i < 128; i++)
Uw_tmp[i] = Uw[j++];
for(i = 0; i < 256; i++)
Uw[i] = Uw_tmp[i];
*/
fft((Word16 *)&Uw, FFTLENGTH, -1);
for(i = 0; i < 105; i++)
snd[i] = uv_mem[i];
index_aux = 73;
for(i = 105; i < FRAME; i++)
snd[i] = shl(Uw[index_aux++].re, 3);
// Weighted Overlap Add Algorithm
index_aux = 24;
index_a = 0;
index_b = 48;
for(i = 56; i < 105; i++)
{
snd[i] = extract_h(L_add(L_mult(snd[i], ws[index_b]), L_mult(shl(Uw[index_aux].re, 3), ws[index_a])));
index_aux++;
index_a++;
index_b--;
}
index_aux = 128;
for(i = 0; i < 105; i++)
uv_mem[i] = shl(Uw[index_aux++].re, 3);
}
+29
View File
@@ -0,0 +1,29 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _UV_SYNT
#define _UV_SYNT
void uv_synt_init(void);
void uv_synt(IMBE_PARAM *imbe_param, Word16 *snd);
void fft(Word16 *datam1, Word16 nn, Word16 isign);
#endif
+248
View File
@@ -0,0 +1,248 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "basic_op.h"
#include "imbe.h"
#include "aux_sub.h"
#include "dsp_sub.h"
#include "math_sub.h"
#include "v_synt.h"
#include "rand_gen.h"
#include "tbls.h"
#include "encode.h"
#include "imbe_vocoder.h"
#define CNST_0_1_Q1_15 0x0CCD
void imbe_vocoder::v_synt_init(void)
{
Word16 i;
for(i = 0; i < NUM_HARMS_MAX; i++)
{
ph_mem[i] = L_deposit_h(rand_gen());
vu_dsn_prev[i] = 0;
}
num_harms_prev3 = 0;
fund_freq_prev = 0;
}
void imbe_vocoder::v_synt(IMBE_PARAM *imbe_param, Word16 *snd)
{
Word32 L_tmp, L_tmp1, fund_freq, L_snd[FRAME], L_ph_acc, L_ph_step;
Word32 L_ph_acc_aux, L_ph_step_prev, L_amp_acc, L_amp_step, L_ph_step_aux;
Word16 num_harms, i, j, *vu_dsn, *sa, *s_ptr, *s_ptr_aux, num_harms_max, num_harms_max_4;
UWord32 ph_mem_prev[NUM_HARMS_MAX], dph[NUM_HARMS_MAX];
Word16 num_harms_inv, num_harms_sh, num_uv;
Word16 freq_flag;
fund_freq = imbe_param->fund_freq;
num_harms = imbe_param->num_harms;
vu_dsn = imbe_param->v_uv_dsn;
sa = imbe_param->sa;
num_harms_inv = imbe_param->div_one_by_num_harm;
num_harms_sh = imbe_param->div_one_by_num_harm_sh;
num_uv = imbe_param->l_uv;
for(i = 0; i < FRAME; i++)
L_snd[i] = 0;
// Update phases (calculated phase value correspond to bound of frame)
L_tmp = (((fund_freq_prev + fund_freq) >> 7) * FRAME/2) << 7; // It is performed integer multiplication by mod 1
L_ph_acc = 0;
for(i = 0; i < NUM_HARMS_MAX; i++)
{
ph_mem_prev[i] = ph_mem[i];
L_ph_acc += L_tmp;
ph_mem[i] += L_ph_acc;
dph[i] = 0;
}
num_harms_max = (num_harms >= num_harms_prev3)?num_harms:num_harms_prev3;
num_harms_max_4 = num_harms_max >> 2;
if(L_abs(L_sub(fund_freq, fund_freq_prev)) >= L_mpy_ls(fund_freq, CNST_0_1_Q1_15))
freq_flag = 1;
else
freq_flag = 0;
L_ph_step = L_ph_step_prev = 0;
for(i = 0; i < num_harms_max; i++)
{
L_ph_step += fund_freq;
L_ph_step_prev += fund_freq_prev;
if(i > num_harms_max_4)
{
if(num_uv == num_harms)
{
dph[i] = L_deposit_h(rand_gen());
}
else
{
L_tmp = L_mult(rand_gen(), num_harms_inv);
dph[i] = L_shr(L_tmp, 15 - num_harms_sh) * num_uv;
}
ph_mem[i] += dph[i];
}
if(vu_dsn[i] == 0 && vu_dsn_prev[i] == 0)
continue;
if(vu_dsn[i] == 1 && vu_dsn_prev[i] == 0) // unvoiced => voiced
{
s_ptr = (Word16 *)ws;
L_ph_acc = ph_mem[i] - (((L_ph_step >> 7) * 104) << 7);
for(j = 56; j <= 104; j++)
{
L_tmp = L_mult(*s_ptr++, sa[i]);
L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc += L_ph_step;
}
for(j = 105; j <= 159; j++)
{
L_tmp = L_mult(sa[i], cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc += L_ph_step;
}
continue;
}
if(vu_dsn[i] == 0 && vu_dsn_prev[i] == 1) // voiced => unvoiced
{
s_ptr = (Word16 *)&ws[48];
L_ph_acc = ph_mem_prev[i];
for(j = 0; j <= 55; j++)
{
L_tmp = L_mult(sa_prev3[i], cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc += L_ph_step_prev;
}
for(j = 56; j <= 104; j++)
{
L_tmp = L_mult(*s_ptr--, sa_prev3[i]);
L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc += L_ph_step_prev;
}
continue;
}
if(i >=7 || freq_flag)
{
s_ptr_aux = (Word16 *)&ws[48];
L_ph_acc_aux = ph_mem_prev[i];
s_ptr = (Word16 *)ws;
L_ph_acc = ph_mem[i] - (((L_ph_step >> 7) * 104) << 7);
for(j = 0; j <= 55; j++)
{
L_tmp = L_mult(sa_prev3[i], cos_fxp(extract_h(L_ph_acc_aux)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc_aux += L_ph_step_prev;
}
for(j = 56; j <= 104; j++)
{
L_tmp = L_mult(*s_ptr_aux--, sa_prev3[i]);
L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc_aux)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_tmp = L_mult(*s_ptr++, sa[i]);
L_tmp = L_mpy_ls(L_tmp, cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc_aux += L_ph_step_prev;
L_ph_acc += L_ph_step;
}
for(j = 105; j <= 159; j++)
{
L_tmp = L_mult(sa[i], cos_fxp(extract_h(L_ph_acc)));
L_tmp = L_shr(L_tmp, 1);
L_snd[j] = L_add(L_snd[j], L_tmp);
L_ph_acc += L_ph_step;
}
continue;
}
L_amp_step = L_mpy_ls(L_shr(L_deposit_h(sub(sa[i], sa_prev3[i])), 4 + 1), CNST_0_1_Q1_15); // (sa[i] - sa_prev3[i]) / 160, 1/160 = 0.1/16
L_amp_acc = L_shr(L_deposit_h(sa_prev3[i]), 1);
L_ph_step_aux = L_mpy_ls(L_shr(fund_freq - fund_freq_prev, 4 + 1), CNST_0_1_Q1_15); // (fund_freq - fund_freq_prev)/(2*160)
L_ph_step_aux = ((L_ph_step_aux >> 7) * (i + 1)) << 7;
L_ph_acc = ph_mem_prev[i];
L_tmp1 = L_mpy_ls(L_shr(dph[i], 4), CNST_0_1_Q1_15); // dph[i] / 160
for(j = 0; j < 160; j++)
{
L_ph_acc_aux = ((L_ph_step_aux >> 9) * j) << 9;
L_ph_acc_aux = ((L_ph_acc_aux >> 9) * j) << 9;
L_tmp = L_mpy_ls(L_amp_acc, cos_fxp(extract_h(L_ph_acc + L_ph_acc_aux)));
L_snd[j] = L_add(L_snd[j], L_tmp);
L_amp_acc = L_add(L_amp_acc, L_amp_step);
L_ph_acc += L_ph_step_prev;
L_ph_acc += L_tmp1;
}
}
for(i = 0; i < FRAME; i++)
*snd++ = extract_h(L_snd[i]);
v_zap(vu_dsn_prev, NUM_HARMS_MAX);
v_equ(vu_dsn_prev, imbe_param->v_uv_dsn, num_harms);
v_equ(sa_prev3, imbe_param->sa, num_harms);
num_harms_prev3 = num_harms;
fund_freq_prev = fund_freq;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _V_SYNT
#define _V_SYNT
void v_synt_init(void);
void v_synt(IMBE_PARAM *imbe_param, Word16 *snd);
#endif
+373
View File
@@ -0,0 +1,373 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#include "typedef.h"
#include "globals.h"
#include "imbe.h"
#include "basic_op.h"
#include "aux_sub.h"
#include "math_sub.h"
#include "dsp_sub.h"
#include "tbls.h"
#include "v_uv_det.h"
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include "encode.h"
#include "imbe_vocoder.h"
#define CNST_0_5625_Q1_15 0x4800
#define CNST_0_45_Q1_15 0x3999
#define CNST_0_1741_Q1_15 0x164A
#define CNST_0_1393_Q1_15 0x11D5
#define CNST_0_99_Q1_15 0x7EB8
#define CNST_0_01_Q1_15 0x0148
#define CNST_0_0025_Q1_15 0x0051
#define CNST_0_25_Q1_15 0x2000
#define CNST_PI_4_Q1_15 0x6488
#define CNST_0_55_Q4_12 0x08CD
extern FILE *fp_in, *fp_out;
extern int frame_cnt;
void imbe_vocoder::pitch_ref_init(void)
{
v_zap(v_uv_dsn, NUM_BANDS_MAX);
th_max = 0;
}
Word16 imbe_vocoder::voiced_sa_calc(Word32 num, Word16 den)
{
Word16 tmp;
Word32 L_tmp;
L_tmp = L_mpy_ls(num, den);
L_tmp = sqrt_l_exp(L_tmp, &tmp);
L_tmp = L_shr(L_tmp, tmp - 3);
//L_tmp =0;
//return (Word16)(2*256.0*sqrt(2*(double)num/(double)den));
return extract_h(L_tmp);
}
Word16 imbe_vocoder::unvoiced_sa_calc(Word32 num, Word16 den)
{
Word16 shift, tmp;
Word32 L_tmp;
shift = norm_s(den);
tmp = div_s(0x4000, shl(den, shift));
L_tmp = L_shl(L_mpy_ls(num, tmp), shift + 2);
L_tmp = sqrt_l_exp(L_tmp, &tmp);
L_tmp = L_shr(L_tmp, tmp - 2 + 6);
L_tmp = L_mpy_ls(L_tmp, 0x4A76);
//L_tmp =0;
//return (Word16)(2*0.1454 * sqrt(2*256*(double)num/(double)den));
return extract_h(L_tmp);
}
//=============================================================================
//
// Voiced/Unvoiced Determination & Spectral Amplitudes Estimation
//
//=============================================================================
void imbe_vocoder::v_uv_det(IMBE_PARAM *imbe_param, Cmplx16 *fft_buf)
{
Word16 i, j, index_a_save, tmp, index_wr;
Word32 fund_freq, fund_freq_2, fund_freq_acc_a, fund_freq_acc_b, fund_freq_acc, fund_fr_acc, L_tmp, amp_re_acc, amp_im_acc;
Word16 ha, hb, index_a, index_b, index_tbl[30], it_ind, re_tmp, im_tmp, re_tmp2, im_tmp2, sc_coef;
Word32 Sw_sum, M_num[NUM_HARMS_MAX], M_num_sum, M_den_sum, D_num, D_den, th_lf, th_hf, th0, fund_fr_step, M_fcn_num, M_fcn_den;
Word16 sp_rec_re, sp_rec_im, M_fcn;
Word16 band_cnt, num_harms_cnt, uv_harms_cnt, Dk;
Word16 num_harms, num_bands, dsn_thr=0;
Word16 thr[NUM_BANDS_MAX], M_den[NUM_HARMS_MAX], b1_vec;
fund_freq = imbe_param->fund_freq;
tmp = shr( add( shr(imbe_param->ref_pitch, 1), CNST_0_25_Q8_8), 8); // fix(pitch_cand / 2 + 0.5)
num_harms = extract_h((UWord32)CNST_0_9254_Q0_16 * tmp); // fix(0.9254 * fix(pitch_cand / 2 + 0.5))
if(num_harms < NUM_HARMS_MIN)
num_harms = NUM_HARMS_MIN;
else if(num_harms > NUM_HARMS_MAX)
num_harms = NUM_HARMS_MAX;
if(num_harms <= 36)
num_bands = extract_h((UWord32)(num_harms + 2) * CNST_0_33_Q0_16); // fix((L+2)/3)
else
num_bands = NUM_BANDS_MAX;
imbe_param->num_harms = num_harms;
imbe_param->num_bands = num_bands;
//=========================================================================
//
// M(th) function calculation
//
//=========================================================================
for(j = 0, th_lf = 0; j < 64; j++)
{
th_lf = L_mac(th_lf, fft_buf[j].re, fft_buf[j].re);
th_lf = L_mac(th_lf, fft_buf[j].im, fft_buf[j].im);
}
for(j = 64, th_hf = 0; j < 128; j++)
{
th_hf = L_mac(th_hf, fft_buf[j].re, fft_buf[j].re);
th_hf = L_mac(th_hf, fft_buf[j].im, fft_buf[j].im);
}
th0 = L_add(th_lf, th_hf);
if(th0 > th_max)
th_max = L_shr(L_add(th_max, th0), 1);
else
th_max = L_add(L_mpy_ls(th_max, CNST_0_99_Q1_15), L_mpy_ls(th0, CNST_0_01_Q1_15));
M_fcn_num = L_add(th0, L_mpy_ls(th_max, CNST_0_0025_Q1_15));
M_fcn_den = L_add(th0, L_mpy_ls(th_max, CNST_0_01_Q1_15));
if(M_fcn_den == 0)
M_fcn = CNST_0_25_Q1_15;
else
{
tmp = norm_l(M_fcn_den);
M_fcn_den = L_shl(M_fcn_den, tmp);
M_fcn_num = L_shl(M_fcn_num, tmp);
M_fcn = div_s(extract_h(M_fcn_num), extract_h(M_fcn_den));
if(th_lf < (L_tmp= L_add(L_shl(th_hf, 2), th_hf))) // compare th_lf < 5*th_hf
{
tmp = norm_l(L_tmp);
M_fcn_den = L_shl(L_tmp, tmp);
th_lf = L_shl(th_lf, tmp);
tmp = div_s(extract_h(th_lf), extract_h(M_fcn_den));
L_tmp = sqrt_l_exp(L_deposit_h(tmp), &tmp);
if(tmp)
L_tmp = L_shr(L_tmp, tmp);
M_fcn = mult(M_fcn, extract_h(L_tmp));
}
}
// ========================================================================
fund_fr_step = L_shl(L_mpy_ls(fund_freq, CNST_PI_4_Q1_15), 2); // mult by PI
uv_harms_cnt = 0;
b1_vec = 0;
band_cnt = 0;
num_harms_cnt = 0;
Sw_sum = 0;
D_num = D_den = 0;
fund_fr_acc = 0;
fund_freq_acc = fund_freq;
fund_freq_2 = L_shr(fund_freq, 1);
fund_freq_acc_a = L_sub(fund_freq, fund_freq_2);
fund_freq_acc_b = L_add(fund_freq, fund_freq_2);
for(j = 0; j < num_harms; j++)
{
ha = extract_h(fund_freq_acc_a);
hb = extract_h(fund_freq_acc_b);
index_a = (ha >> 8) + ((ha & 0xFF)?1:0);
index_b = (hb >> 8) + ((hb & 0xFF)?1:0);
L_tmp = L_shl(L_deposit_h(index_a), 8);
L_tmp = L_sub(L_tmp, fund_freq_acc);
L_tmp = L_add(L_tmp, 0x00020000); // for rounding purpose
L_tmp = L_shr(L_tmp, 2);
index_a_save = index_a;
it_ind = 0;
// =========== v/uv determination threshold function ==
if(num_harms_cnt == 0) // calculate one time per band
{
if(imbe_param->e_p > CNST_0_55_Q4_12 && band_cnt >= 1)
dsn_thr = 0;
else if(v_uv_dsn[band_cnt] == 1)
dsn_thr = mult(M_fcn, sub(CNST_0_5625_Q1_15, mult(CNST_0_1741_Q1_15, extract_h(fund_fr_acc))));
else
dsn_thr = mult(M_fcn, sub(CNST_0_45_Q1_15, mult(CNST_0_1393_Q1_15, extract_h(fund_fr_acc))));
fund_fr_acc = L_add(fund_fr_acc, fund_fr_step);
thr[band_cnt] = dsn_thr;
}
// ====================================================
M_den_sum = 0;
amp_re_acc = amp_im_acc = 0;
while(index_a < index_b)
{
index_wr = extract_h(L_tmp);
if(index_wr < 0 && (L_tmp & 0xFFFF)) // truncating for negative number
index_wr = add(index_wr, 1);
index_wr = add(index_wr, 160);
index_tbl[it_ind++] = index_wr;
if(index_wr >= 0 && index_wr <= 320)
{
amp_re_acc = L_mac(amp_re_acc, fft_buf[index_a].re, wr_sp[index_wr]);
amp_im_acc = L_mac(amp_im_acc, fft_buf[index_a].im, wr_sp[index_wr]);
M_den_sum = L_add(M_den_sum, mult(wr_sp[index_wr], wr_sp[index_wr]));
}
index_a++;
L_tmp = L_add(L_tmp, 0x400000);
}
sc_coef = div_s(0x4000, extract_l(L_shr(M_den_sum, 1)));
im_tmp2 = mult(extract_h(amp_im_acc), sc_coef);
re_tmp2 = mult(extract_h(amp_re_acc), sc_coef);
M_num_sum = 0;
it_ind = 0;
index_a = index_a_save;
while(index_a < index_b)
{
index_wr = index_tbl[it_ind++];
if(index_wr < 0 || index_wr > 320)
sp_rec_re = sp_rec_im = 0;
else
{
sp_rec_im = mult( im_tmp2, wr_sp[index_wr]);
sp_rec_re = mult( re_tmp2, wr_sp[index_wr]);
}
re_tmp = sub(fft_buf[index_a].re, sp_rec_re);
im_tmp = sub(fft_buf[index_a].im, sp_rec_im);
D_num = L_mac(D_num, re_tmp, re_tmp);
D_num = L_mac(D_num, im_tmp, im_tmp);
M_num_sum = L_mac(M_num_sum, fft_buf[index_a].re, fft_buf[index_a].re);
M_num_sum = L_mac(M_num_sum, fft_buf[index_a].im, fft_buf[index_a].im);
index_a++;
}
M_den[j] = sc_coef;
M_num[j] = M_num_sum;
D_den = L_add(D_den, M_num_sum);
if(++num_harms_cnt == 3 && band_cnt < num_bands - 1)
{
b1_vec <<= 1;
if(D_den > D_num && D_den != 0)
{
tmp = norm_l(D_den);
Dk = div_s(extract_h(L_shl(D_num, tmp)), extract_h(L_shl(D_den, tmp)));
}
else
Dk = MAX_16;
if(Dk < dsn_thr)
{
// voiced band
v_uv_dsn[band_cnt] = 1;
b1_vec |= 1;
i = j - 2;
while(i <= j)
{
imbe_param->sa[i] = voiced_sa_calc(M_num[i], M_den[i]);
imbe_param->v_uv_dsn[i] = 1;
i++;
}
}
else
{
// unvoiced band
v_uv_dsn[band_cnt] = 0;
i = j - 2;
while(i <= j)
{
imbe_param->sa[i] = unvoiced_sa_calc(M_num[i], index_b - index_a_save);
imbe_param->v_uv_dsn[i] = 0;
uv_harms_cnt++;
i++;
}
}
D_num = D_den = 0;
num_harms_cnt = 0;
band_cnt++;
}
fund_freq_acc_a = L_add(fund_freq_acc_a, fund_freq);
fund_freq_acc_b = L_add(fund_freq_acc_b, fund_freq);
fund_freq_acc = L_add(fund_freq_acc, fund_freq);
}
if(num_harms_cnt)
{
b1_vec <<= 1;
if(D_den > D_num && D_den != 0)
{
tmp = norm_l(D_den);
Dk= div_s(extract_h(L_shl(D_num, tmp)), extract_h(L_shl(D_den, tmp)));
}
else
Dk = MAX_16;
if(Dk < dsn_thr)
{
// voiced band
v_uv_dsn[band_cnt] = 1;
b1_vec |= 1;
i = num_harms - num_harms_cnt;
while(i < num_harms)
{
imbe_param->sa[i] = voiced_sa_calc(M_num[i], M_den[i]);
imbe_param->v_uv_dsn[i] = 1;
i++;
}
}
else
{
// unvoiced band
v_uv_dsn[band_cnt] = 0;
i = num_harms - num_harms_cnt;
while(i < num_harms)
{
imbe_param->sa[i] = unvoiced_sa_calc(M_num[i], index_b - index_a_save);
imbe_param->v_uv_dsn[i] = 0;
uv_harms_cnt++;
i++;
}
}
}
imbe_param->l_uv = uv_harms_cnt;
imbe_param->b_vec[1] = b1_vec; // Save encoded voiced/unvoiced decision
imbe_param->b_vec[0] = shr( sub(imbe_param->ref_pitch, 0x1380), 7); // Pitch encode fix(2*pitch - 39)
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* The software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
#ifndef _V_UV_DET
#define _V_UV_DET
void v_uv_det(IMBE_PARAM *imbe_param, Cmplx16 *fft_buf);
#endif
+191
View File
@@ -0,0 +1,191 @@
// Public API of ambe2.h over the halfrate codec (lib/halfrate). GPL-3.0-or-later.
#include <string.h>
#include <vector>
#include <stdlib.h>
#include <math.h>
#include "ambe2.h"
#include "halfrate.h"
struct ambe2_encoder { hr_encoder *hr; };
struct ambe2_decoder { hr_decoder *hr; };
int ambe2_default_codec(void) { return AMBE2_CODEC_HALFRATE; }
const char *ambe2_codec_name(int codec) { return codec == AMBE2_CODEC_HALFRATE ? "halfrate" : NULL; }
//const char *ambe2_version(void) { return AMBE2_VERSION " halfrate " HALFRATE_VERSION; }
// 49 bits in on-air order <-> halfrate's 7-byte frame (the same bits, packed MSB first)
static void bits_to_hr(const uint8_t *bits, uint8_t *frame) {
uint8_t b[AMBE2_BITS];
for (int i = 0; i < AMBE2_BITS; i++) b[i] = bits[i] & 1;
hr_frame_from_bits(b, frame);
}
ambe2_encoder *ambe2_encoder_new_codec(int codec, int clipped) {
if (codec != AMBE2_CODEC_HALFRATE) return NULL;
ambe2_encoder *e = (ambe2_encoder *)calloc(1, sizeof *e);
if (!e) return NULL;
e->hr = hr_encoder_new();
if (!e->hr) { free(e); return NULL; }
return e;
}
ambe2_encoder *ambe2_encoder_new(void) { return ambe2_encoder_new_codec(AMBE2_CODEC_HALFRATE, 0); }
void ambe2_encoder_free(ambe2_encoder *e) {
if (!e) return;
hr_encoder_free(e->hr);
free(e);
}
int ambe2_encoder_codec(const ambe2_encoder *e) { return AMBE2_CODEC_HALFRATE; }
int ambe2_encoder_set_option(ambe2_encoder *e, const char *name, double value) { return e ? hr_encoder_set(e->hr, name, value) : -1; }
void ambe2_encode(ambe2_encoder *e, const int16_t *pcm, uint8_t *bits) {
uint8_t frame[HR_FRAME_BYTES];
hr_encode(e->hr, pcm, frame);
hr_bits_from_frame(frame, bits);
}
static void to_pcm(const float *x, int16_t *pcm) {
for (int i = 0; i < AMBE2_SAMPLES; i++) {
double v = nearbyint((double)x[i] * 32768.0);
pcm[i] = (int16_t)(v > 32767 ? 32767 : v < -32768 ? -32768 : v);
}
}
// Frame k comes out of the encoder AMBE2_ENCODER_DELAY calls later; silence after the clip pushes the last ones out.
size_t ambe2_encode_clip(ambe2_encoder *e, const float *x, size_t n, uint8_t *bits) {
size_t frames = n / AMBE2_SAMPLES;
int16_t pcm[AMBE2_SAMPLES];
uint8_t skip[AMBE2_BITS];
for (size_t t = 0; t < frames + AMBE2_ENCODER_DELAY; t++) {
if (t < frames) to_pcm(x + t * AMBE2_SAMPLES, pcm);
else memset(pcm, 0, sizeof pcm);
ambe2_encode(e, pcm, t >= AMBE2_ENCODER_DELAY ? bits + (t - AMBE2_ENCODER_DELAY) * AMBE2_BITS : skip);
}
return frames;
}
// Streaming: raw samples wait in `pending` until a frame is whole; `encoded` counts encoder calls (with the silence of
// the flush), `real` the frames of audio. The output of call k is frame k - AMBE2_ENCODER_DELAY.
struct ambe2_stream { ambe2_encoder *e; std::vector<int16_t> pending; size_t encoded, real; };
ambe2_stream *ambe2_stream_new_codec(int codec, int clipped) {
ambe2_encoder *e = ambe2_encoder_new_codec(codec, clipped);
if (!e) return NULL;
ambe2_stream *s = new ambe2_stream;
s->e = e;
s->encoded = s->real = 0;
return s;
}
ambe2_stream *ambe2_stream_new(int clipped) { return ambe2_stream_new_codec(AMBE2_CODEC_HALFRATE, clipped); }
void ambe2_stream_free(ambe2_stream *s) { if (s) { ambe2_encoder_free(s->e); delete s; } }
int ambe2_stream_set_option(ambe2_stream *s, const char *name, double value) { return s ? ambe2_encoder_set_option(s->e, name, value) : -1; }
// One encoder call; returns 1 when it gave a frame of the stream (written to bits).
static int stream_encode(ambe2_stream *s, const int16_t *pcm, uint8_t *bits) {
uint8_t skip[AMBE2_BITS];
size_t k = s->encoded++;
bool out = k >= AMBE2_ENCODER_DELAY && k - AMBE2_ENCODER_DELAY < s->real;
ambe2_encode(s->e, pcm, out ? bits : skip);
return out;
}
static size_t stream_emit(ambe2_stream *s, uint8_t *bits, size_t max_frames, bool final) {
size_t out = 0, used = 0;
// a whole frame of audio is encoded only when its output (if any) has room
while (s->pending.size() - used >= AMBE2_SAMPLES && (s->encoded < AMBE2_ENCODER_DELAY || out < max_frames)) {
s->real++;
out += stream_encode(s, &s->pending[used], bits + out * AMBE2_BITS);
used += AMBE2_SAMPLES;
}
s->pending.erase(s->pending.begin(), s->pending.begin() + used);
if (final && s->pending.size() < AMBE2_SAMPLES) { // (whole frames still waiting: no room, call again)
s->pending.clear(); // a partial last frame is dropped, as in ambe2_encode_clip
int16_t zero[AMBE2_SAMPLES] = {0};
while (out < max_frames && s->encoded < s->real + AMBE2_ENCODER_DELAY)
out += stream_encode(s, zero, bits + out * AMBE2_BITS);
}
return out;
}
size_t ambe2_stream_push(ambe2_stream *s, const int16_t *pcm, size_t n, uint8_t *bits, size_t max_frames) {
s->pending.insert(s->pending.end(), pcm, pcm + n);
return stream_emit(s, bits, max_frames, false);
}
size_t ambe2_stream_flush(ambe2_stream *s, uint8_t *bits, size_t max_frames) { return stream_emit(s, bits, max_frames, true); }
// halfrate decoder settings fitted against the DVSI stick on the train split of the captured decode clips (2026-10-28),
// by the per-frame distance over 24 mel bands: 2.31 -> 2.01 dB on test, 2.36 -> 2.10 on dev. Most of it is the formant
// enhancement (weaker, split moved from 1 kHz to 460 Hz) and mixed_voiced_share 1. repeat_high_gain lifts the top band
// on erased frames that repeat, as the stick does (about 3 dB per repeat at 3.5 kHz); frames around erasures 2.54 -> 2.04
// dB on test. Its own defaults are in lib/halfrate/docs/options.md; docs/tuning.md has the report.
static const struct { const char *name; double value; } kHalfrateDecoderTuning[] = {
{"output_gain", 2.07}, {"high_band_tilt", 0.5}, {"high_band_start", 0.66}, {"silence_gain", 0.26},
{"enhance_strength", 0.61}, {"enhance_min_weight", 0.39}, {"enhance_max_weight", 1.24},
{"enhance_power_high", 0.72}, {"enhance_power_low", 0.2}, {"enhance_split_frequency", 0.057},
{"mixed_voiced_share", 1.0}, {"repeat_high_gain", 1.78},
};
ambe2_decoder *ambe2_decoder_new_codec(int codec) {
if (codec != AMBE2_CODEC_HALFRATE) return NULL;
ambe2_decoder *d = (ambe2_decoder *)calloc(1, sizeof *d);
if (!d) return NULL;
d->hr = hr_decoder_new();
if (!d->hr) { free(d); return NULL; }
for (const auto &t : kHalfrateDecoderTuning) hr_decoder_set(d->hr, t.name, t.value);
return d;
}
ambe2_decoder *ambe2_decoder_new(void) { return ambe2_decoder_new_codec(AMBE2_CODEC_HALFRATE); }
void ambe2_decoder_free(ambe2_decoder *d) {
if (!d) return;
hr_decoder_free(d->hr);
free(d);
}
int ambe2_decoder_codec(const ambe2_decoder *d) { return AMBE2_CODEC_HALFRATE; }
int ambe2_decoder_set_option(ambe2_decoder *d, const char *name, double value) { return d ? hr_decoder_set(d->hr, name, value) : -1; }
// halfrate frame kinds -> the return values of ambe2_decode
static int halfrate_kind(int kind) {
switch (kind) {
case HR_ERASURE: return 1;
case HR_TONE: return 3;
default: return 0; // HR_VOICE, HR_SILENCE
}
}
int ambe2_decode(ambe2_decoder *d, const uint8_t *bits, int16_t *pcm) {
uint8_t frame[HR_FRAME_BYTES];
bits_to_hr(bits, frame);
return halfrate_kind(hr_decode(d->hr, frame, pcm));
}
int ambe2_decode_packed(ambe2_decoder *d, const uint8_t *frame9, int16_t *pcm) { return halfrate_kind(hr_decode_air(d->hr, frame9, pcm)); }
void ambe2_pack(const uint8_t *bits, uint8_t *frame9) {
uint8_t frame[HR_FRAME_BYTES];
bits_to_hr(bits, frame);
hr_fec_encode(frame, frame9);
}
int ambe2_unpack(const uint8_t *frame9, uint8_t *bits) {
uint8_t frame[HR_FRAME_BYTES];
int errs = hr_fec_decode(frame9, frame);
hr_bits_from_frame(frame, bits);
return errs;
}
static size_t pack_all(const std::vector<uint8_t> &bits, size_t n, uint8_t *frames) {
for (size_t i = 0; i < n; i++) ambe2_pack(&bits[i * AMBE2_BITS], frames + i * AMBE2_FRAME_BYTES);
return n;
}
size_t ambe2_stream_push_packed(ambe2_stream *s, const int16_t *pcm, size_t n, uint8_t *frames, size_t max_frames) {
std::vector<uint8_t> bits(max_frames * AMBE2_BITS + 1);
return pack_all(bits, ambe2_stream_push(s, pcm, n, bits.data(), max_frames), frames);
}
size_t ambe2_stream_flush_packed(ambe2_stream *s, uint8_t *frames, size_t max_frames) {
std::vector<uint8_t> bits(max_frames * AMBE2_BITS + 1);
return pack_all(bits, ambe2_stream_flush(s, bits.data(), max_frames), frames);
}
+98
View File
@@ -0,0 +1,98 @@
/* ambe2: software AMBE+2 half-rate (DMR) encoder and decoder. GPL-3.0-or-later.
*
* The codec is lib/halfrate (its own input filtering, pitch tracking, pause gate, erasure concealment, tones and output
* limiting; options by name, lib/halfrate/docs/options.md). It writes and plays standard AMBE+2 frames.
*
* A frame is 49 bits, one bit per byte (0 or 1), in the on-air order. ambe2_pack/ambe2_unpack turn it into the
* 9-byte frame with FEC and interleave that DMR carries (36 dibits, first dibit in the top two bits of byte 0).
* Audio is 8 kHz, 16-bit mono, 160 samples (20 ms) per frame. One encoder or decoder per stream: both keep
* state from frame to frame. Not thread-safe per object; separate objects are independent.
*/
#ifndef AMBE2_H
#define AMBE2_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define AMBE2_SAMPLES 160
#define AMBE2_BITS 49
#define AMBE2_FRAME_BYTES 9
typedef struct ambe2_encoder ambe2_encoder;
typedef struct ambe2_decoder ambe2_decoder;
typedef struct ambe2_stream ambe2_stream;
/* The one codec. The _codec calls take it so that a later codec can sit beside it; any other value gives NULL. */
#define AMBE2_CODEC_HALFRATE 0
/* AMBE2_CODEC_HALFRATE. */
int ambe2_default_codec(void);
/* "halfrate"; NULL for an unknown codec. */
const char *ambe2_codec_name(int codec);
/* The codec of an encoder or decoder. */
int ambe2_encoder_codec(const ambe2_encoder *e);
int ambe2_decoder_codec(const ambe2_decoder *d);
ambe2_encoder *ambe2_encoder_new(void);
/* An encoder of the given codec; NULL for an unknown codec. clipped is not used (kept for source compatibility). */
ambe2_encoder *ambe2_encoder_new_codec(int codec, int clipped);
void ambe2_encoder_free(ambe2_encoder *e);
/* An option of the encoder by name (lib/halfrate/docs/options.md). Returns 0, or -1 for an unknown name or a NaN or
* infinite value. */
int ambe2_encoder_set_option(ambe2_encoder *e, const char *name, double value);
/* pcm: 160 samples in, bits: 49 bytes out. The encoder looks ahead: its frames come out AMBE2_ENCODER_DELAY frames late
* (the first ones are silence); ambe2_encode_clip and the stream line them up with the audio. */
void ambe2_encode(ambe2_encoder *e, const int16_t *pcm, uint8_t *bits);
#define AMBE2_ENCODER_DELAY 2
/* A whole clip: x is the raw clip as float samples in [-1, 1] at 8 kHz. n/160 frames of 49 bytes go to bits (room for
* n/160*49), frame k from samples 160 k to 160 k + 159. Returns the number of frames. The encoder is used up afterwards
* (free it). */
size_t ambe2_encode_clip(ambe2_encoder *e, const float *x, size_t n, uint8_t *bits);
/* Streaming encoder: raw 8 kHz audio in any chunk size, frames out 40 ms (two frames) late, the same frames as
* ambe2_encode_clip on the whole clip. One stream per call; separate streams are independent and may run on separate
* threads. clipped is not used (kept for source compatibility). push returns the frames that became ready (at most
* max_frames; push 0 samples to fetch the rest); flush ends the stream and returns the frames still waiting. bits: 49
* bytes per frame. */
ambe2_stream *ambe2_stream_new(int clipped);
/* A stream of the given codec; NULL for an unknown codec. */
ambe2_stream *ambe2_stream_new_codec(int codec, int clipped);
/* ambe2_encoder_set_option on the stream's encoder. Set options before the first push. */
int ambe2_stream_set_option(ambe2_stream *s, const char *name, double value);
void ambe2_stream_free(ambe2_stream *s);
size_t ambe2_stream_push(ambe2_stream *s, const int16_t *pcm, size_t n, uint8_t *bits, size_t max_frames);
size_t ambe2_stream_flush(ambe2_stream *s, uint8_t *bits, size_t max_frames);
/* A new decoder starts from our tuning, not from halfrate's own defaults (kHalfrateDecoderTuning in ambe2.cc: output
* stage, formant enhancement, voicing mix and a high-band lift on repeated erased frames; docs/tuning.md). It conceals
* erased frames (repeat, then mute, then comfort noise), plays tone frames and limits its output. */
ambe2_decoder *ambe2_decoder_new(void);
/* A decoder of the given codec; NULL for an unknown codec. */
ambe2_decoder *ambe2_decoder_new_codec(int codec);
void ambe2_decoder_free(ambe2_decoder *d);
/* An option of the decoder by name (lib/halfrate/docs/options.md). Returns 0, or -1 for an unknown name or a NaN or
* infinite value. */
int ambe2_decoder_set_option(ambe2_decoder *d, const char *name, double value);
/* bits: 49 bytes in, pcm: 160 samples out. Returns 0 for a voice (or silence) frame, 1 for an erased frame, 3 for a tone
* frame. */
int ambe2_decode(ambe2_decoder *d, const uint8_t *bits, int16_t *pcm);
/* The same calls on 9-byte on-air frames (what DMR carries), for callers that never see the 49-bit form. frames:
* 9 bytes per frame. The decoder corrects what the FEC can; the return value is as for ambe2_decode. */
size_t ambe2_stream_push_packed(ambe2_stream *s, const int16_t *pcm, size_t n, uint8_t *frames, size_t max_frames);
size_t ambe2_stream_flush_packed(ambe2_stream *s, uint8_t *frames, size_t max_frames);
int ambe2_decode_packed(ambe2_decoder *d, const uint8_t *frame9, int16_t *pcm);
/* Library version and the halfrate codec version, e.g. "2.0.0 halfrate 0.2.0". */
const char *ambe2_version(void);
/* 49 bits <-> the 9-byte on-air frame. unpack returns the number of bit errors the FEC corrected, or -1 when the first
* code word had more than it can correct (the bits are still written, as decoded). */
void ambe2_pack(const uint8_t *bits, uint8_t *frame9);
int ambe2_unpack(const uint8_t *frame9, uint8_t *bits);
#ifdef __cplusplus
}
#endif
#endif
+286
View File
@@ -0,0 +1,286 @@
#include "analysis.h"
#include <cstring>
#include "fastmath.h"
#include "fft.h"
#include "numeric.h"
#include "constants.h"
#include "history.h"
#include "tables.h"
namespace halfrate::enc {
using core::fast_log2;
using core::fast_sqrt;
using core::to_int32_sat;
namespace {
// The window response at p (1/16 bins, 256 = the centre), linearly interpolated; clamped at both ends.
float response_at(const AnalysisWindow& an, float p) {
if (p <= 0.0f) return an.response[0];
if (p >= 512.0f) return an.response[512];
int i = to_int32_sat(p);
float fraction = p - static_cast<float>(to_int32_sat(p));
return an.response[i] + fraction * (an.response[i + 1] - an.response[i]);
}
// The harmonic (rounded bin / spacing) a bin belongs to.
inline int harmonic_of_bin(float bin, float spacing) { return to_int32_sat(bin / spacing + 0.5f); }
} // namespace
void init_analysis_window(AnalysisWindow& an, const float* window, int length) {
an.response.fill(0.0f);
an.first_harmonic.fill(0);
an.first_bin.fill(0);
an.bin_count.fill(0);
an.weight_offset.fill(0);
an.weight_energy.fill(0.0f);
an.gain.fill(0.0f);
an.weights.fill(0.0f);
an.silence_first_bin.fill(0);
an.silence_bin_count.fill(0);
an.silence_norm.fill(0.0f);
int half = length / 2;
an.length = length;
an.half = half;
an.window = window;
for (int m = -256; m <= 256; m++) {
float s = 0.0f;
for (int i = 0; i < length; i++)
s = s + window[i] * tables::kCosine[static_cast<uint32_t>(m * (i - half)) & 0x1fff];
an.response[m + 256] = s;
}
int h = 0, offset = 0;
for (int j = 0; j < kPitchCount; j++) {
float f0 = tables::kPitchFundamental[j];
int count = tables::kPitchHarmonicCount[j];
an.first_harmonic[j] = h;
if (count < 1) continue;
int next = 0;
float spacing = f0 * 512.0f; // bins between harmonics
for (int k = 1;; k++) {
int first, n = 0, end;
float energy;
if (next > kFitBins - 1) {
first = next;
end = next;
energy = kTiny;
} else {
int b = next;
for (; b < kFitBins; b++)
if (k <= harmonic_of_bin(static_cast<float>(b), spacing)) break;
if (b >= kFitBins) {
first = kFitBins;
end = kFitBins;
energy = kTiny;
} else {
first = b;
float centre = spacing * static_cast<float>(static_cast<unsigned>(k));
float* w = &an.weights[offset];
energy = 0.0f;
for (end = b; end < kFitBins; end++) {
float bin = static_cast<float>(end);
if (k != harmonic_of_bin(bin, spacing)) break;
float v = response_at(an, (bin - centre) * 16.0f + 256.0f);
w[n++] = v;
energy = energy + v * v;
}
energy = energy + kTiny;
}
}
an.first_bin[h] = static_cast<int16_t>(first);
an.bin_count[h] = static_cast<int16_t>(n);
an.weight_offset[h] = offset;
an.weight_energy[h] = energy;
an.gain[h] = 1.0f / energy;
offset = n + offset;
h++;
next = end;
if (k == count) break;
}
}
an.first_harmonic[kPitchCount] = h;
// silence band b (1..14): the bins i of 0..256 whose rounded i / 16 is b
for (int b = 1; b < kSilenceBands + 1; b++) {
float centre = static_cast<float>(static_cast<unsigned>(b)) * 16.0f;
float s = 0.0f;
int n = 0, first = -1;
for (int i = 0; i < kSpectrumBins; i++) {
if (b != to_int32_sat(static_cast<float>(i) * 0.0625f + 0.5f)) continue;
float v = response_at(an, (static_cast<float>(i) - centre) * 16.0f + 256.0f);
if (first < 0) first = i;
s = s + v * v;
n++;
}
an.silence_first_bin[b - 1] = static_cast<int16_t>(first);
an.silence_bin_count[b - 1] = static_cast<int16_t>(n);
an.silence_norm[b - 1] = s;
}
}
void analysis_spectrum(const AnalysisWindow& an, const SignalHistory& history, int center, Spectrum& out) {
int n = an.half;
const float* w = an.window;
float re[512], im[512];
std::memset(re, 0, sizeof re);
std::memset(im, 0, sizeof im);
for (int k = 0; k < n; k++) {
re[k] = history.sample(center + k) * w[n + k];
re[512 - n + k] = history.sample(center - n + k) * w[k];
}
core::fft512(re, im);
for (int k = 0; k < kSpectrumBins; k++) {
out.re[k] = re[k];
out.im[k] = im[k];
}
}
void harmonic_fit_cost(const AnalysisWindow& an, const Spectrum& s, float* cost, float* energy) {
float e = 0.0f;
for (int i = 0; i < kFitBins; i += 4) {
float lane[4];
for (int j = 0; j < 4; j++) lane[j] = s.re[i + j] * s.re[i + j] + s.im[i + j] * s.im[i + j];
e = e + lane[0];
e = e + lane[1];
e = e + lane[2];
e = e + lane[3];
}
float total = e + kTiny;
*energy = total;
float inverse = 1.0f / total;
int h = an.first_harmonic[0];
for (int j = 0; j < kPitchCount; j++) {
int end = an.first_harmonic[j + 1];
float explained = 0.0f;
for (; h < end; h++) {
int n = an.bin_count[h];
float ar = 0.0f, ai = 0.0f;
if (n >= 1) {
const float* w = &an.weights[an.weight_offset[h]];
int first = an.first_bin[h];
for (int k = 0; k < n; k++) {
ar = ar + s.re[first + k] * w[k];
ai = ai + w[k] * s.im[first + k];
}
}
explained = explained + (ar * ar + ai * ai) * an.gain[h];
}
cost[j] = 1.0f - inverse * explained;
h = end;
}
}
void yin_difference(const SignalHistory& history, int center, const float* lags, int lag_count, int max_lag,
float* out) {
float difference[163], normalised[161];
if (max_lag >= 0) {
int base = center + (-(max_lag / 2)) - 100;
for (int tau = 0; tau <= max_lag; tau++) {
float s = 0.0f;
for (int i = 0; i < 200; i += 4) {
float lane[4];
for (int j = 0; j < 4; j++) {
float v = history.sample(base + i + j) - history.sample(base + tau + i + j);
lane[j] = v * v;
}
s = s + lane[0];
s = s + lane[1];
s = s + lane[2];
s = s + lane[3];
}
difference[tau] = s;
}
normalised[0] = 1.0f;
if (max_lag != 0) {
float s = 0.0f;
for (unsigned tau = 1; tau <= static_cast<unsigned>(max_lag); tau++) {
s = s + difference[tau];
normalised[tau] = difference[tau] / (s / static_cast<float>(tau) + kTiny);
}
}
} else {
normalised[0] = 1.0f;
}
for (int i = 0; i < lag_count; i++) {
float lag = lags[i];
int k = to_int32_sat(lag);
float v;
if (max_lag > k) {
float fraction = lag - static_cast<float>(k);
v = normalised[k] + fraction * (normalised[k + 1] - normalised[k]);
} else {
v = normalised[max_lag];
}
out[i] = v;
}
}
void fit_harmonics(const AnalysisWindow& an, const Spectrum& s, int pitch, int harmonic_count, float fundamental,
HarmonicFit& out) {
out = HarmonicFit{};
std::array<float, 8> band_energy{}, band_error{};
out.harmonic_count = harmonic_count;
if (harmonic_count >= 1) {
long h = an.first_harmonic[pitch];
for (unsigned k = 1; k <= static_cast<unsigned>(harmonic_count); k++, h++) {
int n = an.bin_count[h];
int band = to_int32_sat(fundamental * (static_cast<float>(k) * 16.0f));
band = band < 7 ? band : 7;
float ar, ai, energy;
if (n >= 1) {
const float* w = &an.weights[an.weight_offset[h]];
int first = an.first_bin[h];
ar = 0.0f;
ai = 0.0f;
for (int i = 0; i < n; i++) {
ar = ar + s.re[first + i] * w[i];
ai = ai + w[i] * s.im[first + i];
}
float g = an.gain[h];
ar = ar * g;
ai = ai * g;
float error = 0.0f;
energy = 0.0f;
for (int i = 0; i < n; i++) {
float r = s.re[first + i], q = s.im[first + i], ww = w[i];
float e1 = r - ar * ww;
float e2 = q - ai * ww;
error = error + (e1 * e1 + e2 * e2);
energy = energy + (r * r + q * q);
}
band_error[band] = error + band_error[band];
band_energy[band] = energy + band_energy[band];
out.band_has_data[band] = 1;
} else {
energy = 0.0f;
ar = an.gain[h] * energy;
ai = ar;
}
out.log2_amplitude[k - 1] = fast_log2(fast_sqrt(ar * ar + ai * ai) + kAmplitudeFloor);
float norm = an.weight_energy[h] + kMinusTiny;
norm = norm < kTiny ? kTiny : norm;
out.log2_rms[k - 1] = fast_log2(fast_sqrt(energy / norm) + kAmplitudeFloor);
}
}
for (int b = 0; b < 8; b++) {
out.band_energy[b] = band_energy[b];
out.band_ratio[b] = out.band_has_data[b] ? band_error[b] / (band_energy[b] + kEnergyFloor) : 0.0f;
}
}
void silence_amplitudes(const AnalysisWindow& an, const Spectrum& s, float* out) {
for (int b = 0; b < kSilenceBands; b++) {
int n = an.silence_bin_count[b];
float sum = 0.0f;
if (n >= 1) {
int first = an.silence_first_bin[b];
for (int k = 0; k < n; k++) sum = sum + (s.re[first + k] * s.re[first + k] + s.im[first + k] * s.im[first + k]);
}
out[b] = fast_log2(fast_sqrt(sum / an.silence_norm[b]) + kAmplitudeFloor);
}
}
} // namespace halfrate::enc
+80
View File
@@ -0,0 +1,80 @@
// Spectral analysis of the encoder: the analysis windows with their per-pitch harmonic tables, the windowed
// spectrum, the per-pitch harmonic fit cost, the YIN difference function, the harmonic fit at one pitch, and the
// band amplitudes of a silent frame.
#pragma once
#include <array>
#include <cstdint>
namespace halfrate::enc {
constexpr int kPitchCount = 120; // pitch grid = pitch indices of the codebook
constexpr int kSpectrumBins = 257; // bins 0..256 of the 512-point FFT
constexpr int kFitBins = 244; // bins the harmonic fits use (0..243, up to 3.8 kHz)
constexpr int kGridHarmonics = 3097; // sum of the harmonic counts of the grid
constexpr int kGridWeights = 27683; // window weights of all grid harmonics
constexpr int kSilenceBands = 14; // 250 Hz bands of a silent frame
constexpr int kMaxFitHarmonics = 56;
// The first 257 bins of a windowed 512-point FFT.
struct Spectrum {
std::array<float, kSpectrumBins> re;
std::array<float, kSpectrumBins> im;
};
// One analysis window and what is precomputed from it: the window's transform sampled every 1/16 bin, and for
// each grid pitch the FFT bins of each harmonic with their weights (the window response at the bin's distance from
// the harmonic). Harmonics of all pitches are numbered together: pitch j has harmonics first_harmonic[j] ..
// first_harmonic[j + 1] - 1.
struct AnalysisWindow {
int32_t length = 0;
int32_t half = 0;
const float* window = nullptr;
std::array<float, 513> response{}; // window transform at (index - 256) / 16 bins
std::array<int32_t, kPitchCount + 1> first_harmonic{};
std::array<int16_t, kGridHarmonics> first_bin{};
std::array<int16_t, kGridHarmonics> bin_count{};
std::array<int32_t, kGridHarmonics> weight_offset{}; // into weights
std::array<float, kGridHarmonics> weight_energy{}; // sum of squared weights (+ 1e-12)
std::array<float, kGridHarmonics> gain{}; // 1 / weight_energy
std::array<float, kGridWeights> weights{};
std::array<int16_t, kSilenceBands> silence_first_bin{};
std::array<int16_t, kSilenceBands> silence_bin_count{};
std::array<float, kSilenceBands> silence_norm{}; // sum of squared window weights of the band
};
// The harmonic fit of one frame at one pitch.
struct HarmonicFit {
int32_t harmonic_count = 0;
std::array<float, 8> band_ratio{}; // per 500 Hz band: fit residual / band energy (0 if no data)
std::array<float, 8> band_energy{};
std::array<uint8_t, 8> band_has_data{};
std::array<float, kMaxFitHarmonics> log2_amplitude{}; // log2 of the fitted harmonic amplitude
std::array<float, kMaxFitHarmonics> log2_rms{}; // log2 rms of the spectrum around the harmonic
};
// Builds the tables of window[length] for the grid (the codebook's pitch table).
void init_analysis_window(AnalysisWindow& an, const float* window, int length);
// Reads input samples by index (the analysis history, see encoder.h).
struct SignalHistory;
// Windowed spectrum of the samples around center (the window's second half from center on, its first half
// before it).
void analysis_spectrum(const AnalysisWindow& an, const SignalHistory& history, int center, Spectrum& out);
// Per grid pitch: 1 - (energy the pitch's harmonics explain) / (total energy). *energy gets the total energy of
// the fitted bins (+ 1e-12).
void harmonic_fit_cost(const AnalysisWindow& an, const Spectrum& s, float* cost, float* energy);
// YIN cumulative-mean-normalised difference (200-sample frames, lags 0..max_lag) at the fractional lags given.
void yin_difference(const SignalHistory& history, int center, const float* lags, int lag_count, int max_lag,
float* out);
// Fits the harmonic_count harmonics of grid pitch `pitch` (fundamental in cycles per sample) to the spectrum.
void fit_harmonics(const AnalysisWindow& an, const Spectrum& s, int pitch, int harmonic_count, float fundamental,
HarmonicFit& out);
// log2 rms amplitudes of the 14 silence bands.
void silence_amplitudes(const AnalysisWindow& an, const Spectrum& s, float* out);
} // namespace halfrate::enc
+31
View File
@@ -0,0 +1,31 @@
// The C API (include/halfrate.h).
#include "halfrate.h"
#include <cstring>
#include "fec.h"
#include "frame.h"
#ifndef HR_VERSION
#define HR_VERSION "unknown"
#endif
using namespace halfrate;
extern "C" {
void hr_fec_encode(const uint8_t* frame, uint8_t* air) { core::fec_encode_frame(frame, air); }
int hr_fec_decode(const uint8_t* air, uint8_t* frame) {
core::FecErrors errors;
if (core::fec_decode_frame(air, frame, &errors) < 0) return -1;
return errors.first_word + errors.second_word;
}
void hr_bits_from_frame(const uint8_t* frame, uint8_t* bits) { core::bits_from_bytes(frame, bits); }
void hr_frame_from_bits(const uint8_t* bits, uint8_t* frame) { core::bytes_from_bits(bits, frame); }
const char* hr_version(void) { return "halfrate " HR_VERSION; }
} // extern "C"
+2481
View File
File diff suppressed because it is too large. Load diff
+30
View File
@@ -0,0 +1,30 @@
// The parameter codebook set: everything the dequantiser (and the encoder's quantiser) looks up to turn the nine
// frame fields into the frame parameters. tables::kCodebook is the one the half-rate codec uses.
#pragma once
#include <array>
#include <cstdint>
namespace halfrate::core {
struct Codebook {
int32_t codec_id; // 1: this half-rate codec
const float* fundamental; // [pitch_count] fundamental, cycles per sample, per pitch index
const int32_t* harmonic_count; // [pitch_count] number of harmonics L per pitch index
int32_t voicing_count; // entries of voicing
const int32_t (*voicing)[8]; // per voicing index: band 0..7 voiced (nonzero) or unvoiced
int32_t plain_voicing_count; // voicing indices below this are plain patterns (not read by the code)
const int32_t (*band_sizes)[4]; // per L: sizes of the 4 blocks the L harmonics split into
int32_t gain_count; // entries of gain
const float* gain; // gain codebook (log2 units)
const float (*shape_low)[3]; // 8-point transform coefficients 1..3, per shape-low index
const float (*shape_high)[4]; // 8-point transform coefficients 4..7, per shape-high index
std::array<const float*, 4> detail; // per block: higher-order coefficients, rows of 4 floats
std::array<int32_t, 4> detail_count; // entries of each detail codebook
std::array<int32_t, 4> detail_stride; // rows per index step of each detail codebook
float prediction; // weight of the previous frame's shape in the prediction
int32_t pitch_count; // pitch indices with an entry (fundamental, harmonic_count)
float gain_weight; // gain = gain_weight * gain[index] + previous_gain_weight * previous gain
float previous_gain_weight;
};
} // namespace halfrate::core
+28
View File
@@ -0,0 +1,28 @@
// The half-rate codec's codebook set (core::Codebook), built from the tables in codebook.cc.
#include "tables.h"
namespace halfrate::tables {
const core::Codebook kCodebook = {
1,
kPitchFundamental,
kPitchHarmonicCount,
32,
kVoicingCodebook,
16,
kBandSizes,
32,
kGainCodebook,
kShapeLowCodebook,
kShapeHighCodebook,
{&kShapeDetail1Codebook[0][0], &kShapeDetail2Codebook[0][0], &kShapeDetail3Codebook[0][0],
&kShapeDetail4Codebook[0][0]},
{32, 16, 16, 8},
{1, 1, 1, 1},
0x1.4cccccp-1f, // 0.65
120,
1.0f,
0.5f,
};
} // namespace halfrate::tables
+50
View File
@@ -0,0 +1,50 @@
// Numeric constants of the encoder. They are written as hex floats because the output depends on their exact
// values (most are the nearest float to the decimal value in the comment, some are not).
#pragma once
namespace halfrate::enc {
constexpr float kTiny = 0x1.197998p-40f; // 1e-12: energy floor added before divisions and logs
constexpr float kMinusTiny = -0x1.197998p-40f; // -1e-12
constexpr float kAmplitudeFloor = 0x1.12e0bep-30f; // 1e-9: added to an amplitude before its log2
constexpr float kEnergyFloor = 0x1.203afap-50f; // 1e-15
constexpr float kHuge = 0x1.c363ccp+127f; // 3e38: "no candidate yet" for minimum searches
constexpr float kMinusHuge = -0x1.c363ccp+127f; // -3e38
constexpr float kVeryLarge = 0x1.93e594p+99f; // 1e30
constexpr float kVeryNegative = -0x1.93e594p+99f; // -1e30: start value of maximum searches
constexpr float kDbPerLog2 = 0x1.815182p+1f; // 10 log10(2): log2 energy to dB
constexpr float kTwoPi = 0x1.921fb6p+2f;
constexpr float kInvSqrt2 = 0x1.6a09e6p-1f; // high-pass design: 1 / sqrt(2)
constexpr float kHalfInvSqrt2 = 0x1.6a09e6p-2f; // shape basis: scale of the pair difference
constexpr float kOctaveMatch = 0x1.eb851ep-5f; // 0.06: log2 distance accepted as "one octave below"
constexpr float kFloorCreep = 0x1.99999ap-5f; // 0.05 dB: noise floor rise per frame
constexpr float kHzToVoicingBand = 0x1.0624dep-9f; // 0.002: Hz to the 500 Hz voicing bands
constexpr float kOneSeventh = 0x1.24924ap-3f; // 1/7
constexpr float kTwoSevenths = 0x1.24924ap-2f; // 2/7
constexpr float kVoicingCostFloor = 0x1.47ae14p-6f; // 0.02: cost of a voiced band that fits
constexpr float kCompensationFloor = 0x1.0c6f7ap-20f; // 1e-6
constexpr float kLevelFeatureScale = 0x1.99999ap-4f; // 0.1: level (dB) as a pitch-net feature
constexpr float kOctaveJumpScale = 0x1.aaaaaap+2f; // 6.6667: log2 distance minus one octave, scaled
constexpr float kOctaveJumpExponent = -0x1.715476p+0f; // -1/ln 2: Gaussian of the octave-jump cost
constexpr float kOctaveLookbackTolerance = 0x1.47ae14p-4f; // 0.08: log2 distance
constexpr float kHighShapeStart = 0x1.19999ap-1f; // 0.55: share of the harmonics where the high weight starts
constexpr float kMinusHighShapeStart = -0x1.19999ap-1f;
constexpr float kHighShapeScale = 0x1.1c71c8p+1f; // 2.2222 = 1 / (1 - 0.55)
constexpr float kRescoreDistanceFloor = 0x1.0c6f7ap-20f; // 1e-6
constexpr float kRescoreBandFloor = 0x1.79ca1p-67f; // 1e-20
constexpr float kHighPassFlush = 0x1.4484cp-100f; // 1e-30: high-pass state below this is flushed to 0
constexpr float kStartupAmplitude = -0x1.8p+3f; // -12: log2 amplitudes of the frames before the first
constexpr float kInitialPreviousFundamental = 0x1.47ae14p-7f; // 0.01
constexpr float kToneFundamental = 0x1p-5f; // 1/32: fundamental of the fixed-pitch (silence) frame
// pitch network
constexpr float kNetInputScale = 0x1.ecc05ep+3f;
constexpr float kNetLayer1Scale = 0x1.0a001p-4f;
constexpr float kNetLayer2Scale = 0x1.918048p-3f;
constexpr float kNetLayer3Scale = 0x1.1defbap-3f;
constexpr float kNetHidden1Scale = 0x1.467474p+2f; // hidden layer 1 output to int8
constexpr float kNetHidden2Scale = 0x1.ca6546p+2f; // hidden layer 2 output to int8
constexpr float kLog2E = 0x1.715476p+0f;
constexpr float kLn2 = 0x1.62e43p-1f;
} // namespace halfrate::enc
+1376
View File
File diff suppressed because it is too large. Load diff
+518
View File
@@ -0,0 +1,518 @@
#include "decoder.h"
#include <cmath>
#include "enhance.h"
#include "fastmath.h"
#include "frame.h"
#include "numeric.h"
#include "decoder_tables.h"
#include "tables.h"
namespace halfrate::dec {
namespace {
using core::fast_exp2;
using core::fast_log2;
using core::fast_sqrt;
constexpr int32_t kFirstNonVoicePitch = 120; // pitch index 120 and up: silence, tone or erasure
constexpr int32_t kSilencePitch = 124; // silence frames are dequantised with this pitch index
constexpr int32_t kSilenceVoicing = 16; // voicing indices 16 and 17 mark silence frames
constexpr int32_t kFirstPulseVoicing = 18; // voicing 18 and up are pulse codes
constexpr int32_t kPulsePairVoicing = 24; // 24 and 25: two pulses
constexpr float kOneOverTwoPi = 0x1.45f306p-3f;
constexpr float kOneOver160 = 0x1.99999ap-8f;
constexpr float kSqrtHalf = 0x1.6a09e6p-1f;
constexpr float kSqrt2 = 0x1.6a09e6p+0f;
constexpr float kDbPerLog2 = 0x1.815182p+1f; // 10 log10(2): power log2 -> dB
constexpr float kLog2PerDb = 0x1.542a5ap-3f; // log2(10) / 20: amplitude dB -> log2
constexpr float kLimiterCeiling = 32766.0f;
constexpr float kFlushBelow = 0x1.4484cp-100f; // ~1e-30: the high-pass state is flushed to 0 below this
constexpr float kToneLevelLog2 = 0x1.e353f8p-4f; // tone level step, log2 per amplitude code
constexpr float kToneScale = 2737.0f;
constexpr float kSampleRate = 8000.0f;
constexpr float kDialTone425 = 425.0f;
// Tone indices
constexpr uint32_t kSingleToneFirst = 5, kSingleToneLast = 122; // index * 31.25 Hz
constexpr uint32_t kKeypadTones = 0x80, kToneSet = 0x90; // 16 dual tones each
constexpr uint32_t kProgress350_440 = 0xa0, kTone425 = 0xa1, kProgress480_620 = 0xa2, kProgress350_490 = 0xa3;
core::EnhanceOptions enhance_options(const Options& o) {
core::EnhanceOptions e;
e.mode = o.enhance;
e.strength = o.enhance_strength;
e.min_weight = o.enhance_min_weight;
e.max_weight = o.enhance_max_weight;
e.bypass_divisor = o.enhance_bypass_divisor;
e.power_high = o.enhance_power_high;
e.power_low = o.enhance_power_low;
e.split_frequency = o.enhance_split_frequency;
return e;
}
} // namespace
Decoder::Decoder() { reset(); }
void Decoder::reset() {
core::dequant_reset(dequant);
synth_reset(synth, options);
last_good = SynthFrame{};
had_good = false;
bad_count = 0;
muted = false;
tone_phase = {};
limiter_gain = 1.0f;
highpass.sections = {};
highpass.cutoff = -1.0f;
}
bool Decoder::set(const char* name, double value) {
SetEffect effect;
if (!set_option(options, name, value, &effect)) return false;
if (effect == SetEffect::reset_synth) synth_reset(synth, options);
return true;
}
// An erased frame: repeat the last good parameters (amplitudes times repeat_gain each time, with a high-band lift
// above repeat_high_start) for repeat_max frames, then one silent frame, then comfort noise.
// Before any good frame: comfort noise at once.
void Decoder::conceal(float* buf) {
int32_t n = bad_count;
bad_count = n + 1;
if (had_good) {
if (n >= options.repeat_max) {
if (!muted) {
SynthFrame silent{};
synth_frame(synth, options, silent, buf);
muted = true;
return;
}
} else if (!muted) {
SynthFrame p = last_good;
int32_t count = p.harmonic_count;
float g = options.repeat_gain;
// the high-band lift rises in dB from 1 at repeat_high_start to repeat_high_gain at half the sample rate
float start = options.repeat_high_start;
float span = 0.5f - start > 0.01f ? 0.5f - start : 0.01f;
float lift = std::log(options.repeat_high_gain > 0.0f ? options.repeat_high_gain : 1.0f);
for (int32_t k = 0; k < count; k++) {
float f = (k + 1) * p.fundamental;
float s = f > start && lift != 0.0f ? g * std::exp(lift * (f - start) / span) : g;
p.amplitude[1 + k] = p.amplitude[1 + k] * s;
}
last_good = p;
synth_frame(synth, options, p, buf);
return;
}
}
synth_comfort_noise(synth, options.comfort_noise_level, buf);
synth.previous = SynthFrame{};
muted = true;
}
// A frame with pitch index 120 and up that is not silence: a tone frame (bits 0..7 all set and the tone index in
// bits 12..43 four times), otherwise an erasure.
DecodedKind Decoder::tone_or_erasure(const uint8_t* in, float* buf) {
for (int k = 0; k < 8; k++) {
if (in[k] == 0) { conceal(buf); return DecodedKind::erasure; }
}
int32_t copies[4];
for (int j = 0; j < 4; j++) {
uint32_t v = 0;
for (int k = 0; k < 8; k++) v = in[12 + 8 * j + k] | (v << 1);
copies[j] = static_cast<int32_t>(v);
}
uint32_t index = static_cast<uint32_t>(copies[0]);
if (!(copies[0] == copies[1] && copies[0] == copies[2] && copies[0] == copies[3])) {
conceal(buf);
return DecodedKind::erasure;
}
uint32_t level = static_cast<uint32_t>(in[9]) << 3 | static_cast<uint32_t>(in[8]) << 4;
level |= static_cast<uint32_t>(in[10]) << 2;
level |= static_cast<uint32_t>(in[11]) << 1;
level |= in[44];
bad_count = 0;
muted = false;
bool silent, dual;
float freq_a, freq_b; // Hz
if (index - kSingleToneFirst <= kSingleToneLast - kSingleToneFirst) {
silent = false; dual = false;
freq_a = static_cast<float>(index) * 31.25f;
freq_b = 0.0f;
} else if ((index & ~0xfu) == kToneSet) {
silent = false;
const int8_t* pick = tables::kToneSetIndex[index - kToneSet];
freq_a = tables::kToneSetLow[pick[0]];
freq_b = tables::kToneSetHigh[pick[1]];
dual = true;
} else if ((index & ~0xfu) == kKeypadTones) {
silent = false;
uint32_t i = index - kKeypadTones;
freq_a = tables::kKeypadLow[tables::kKeypadLowIndex[i]];
freq_b = tables::kKeypadHigh[tables::kKeypadHighIndex[i]];
dual = true;
} else {
dual = false; silent = true; freq_a = 0.0f; freq_b = 0.0f;
if (index == kProgress480_620) { silent = false; dual = true; freq_a = 480.0f; freq_b = 620.0f; }
else if (index == kProgress350_490) { silent = false; dual = true; freq_a = 350.0f; freq_b = 490.0f; }
else if (index == kProgress350_440) { silent = false; dual = true; freq_a = 350.0f; freq_b = 440.0f; }
else if (index == kTone425) { silent = false; freq_a = kDialTone425; }
}
float scale = options.tone_gain;
float a = fast_exp2(static_cast<float>(level) * kToneLevelLog2);
if (silent) {
for (int i = 0; i < kFrameSamples; i++) buf[i] = 0.0f;
} else {
float s = scale * kToneScale;
a = s * a;
float a_single = a * kSqrt2; // the 425 Hz tone is a single tone at the level of two
int64_t inc1 = core::to_int64_sat(static_cast<double>(freq_a / kSampleRate) * 4294967296.0);
int64_t inc2 = core::to_int64_sat(static_cast<double>(freq_b / kSampleRate) * 4294967296.0);
if (index == kTone425) a = a_single;
uint32_t ph1 = tone_phase[0], ph2 = tone_phase[1];
const float* cosine = halfrate::tables::kCosine;
for (int i = 0; i < kFrameSamples; i++) {
float v = cosine[(ph1 + 0x40000u) >> 19];
if (dual) v = v + cosine[(ph2 + 0x40000u) >> 19];
buf[i] = a * v;
ph1 += static_cast<uint32_t>(inc1);
ph2 += static_cast<uint32_t>(inc2);
}
tone_phase[1] = ph2;
tone_phase[0] = ph1;
}
core::dequant_reset(dequant);
synth_reset(synth, options);
had_good = false;
return DecodedKind::tone;
}
// A voice or silence frame from its dequantised parameters: harmonic amplitudes, phase term, enhancement, high-band
// tilt, per-harmonic voiced / noise / pulse weights, pulse placement; then synthesis.
void Decoder::voice(const core::FrameParams& params, bool silence, float* buf) {
const Options& o = options;
int32_t voicing = params.fields[core::kFieldVoicing];
int32_t pitch = params.fields[core::kFieldPitch];
float voiced_share = fast_sqrt(o.mixed_voiced_share);
float noise_share = fast_sqrt(1.0f - o.mixed_voiced_share);
SynthFrame p{};
float phase[kHarmonicSlots] = {};
float amp[kFrameSamples]; // [1..L]
p.active = 1;
p.fundamental = params.fundamental;
int32_t count = params.harmonic_count;
p.harmonic_count = count;
const int32_t decoded_count = count;
const float* log2_amp = params.log2_amplitude.data();
if (decoded_count >= 1) {
for (int64_t k = 0; k < params.harmonic_count; k++) amp[1 + k] = fast_exp2(log2_amp[k]);
count = params.harmonic_count;
// phase term per harmonic: a weighted sum of log-amplitude differences around it (odd taps only if set)
int64_t taps = o.phase_taps;
float dispersion = o.phase_dispersion;
for (int64_t l = 1; l <= count; l++) {
int64_t l1 = l - 1;
float s = 0.0f;
if (taps >= 1) {
int64_t m = 0, nm = 0;
int64_t step = o.phase_odd_taps != 0 ? 2 : 1;
do {
float up = 0.0f, lo;
if (count >= static_cast<int32_t>(static_cast<int32_t>(l1) + static_cast<int32_t>(m) + 2)) {
up = log2_amp[l1 + m + 1];
up = up + o.phase_level_offset;
}
if (l1 + nm >= 1) {
lo = log2_amp[l1 + nm - 1];
lo = lo + o.phase_level_offset;
} else {
lo = 0.0f;
}
up = up - lo;
up = up * tables::kPhaseTapWeight[m + 1];
s = s + up;
m += step;
nm -= step;
} while (m + 1 <= taps);
}
s = dispersion * s;
s = s * kOneOverTwoPi;
phase[l] = s;
}
}
core::enhance_formants(enhance_options(o), params.fundamental, params.omega, params.harmonic_count, amp);
count = params.harmonic_count;
bool weights_done = false;
if (o.high_band_tilt != 0.0f) {
if (count < 1) {
weights_done = true;
} else {
// a gain rising from 1 at high_band_start * L to 2^high_band_tilt at L
for (int64_t l = 1;; l++) {
float t = static_cast<float>(static_cast<uint32_t>(l)) / static_cast<float>(count);
float start = o.high_band_start;
t = t - start;
float width = 1.0f - start;
t = t / width;
if (t > 0.0f) {
float a = amp[l];
t = t > 1.0f ? 1.0f : t;
t = t * o.high_band_tilt;
amp[l] = a * fast_exp2(t);
}
if (!(l < count)) break;
}
}
}
if (!weights_done && count >= 1) {
const char* classes = tables::kHarmonicClass[silence ? tables::kSilenceClassRow : (voicing & 0x1f)];
float w0 = params.fundamental;
float gain = o.output_gain;
for (int64_t k = 0; k < count; k++) {
float x = w0 * static_cast<float>(static_cast<uint32_t>(k + 1));
int32_t slot = core::to_int32_sat(static_cast<double>(x) * 32.0);
slot = slot < 15 ? slot : 15;
char c = classes[slot];
p.amplitude[1 + k] = amp[1 + k] * gain;
float weight = 1.0f;
std::array<float, kHarmonicSlots>* target = &p.unvoiced;
switch (c) {
case tables::kPulseNoise:
if (o.pulse_enable != 0) {
p.pulse[1 + k] = o.pulse_noise_pulse_weight;
weight = o.pulse_noise_noise_weight;
}
break;
case tables::kVoiced:
weight = o.voiced_weight;
target = &p.voiced;
break;
case tables::kMixed:
p.voiced[1 + k] = voiced_share * o.voiced_weight;
weight = noise_share;
break;
case tables::kPulse:
target = o.pulse_enable == 0 ? &p.unvoiced : &p.pulse;
break;
default:
break;
}
(*target)[1 + k] = weight;
}
for (int32_t l = 1; l <= count; l++) p.phase[l] = phase[l];
}
p.pulse_count = 0;
if (!silence && voicing >= kFirstPulseVoicing) {
if (o.pulse_enable != 0) {
float window = o.pulse_window;
float distance;
if ((voicing & 0x7ffffffe) == kPulsePairVoicing) {
// two pulses: positions from pitch / 11 and pitch % 11
int32_t q = pitch / 11, r = pitch - q * 11;
float s = o.pulse_pair_first_step * static_cast<float>(q);
s = o.pulse_pair_first_start + s;
s = s + 0.5f;
int32_t pos = core::to_int32_sat(s);
float s2 = o.pulse_pair_second_step * static_cast<float>(r);
s2 = o.pulse_pair_second_start + s2;
s2 = s2 + 0.5f;
int32_t pos2 = core::to_int32_sat(s2);
float d1 = static_cast<float>(static_cast<int32_t>(static_cast<uint32_t>(pos) - 160u));
d1 = pos < 160 ? -d1 : d1;
float d2 = static_cast<float>(static_cast<int32_t>(static_cast<uint32_t>(pos2) - 80u));
d1 = d1 / window;
d1 = 1.0f - d1;
d1 = core::max_num(d1, 0.0f);
p.pulse_gain[0] = d1;
p.pulse_position[1] = pos2;
distance = pos2 < 80 ? -d2 : d2;
p.pulse_count = 2;
p.pulse_position[0] = pos;
} else {
int32_t base = static_cast<uint32_t>(voicing) < static_cast<uint32_t>(kPulsePairVoicing)
? o.pulse_position_low : o.pulse_position_high;
p.pulse_position[0] = static_cast<int32_t>(static_cast<uint32_t>(base) + static_cast<uint32_t>(pitch));
float d = static_cast<float>(static_cast<int32_t>(static_cast<uint32_t>(pitch) -
static_cast<uint32_t>(o.pulse_window_center)));
bool below = static_cast<int64_t>(pitch) - static_cast<int64_t>(o.pulse_window_center) < 0;
distance = below ? -d : d;
p.pulse_count = 1;
}
float g = distance / window;
g = 1.0f - g;
g = core::max_num(g, 0.0f);
p.pulse_gain[p.pulse_count - 1] = g;
}
} else if (silence && decoded_count >= 1) {
float g = o.silence_gain;
for (int32_t k = 0; k < decoded_count; k++) p.amplitude[1 + k] = p.amplitude[1 + k] * g;
}
bad_count = 0;
muted = false;
synth_frame(synth, options, p, buf);
last_good = p;
had_good = true;
}
// Output stage: the high-pass (when highpass_hz > 0), the frame limiter, the soft limiter, rounding to 16 bits.
void Decoder::output(float* buf, int16_t* pcm) {
const Options& o = options;
float fc = o.highpass_hz;
HighPass& hp = highpass;
if (fc > 0.0f) {
if (!(hp.cutoff == fc)) {
// RBJ high-pass biquad, Q = 1/sqrt(2)
float t = fc * 8192.0f;
t = t / kSampleRate;
t = t + 0.5f;
int32_t i = core::to_int32_sat(static_cast<double>(t));
const float* cosine = halfrate::tables::kCosine;
float cos_w = cosine[static_cast<uint32_t>(i) & 0x1fff];
float alpha = cosine[(static_cast<uint32_t>(i) + 0x1800) & 0x1fff];
alpha = alpha * kSqrtHalf;
float a0 = alpha + 1.0f;
float one_plus_cos = cos_w + 1.0f;
hp.b1 = -one_plus_cos / a0;
float b0 = one_plus_cos * 0.5f, a1 = cos_w * -2.0f;
b0 = b0 / a0;
a1 = a1 / a0;
hp.b0 = b0;
hp.b2 = b0;
hp.a1 = a1;
hp.a2 = (1.0f - alpha) / a0;
hp.cutoff = fc;
}
for (HighPass::Section& z : hp.sections) {
for (int i = 0; i < kFrameSamples; i++) {
float x = buf[i];
float y = x * hp.b0;
float p0 = hp.b1 * z.x1, p1 = hp.b2 * z.x2;
y = y + p0;
y = y + p1;
float q0 = hp.a1 * z.y1, q1 = hp.a2 * z.y2;
y = y - q0;
y = y - q1;
float old_x1 = z.x1, old_y1 = z.y1;
z.x1 = x;
z.x2 = old_x1;
// written as !(>=) so a NaN is flushed too rather than kept in the filter state
z.y1 = !(std::fabs(y) >= kFlushBelow) ? 0.0f : y;
z.y2 = !(std::fabs(old_y1) >= kFlushBelow) ? 0.0f : old_y1;
buf[i] = y;
}
}
}
if (o.frame_limiter != 0) {
// frame rms in dB; above the knee the excess is scaled by the ratio; the gain ramps across the frame
float e = 0.0f;
for (int i = 0; i < kFrameSamples; i++) { float q = buf[i] * buf[i]; e = e + q; }
e = e * kOneOver160;
float db = 0.0f, g = 1.0f;
if (e > 1.0f) db = fast_log2(e) * kDbPerLog2;
float knee = o.frame_limiter_knee_db;
if (db > knee) {
float t = db - knee;
t = t * o.frame_limiter_ratio;
t = knee + t;
t = t - db;
t = t * kLog2PerDb;
g = fast_exp2(t);
}
for (uint32_t i = 0; i < kFrameSamples; i++) {
float r = static_cast<float>(i + 1) * kOneOver160;
float g0 = limiter_gain;
float dg = g - g0;
r = r * dg;
r = g0 + r;
buf[i] = buf[i] * r;
}
limiter_gain = g;
}
// soft limiter: above the threshold x - t is mapped to (x - t) / (1 + (x - t) / (ceiling - t))
float t = o.limiter_threshold;
bool on = !(t <= 0.0f);
float headroom = kLimiterCeiling - t;
for (int i = 0; i < kFrameSamples; i++) {
float x = buf[i];
bool negative = x < 0.0f;
float ax = negative ? -x : x;
if (on && !(t >= ax)) {
float v = ax - t;
v = v / headroom;
float w = v * headroom;
v = v + 1.0f;
v = w / v;
v = t + v;
x = negative ? -v : v;
}
bool high = x >= 32767.0f, low = -32768.0f >= x;
if (!high && !low) {
float h = x >= 0.0f ? 0.5f : -0.5f;
pcm[i] = static_cast<int16_t>(static_cast<uint16_t>(core::to_int32_sat(static_cast<double>(x + h))));
} else {
pcm[i] = low && !high ? -32768 : 32767;
}
}
}
DecodedKind Decoder::decode_bits(const uint8_t* in, int16_t* pcm) {
uint8_t bits[core::kFrameBits];
if (options.rotated_layout == 1) core::standard_from_rotated(in, bits);
else for (int i = 0; i < core::kFrameBits; i++) bits[i] = in[i];
const core::FrameFields received = core::fields_from_bits(bits);
float buf[kFrameSamples];
core::FrameParams params;
DecodedKind kind;
int32_t pitch = received[core::kFieldPitch];
if (pitch < kFirstNonVoicePitch) {
// voice. Pulse frames may be dequantised with a fixed pitch index; the received one places the pulses.
core::FrameFields fields = received;
if (received[core::kFieldVoicing] >= kFirstPulseVoicing && options.pulse_pitch_index >= 0) {
fields[core::kFieldPitch] = options.pulse_pitch_index;
core::dequantize(dequant, fields, &params);
params.fields[core::kFieldPitch] = received[core::kFieldPitch];
} else {
core::dequantize(dequant, fields, &params);
}
voice(params, false, buf);
kind = DecodedKind::voice;
} else if ((received[core::kFieldVoicing] & ~1) == kSilenceVoicing) {
// silence: dequantised at a fixed pitch; silence_predictor decides what that does to the predictor
core::FrameFields fields = received;
fields[core::kFieldPitch] = kSilencePitch;
int32_t mode = options.silence_predictor;
if (mode == 2 || mode == 3) {
core::DequantState saved = dequant;
if (mode == 2) core::dequant_reset(dequant);
core::dequantize(dequant, fields, &params);
dequant = saved;
} else {
if (mode == 1) core::dequant_reset(dequant);
core::dequantize(dequant, fields, &params);
if (mode == 4 || mode == 1) core::dequant_reset(dequant);
}
params.fields[core::kFieldVoicing] = kSilenceVoicing;
voice(params, true, buf);
kind = DecodedKind::silence;
} else {
// the tone fields are read from the frame as received (no layout rotation)
kind = tone_or_erasure(in, buf);
}
output(buf, pcm);
return kind;
}
void Decoder::decode_erased(int16_t* pcm) {
float buf[kFrameSamples];
conceal(buf);
output(buf, pcm);
}
} // namespace halfrate::dec
+60
View File
@@ -0,0 +1,60 @@
// The frame decoder: voice, silence and tone frames, erasures (repeat, mute, comfort noise), and the output stage
// (high-pass, frame limiter, soft limiter, conversion to 16 bits).
#pragma once
#include <array>
#include <cstdint>
#include "dequant.h"
#include "decoder_options.h"
#include "decoder_synth.h"
namespace halfrate::dec {
// What a frame decoded as.
enum class DecodedKind : int32_t {
voice = 0,
silence = 1,
tone = 2,
erasure = 3, // concealed: a repeat of the last good frame, a muted frame or comfort noise
};
// Two cascaded biquad high-pass sections with the same coefficients.
struct HighPass {
float cutoff = -1.0f; // the cutoff (Hz) the coefficients are for
float b0 = 0.0f, b1 = 0.0f, b2 = 0.0f, a1 = 0.0f, a2 = 0.0f;
struct Section {
float x1 = 0.0f, x2 = 0.0f, y1 = 0.0f, y2 = 0.0f;
};
std::array<Section, 2> sections{};
};
struct Decoder {
Options options;
core::DequantState dequant;
SynthState synth;
SynthFrame last_good; // the last voice or silence frame (amplitudes scaled by each repeat)
bool had_good = false; // a voice or silence frame was decoded since the start or the last tone
int32_t bad_count = 0; // consecutive erased frames
bool muted = false; // erasures have gone past repeating
std::array<uint32_t, 2> tone_phase{}; // tone oscillators, 2^32 = one cycle
float limiter_gain = 1.0f; // frame limiter gain reached at the end of the previous frame
HighPass highpass;
Decoder();
// Back to the start-of-stream state; the options are kept.
void reset();
// Sets an option by name (options.h); false for an unknown name.
bool set(const char* name, double value);
// One 49-bit frame (one bit per byte) to 160 samples.
DecodedKind decode_bits(const uint8_t* bits49, int16_t* pcm);
// One erased frame (no frame received) to 160 samples.
void decode_erased(int16_t* pcm);
private:
void conceal(float* buf);
DecodedKind tone_or_erasure(const uint8_t* bits49, float* buf);
void voice(const core::FrameParams& params, bool silence, float* buf);
void output(float* buf, int16_t* pcm);
};
} // namespace halfrate::dec
+47
View File
@@ -0,0 +1,47 @@
// The decoder half of the C API (include/halfrate.h).
#include <cstring>
#include <new>
#include "fec.h"
#include "frame.h"
#include "decoder.h"
#include "halfrate.h"
struct hr_decoder {
halfrate::dec::Decoder decoder;
};
extern "C" {
hr_decoder* hr_decoder_new(void) { return new (std::nothrow) hr_decoder(); }
void hr_decoder_free(hr_decoder* d) { delete d; }
int hr_decoder_set(hr_decoder* d, const char* name, double value) {
if (!d || !name) return -1;
return d->decoder.set(name, value) ? 0 : -1;
}
int hr_decode(hr_decoder* d, const uint8_t* frame, int16_t* pcm) {
if (!pcm) return -1;
if (!d) {
std::memset(pcm, 0, HR_SAMPLES * sizeof(int16_t));
return -1;
}
if (!frame) {
d->decoder.decode_erased(pcm);
return HR_ERASURE;
}
uint8_t bits[HR_BITS];
halfrate::core::bits_from_bytes(frame, bits);
return static_cast<int>(d->decoder.decode_bits(bits, pcm));
}
int hr_decode_air(hr_decoder* d, const uint8_t* air, int16_t* pcm) {
if (!d || !air) return hr_decode(d, nullptr, pcm);
uint8_t frame[HR_FRAME_BYTES];
halfrate::core::fec_decode_frame(air, frame, nullptr);
return hr_decode(d, frame, pcm);
}
} // extern "C"
+144
View File
@@ -0,0 +1,144 @@
#include "decoder_options.h"
#include <cstring>
#include "numeric.h"
namespace halfrate::dec {
namespace {
enum class Kind { real, integer, integer_resets_synth };
struct OptionEntry {
const char* name;
Kind kind;
float Options::*real;
int32_t Options::*integer;
int32_t low, high; // integer options are clamped to [low, high]
};
constexpr int32_t kIntMin = INT32_MIN, kIntMax = INT32_MAX;
constexpr OptionEntry real(const char* name, float Options::*field) {
return {name, Kind::real, field, nullptr, 0, 0};
}
constexpr OptionEntry integer(const char* name, int32_t Options::*field, int32_t low = kIntMin, int32_t high = kIntMax) {
return {name, Kind::integer, nullptr, field, low, high};
}
constexpr OptionEntry resetting(const char* name, int32_t Options::*field) {
return {name, Kind::integer_resets_synth, nullptr, field, kIntMin, kIntMax};
}
const OptionEntry kOptionTable[] = {
real("output_gain", &Options::output_gain),
real("mixed_voiced_share", &Options::mixed_voiced_share),
real("mixed_pulse_share", &Options::mixed_pulse_share),
real("pulse_noise_pulse_weight", &Options::pulse_noise_pulse_weight),
real("pulse_noise_noise_weight", &Options::pulse_noise_noise_weight),
real("voiced_weight", &Options::voiced_weight),
real("high_band_start", &Options::high_band_start),
real("high_band_tilt", &Options::high_band_tilt),
// replaces a voice frame's pitch index, so it must be a voice pitch: at most 119 (120 and up are tones and
// erasures, which have no fundamental)
integer("pulse_pitch_index", &Options::pulse_pitch_index, kIntMin, 119),
real("pulse_window", &Options::pulse_window),
integer("pulse_window_center", &Options::pulse_window_center),
integer("pulse_enable", &Options::pulse_enable),
integer("pulse_position_low", &Options::pulse_position_low),
integer("pulse_position_high", &Options::pulse_position_high),
real("pulse_pair_first_start", &Options::pulse_pair_first_start),
real("pulse_pair_first_step", &Options::pulse_pair_first_step),
real("pulse_pair_second_start", &Options::pulse_pair_second_start),
real("pulse_pair_second_step", &Options::pulse_pair_second_step),
real("phase_dispersion", &Options::phase_dispersion),
real("phase_level_offset", &Options::phase_level_offset),
// the phase term's tap weights are a table of 32
integer("phase_taps", &Options::phase_taps, 1, 32),
integer("phase_odd_taps", &Options::phase_odd_taps),
real("silence_gain", &Options::silence_gain),
integer("silence_predictor", &Options::silence_predictor),
integer("rotated_layout", &Options::rotated_layout),
real("tone_gain", &Options::tone_gain),
integer("enhance", &Options::enhance),
real("enhance_reserved", &Options::enhance_reserved),
real("enhance_strength", &Options::enhance_strength),
real("enhance_min_weight", &Options::enhance_min_weight),
real("enhance_max_weight", &Options::enhance_max_weight),
integer("enhance_bypass_divisor", &Options::enhance_bypass_divisor),
real("enhance_power_high", &Options::enhance_power_high),
real("enhance_power_low", &Options::enhance_power_low),
real("enhance_split_frequency", &Options::enhance_split_frequency),
integer("repeat_max", &Options::repeat_max),
real("repeat_gain", &Options::repeat_gain),
real("repeat_high_gain", &Options::repeat_high_gain),
real("repeat_high_start", &Options::repeat_high_start),
real("comfort_noise_level", &Options::comfort_noise_level),
integer("fec_error_threshold", &Options::fec_error_threshold),
real("highpass_hz", &Options::highpass_hz),
integer("frame_limiter", &Options::frame_limiter),
real("frame_limiter_knee_db", &Options::frame_limiter_knee_db),
real("frame_limiter_ratio", &Options::frame_limiter_ratio),
real("limiter_threshold", &Options::limiter_threshold),
real("unvoiced_level_db", &Options::unvoiced_level_db),
real("unvoiced_tilt", &Options::unvoiced_tilt),
real("unvoiced_gain", &Options::unvoiced_gain),
real("pulse_gain", &Options::pulse_gain),
real("pulse_length", &Options::pulse_length),
integer("pulse_minimum_phase", &Options::pulse_minimum_phase),
integer("pulse_shift", &Options::pulse_shift),
real("voiced_fade_start", &Options::voiced_fade_start),
real("voiced_fade_end", &Options::voiced_fade_end),
// the single-block noise (320 samples) is added at this offset into 160 + 320 samples of output and overlap
integer("noise_offset", &Options::noise_offset, 0, 160),
resetting("initial_phase", &Options::initial_phase),
real("unvoiced_top_extend", &Options::unvoiced_top_extend),
integer("unvoiced_low_cut", &Options::unvoiced_low_cut),
integer("phase_tracking", &Options::phase_tracking),
integer("amplitude_log_interp", &Options::amplitude_log_interp),
integer("amplitude_hold", &Options::amplitude_hold),
integer("amplitude_frequency_map", &Options::amplitude_frequency_map),
integer("noise_method", &Options::noise_method),
integer("noise_coherent_bins", &Options::noise_coherent_bins),
real("noise_phase_jitter", &Options::noise_phase_jitter),
integer("noise_white", &Options::noise_white),
real("noise_gain_bin1", &Options::noise_gain_bin1),
real("noise_gain_bins23", &Options::noise_gain_bins23),
real("pulse_highpass_hz", &Options::pulse_highpass_hz),
resetting("noise_seed", &Options::noise_seed),
real("voicing_off_start", &Options::voicing_off_start),
real("voicing_off_end", &Options::voicing_off_end),
real("voicing_on_start", &Options::voicing_on_start),
real("voicing_on_end", &Options::voicing_on_end),
real("harmonic_fade_start", &Options::harmonic_fade_start),
real("harmonic_fade_end", &Options::harmonic_fade_end),
integer("pitch_glide_start", &Options::pitch_glide_start),
integer("pitch_glide_end", &Options::pitch_glide_end),
integer("amplitude_glide_start", &Options::amplitude_glide_start),
integer("amplitude_glide_end", &Options::amplitude_glide_end),
};
} // namespace
bool set_option(Options& options, const char* name, double value, SetEffect* effect) {
if (effect) *effect = SetEffect::none;
if (!name) return false;
// NaN and infinities would stay in the filter and predictor state after the option is set back
if (!std::isfinite(value)) return false;
double half = value >= 0.0 ? 0.5 : -0.5;
int32_t rounded = core::to_int32_sat(value + half);
for (const OptionEntry& entry : kOptionTable) {
if (std::strcmp(name, entry.name) != 0) continue;
if (entry.kind == Kind::real) {
options.*entry.real = static_cast<float>(value);
} else {
int32_t v = rounded < entry.low ? entry.low : rounded;
options.*entry.integer = v > entry.high ? entry.high : v;
if (entry.kind == Kind::integer_resets_synth && effect) *effect = SetEffect::reset_synth;
}
return true;
}
return false;
}
} // namespace halfrate::dec
+108
View File
@@ -0,0 +1,108 @@
// Decoder options: every tuning value of the decoder and its synthesiser, with their defaults, and setting them
// by name. docs/options.md describes each one.
#pragma once
#include <cstdint>
namespace halfrate::dec {
struct Options {
// ---- frame decoding and parameter shaping
float output_gain = 0x1.0a3d7p+1f; // 2.08: every harmonic amplitude is scaled by this
float mixed_voiced_share = 0.85f; // mixed harmonics: voiced weight sqrt(share), noise sqrt(1 - share)
float mixed_pulse_share = 0.5f; // stored only, no effect
float pulse_noise_pulse_weight = 0x1.ccccccp-1f; // 0.9: pulse weight of pulse-plus-noise harmonics
float pulse_noise_noise_weight = 0x1.3d70a4p-1f; // 0.62: noise weight of pulse-plus-noise harmonics
float voiced_weight = 1.0f; // voiced weight of voiced harmonics (and scale of mixed ones)
float high_band_start = 0x1.4cccccp-1f; // 0.65: the high-band tilt starts at this fraction of L
float high_band_tilt = 0x1.19999ap-1f; // 0.55: log2 gain reached at the top harmonic; 0 = off
int32_t pulse_pitch_index = 119; // pulse frames dequantise with this pitch index (< 0: their own)
float pulse_window = 88.0f; // a pulse's gain falls linearly to 0 over this many samples
int32_t pulse_window_center = 62; // single pulses: distance is counted from this pitch index
int32_t pulse_enable = 1; // 0: pulse codes decode as noise and no pulses are placed
int32_t pulse_position_low = 98; // single pulse position = this + pitch index (voicing 18..23)
int32_t pulse_position_high = 16; // the same for voicing 26..31
float pulse_pair_first_start = 101.0f; // pulse pairs (voicing 24, 25): first position start + step * (b0 / 11)
float pulse_pair_first_step = 0x1.79999ap+3f; // 11.8
float pulse_pair_second_start = 21.0f; // second position start + step * (b0 % 11)
float pulse_pair_second_step = 0x1.79999ap+3f;
float phase_dispersion = 0x1.333334p-2f; // 0.3: weight of the phase term from the log-amplitude slope
float phase_level_offset = 0.0f; // added to each log amplitude the phase term reads
int32_t phase_taps = 19; // taps of the phase term (clamped to 1..32)
int32_t phase_odd_taps = 1; // nonzero: odd taps only
float silence_gain = 0x1.333334p-2f; // 0.3: amplitude scale of silence frames
int32_t silence_predictor = 3; // what a silence frame does to the predictor (docs/options.md)
int32_t rotated_layout = 1; // 1: frames arrive in the rotated bit layout
float tone_gain = 1.0f; // tone frame level scale
// ---- enhancement (core::EnhanceOptions)
int32_t enhance = 1; // the formant enhancement runs only when this is exactly 1
float enhance_reserved = 1.0f; // stored only, no effect
float enhance_strength = 0.5f;
float enhance_min_weight = 0x1.333334p-2f; // 0.3
float enhance_max_weight = 2.0f;
int32_t enhance_bypass_divisor = 0;
float enhance_power_high = 0x1.b33334p-1f; // 0.85
float enhance_power_low = 0x1.333334p-1f; // 0.6
float enhance_split_frequency = 0.125f; // cycles per sample
// ---- erasures
int32_t repeat_max = 3; // erased frames that repeat the last good one before muting
float repeat_gain = 1.0f; // amplitude scale per repeat (compounding)
float repeat_high_gain = 1.0f; // extra scale per repeat at half the sample rate (compounding)
float repeat_high_start = 0.3f; // the lift rises in dB from here, cycles per sample (2.4 kHz)
float comfort_noise_level = 1.0f; // rms of the comfort noise after muting
int32_t fec_error_threshold = 6; // stored only, no effect on this frame format
// ---- output stage
float highpass_hz = 0.0f; // cutoff of the 4th-order high-pass; <= 0 = off
int32_t frame_limiter = 1; // nonzero: the frame level limiter runs
float frame_limiter_knee_db = 78.5f; // frame rms (dB) above which it compresses
float frame_limiter_ratio = 0x1.333334p-2f; // 0.3: slope above the knee
float limiter_threshold = 28000.0f; // soft limiter start (towards 32766); <= 0 = off
float unvoiced_level_db = 0.0f; // stored only, no effect
float unvoiced_tilt = 0.0f; // stored only, no effect
// ---- synthesiser
float unvoiced_gain = 0x1.b33334p-1f; // 0.85
float pulse_gain = 1.0f;
float pulse_length = 48.0f; // half width of the pulse's cosine window, samples
int32_t pulse_minimum_phase = 0; // nonzero: minimum-phase pulse shape, else zero phase
int32_t pulse_shift = 0; // samples added to each pulse position
float voiced_fade_start = 0.0f; // per-harmonic oscillators: fade-out of a harmonic that stops
float voiced_fade_end = 160.0f;
int32_t noise_offset = 0; // single-block noise: sample offset (clamped to 0..160)
int32_t initial_phase = 0; // oscillator start phases: 0 zero, 1 random, 2 quadratic
float unvoiced_top_extend = 0.0f; // single-block noise and pulses: reach above harmonic L
int32_t unvoiced_low_cut = 1; // nonzero: no noise below harmonic 1/2
int32_t phase_tracking = 1; // 1: one fundamental phase track, else per-harmonic oscillators
int32_t amplitude_log_interp = 1; // nonzero: amplitudes glide log-linearly
int32_t amplitude_hold = 0;
int32_t amplitude_frequency_map = 0;
int32_t noise_method = 1; // 1: four 40-sample blocks, 0: one 320-sample block, else none
int32_t noise_coherent_bins = 64; // noise bins below this keep a drifting phase
float noise_phase_jitter = 0.5f;
int32_t noise_white = 0;
float noise_gain_bin1 = 0.0f;
float noise_gain_bins23 = 0x1.333334p-3f; // 0.15
float pulse_highpass_hz = 0.0f;
int32_t noise_seed = 0;
float voicing_off_start = 5.0f; // crossfade window (samples) of a harmonic losing voicing
float voicing_off_end = 50.0f;
float voicing_on_start = 55.0f; // and of one gaining it
float voicing_on_end = 90.0f;
float harmonic_fade_start = 0.0f; // and of one present in only one of the two frames
float harmonic_fade_end = 160.0f;
int32_t pitch_glide_start = 0; // the fundamental glides over these samples
int32_t pitch_glide_end = 160;
int32_t amplitude_glide_start = 0; // the amplitudes glide over these samples
int32_t amplitude_glide_end = 160;
};
// What setting an option does besides storing the value.
enum class SetEffect { none, reset_synth };
// Sets option name to value: float options take (float)value, integer options value rounded half away from zero
// (saturating), some are clamped (docs/options.md). Returns false for an unknown name.
bool set_option(Options& options, const char* name, double value, SetEffect* effect);
} // namespace halfrate::dec
+629
View File
@@ -0,0 +1,629 @@
#include "decoder_synth.h"
#include <cmath>
#include "fastmath.h"
#include "fft.h"
#include "numeric.h"
#include "tables.h"
namespace halfrate::dec {
namespace {
using core::fast_exp2;
using core::fast_log2;
using core::fast_sqrt;
using core::to_int32_sat;
using core::to_int64_sat;
constexpr float kOneOver160 = 0x1.99999ap-8f;
constexpr float kOneOverTwoPi = 0x1.45f306p-3f;
constexpr float kLogFloor = 0x1.0c6f7ap-20f; // 1e-6
constexpr float kLn2 = 0x1.62e43p-1f;
constexpr float kLog2E = 0x1.715476p+0f;
constexpr float kWhiteNoiseScale = 0x1.186f18p-2f;
constexpr float kSqrt3 = 0x1.bb67aep+0f; // uniform noise of amplitude 1 has rms 1/sqrt(3)
constexpr float kFlushBelow = 1.0000000031710769e-30f;
constexpr uint32_t kSeedOffset = 0x51c0de01u;
const float* const kCos = tables::kCosine;
inline uint32_t next_random(uint32_t x) { return x * 1664525u + 1013904223u; }
// x * 2^bits to int64, truncating and saturating
inline int64_t to_fixed64(float x, int bits) { return to_int64_sat(static_cast<double>(x) * static_cast<double>(1ull << bits)); }
// a fundamental (cycles per sample) as a 32.32 fixed-point phase increment, in the top half of 64 bits
inline uint64_t phase_increment(float fundamental) { return static_cast<uint64_t>(to_fixed64(fundamental, 32)) << 32; }
// high 64 bits of the signed 128-bit product
inline int64_t multiply_high(int64_t a, int64_t b) { return static_cast<int64_t>((static_cast<__int128>(a) * b) >> 64); }
// NaN (a zero-length crossfade at its start point) gives 0
inline float clamp01(float v) {
v = !(v >= 0.0f) ? 0.0f : v;
return v > 1.0f ? 1.0f : v;
}
// The noise spectrum of a frame at the 65 bins of a 128-point FFT: amplitude[k] and its unvoiced weight band[k],
// taken from the harmonic nearest bin k.
void noise_bins(const Options& options, const SynthFrame& frame, float* amplitude, float* band) {
if (frame.active == 0) {
for (int k = 0; k < 65; k++) { amplitude[k] = 0.0f; band[k] = 0.0f; }
return;
}
float w0 = frame.fundamental;
float period = 1.0f / w0;
float gain = options.unvoiced_gain;
gain = gain * fast_sqrt(32.0f / w0);
for (uint32_t k = 0; k < 65; k++) {
float x = period * (static_cast<float>(k) * (1.0f / 128.0f));
x = x + 0.5f;
int32_t l = to_int32_sat(x);
amplitude[k] = 0.0f;
band[k] = 0.0f;
if (l <= 0) {
if (options.unvoiced_low_cut != 0) continue;
l = 1;
}
if (l > frame.harmonic_count) continue;
float bin_gain;
if (k == 1) bin_gain = options.noise_gain_bin1;
else if (k <= 3) bin_gain = options.noise_gain_bins23;
else bin_gain = 1.0f;
float a = gain * frame.amplitude[l];
amplitude[k] = a * bin_gain;
band[k] = frame.unvoiced[l];
}
}
// In-place 128-point forward FFT (kernel e^-j), unscaled.
void fft128(float* re, float* im) {
for (int i = 0; i < 128; i++) {
int j = 0;
for (int b = 0; b < 7; b++) j |= ((i >> b) & 1) << (6 - b);
if (i < j) {
float a = re[i], b = im[i];
re[i] = re[j]; im[i] = im[j];
re[j] = a; im[j] = b;
}
}
for (uint32_t len = 2; len <= 128; len *= 2) {
uint32_t half = len >> 1;
uint32_t step = 8192u / len;
for (uint32_t base = 0; base < 128; base += len) {
for (uint32_t k = 0; k < half; k++) {
float c = kCos[k * step];
float s = kCos[(0x1800 + k * step) & 0x1fff];
uint32_t p = base + half + k, q = base + k;
float xr = re[p], xi = im[p];
float tr = c * xr + s * xi;
float ti = c * xi - s * xr;
re[p] = re[q] - tr;
im[p] = im[q] - ti;
re[q] = tr + re[q];
im[q] = ti + im[q];
}
}
}
}
// The spectrum at the 257 bins of a 512-point FFT: amplitude times weight of the harmonic nearest each bin.
// Bins above harmonic L get harmonic L's value up to L + 0.5 + top * L when top > 0.
void spectrum_bins(const SynthFrame& frame, const float* weight, int low_cut, float top, float* out) {
float period = 1.0f / frame.fundamental;
for (uint32_t k = 0; k < 257; k++) {
float h = period * (static_cast<float>(k) * (1.0f / 512.0f));
int32_t l = to_int32_sat(h + 0.5f);
float v = 0.0f;
if (!(l <= 0 && low_cut != 0)) {
int32_t m = l > 0 ? l : 1;
int32_t count = frame.harmonic_count;
bool inside = true;
if (m > count) {
inside = false;
if (top > 0.0f) {
float fl = static_cast<float>(count);
float limit = (fl + 0.5f) + top * fl;
if (h < limit) { m = count; inside = true; }
}
}
if (inside) v = frame.amplitude[m] * weight[m];
}
out[k] = v;
}
}
// In-place inverse 512-point FFT, scaled by 1/512.
void ifft512(float* re, float* im) {
for (int i = 0; i < 512; i++) im[i] = -im[i];
core::fft512(re, im);
for (int i = 0; i < 512; i++) {
re[i] = re[i] * (1.0f / 512.0f);
im[i] = im[i] * (-1.0f / 512.0f);
}
}
// The ramp start and end of a glide over [start, end) samples, clamped to the frame; the whole frame when empty.
void glide_span(int32_t start, int32_t end, int32_t* from, int32_t* to) {
if (start < 0) start = 0;
if (end >= kFrameSamples) end = kFrameSamples;
if (end > start) { *to = end; *from = start; }
else { *to = kFrameSamples; *from = 0; }
}
// Harmonics with phase_tracking 1: one fundamental phase track, harmonic l at l times it plus its own phase offset.
void harmonics_tracked(SynthState& state, const Options& options, const SynthFrame& frame, float* out,
int count, uint64_t previous_increment) {
const SynthFrame& prev = state.previous;
uint64_t current_increment = previous_increment;
if (frame.active) current_increment = phase_increment(frame.fundamental);
// the fundamental's phase per sample; its frequency glides between the two frames' over the pitch glide span
uint64_t phase[kFrameSamples];
int32_t start, end;
glide_span(options.pitch_glide_start, options.pitch_glide_end, &start, &end);
int64_t slope = static_cast<int64_t>(current_increment - previous_increment) / static_cast<int64_t>(end - start);
int64_t half_slope = static_cast<int64_t>(static_cast<uint64_t>(slope) + (static_cast<uint64_t>(slope) >> 63)) >> 1;
uint64_t acc = state.fundamental_phase;
uint64_t freq = previous_increment;
for (int64_t n = 0; n < kFrameSamples; n++) {
phase[n] = acc;
uint64_t step = freq + half_slope, next = freq + slope;
if (n >= end) { next = freq; step = current_increment; }
if (static_cast<uint64_t>(n) < static_cast<uint64_t>(static_cast<uint32_t>(start))) step = previous_increment;
else freq = next;
acc = step + acc;
}
state.fundamental_phase = acc;
for (int64_t l = 1; l <= count; l++) {
bool in_prev = prev.active ? l <= prev.harmonic_count : false;
bool in_cur = frame.active ? l <= frame.harmonic_count : false;
float amp_prev = in_prev ? prev.amplitude[l] : 0.0f;
float amp_cur = in_cur ? frame.amplitude[l] : 0.0f;
float voi_prev = in_prev ? prev.voiced[l] : 0.0f;
float voi_cur = in_cur ? frame.voiced[l] : 0.0f;
if (voi_prev == 0.0f && voi_cur == 0.0f) continue;
bool both = in_prev && in_cur;
if (both && options.amplitude_frequency_map && voi_prev > 0.0f && voi_cur > 0.0f) {
// the previous frame's amplitude at the frequency of this frame's harmonic l
float x = static_cast<float>(static_cast<uint32_t>(l));
x = frame.fundamental * x;
x = x / prev.fundamental;
int32_t i = to_int32_sat(x);
float fraction = x - static_cast<float>(to_int32_sat(x));
if (i < 1) fraction = 0.0f;
i = i > 1 ? i : 1;
int32_t prev_count = prev.harmonic_count;
float frac = i < prev_count ? fraction : 0.0f;
i = i < prev_count ? i : prev_count;
amp_prev = prev.amplitude[i];
if (frac > 0.0f && amp_prev > 0.0f && prev.amplitude[i + 1] > 0.0f) {
float l0 = fast_log2(amp_prev);
float l1 = fast_log2(prev.amplitude[i + 1]);
float l0b = fast_log2(prev.amplitude[i]);
float y = l1 - l0b;
y = frac * y;
y = l0 + y;
amp_prev = fast_exp2(y);
}
}
if (!in_prev || (voi_prev == 0.0f && options.amplitude_hold)) amp_prev = amp_cur;
if (!in_cur || (options.amplitude_hold && voi_cur == 0.0f)) amp_cur = amp_prev;
float lo, hi;
if (!both) { lo = options.harmonic_fade_start; hi = options.harmonic_fade_end; }
else if (voi_cur < voi_prev) { lo = options.voicing_off_start; hi = options.voicing_off_end; }
else { lo = options.voicing_on_start; hi = options.voicing_on_end; }
float phase_cur = in_cur ? frame.phase[l] : prev.phase[l];
float phase_prev = phase_cur;
if (in_prev) phase_prev = prev.phase[l];
float dphase = phase_cur - phase_prev;
float round = dphase >= 0.0f ? 0.5f : -0.5f;
bool use_log = (amp_cur > 0.0f && amp_prev > 0.0f) && options.amplitude_log_interp != 0;
int32_t amp_start, amp_end;
glide_span(options.amplitude_glide_start, options.amplitude_glide_end, &amp_start, &amp_end);
float ratio, amp_slope;
if (use_log) {
float lc = fast_log2(amp_cur);
float lp = fast_log2(amp_prev);
float y = lc - lp;
float r = 1.0f / static_cast<float>(amp_end - amp_start);
ratio = fast_exp2(r * y);
amp_slope = 0.0f;
} else {
float d = amp_cur - amp_prev;
float r = 1.0f / static_cast<float>(amp_end - amp_start);
amp_slope = d * r;
ratio = 1.0f;
}
float phase_wrap = dphase - static_cast<float>(to_int32_sat(dphase + round));
float voicing_span = hi - lo;
float dvoicing = voi_cur - voi_prev;
float amp = amp_prev;
for (uint64_t n = 0; n < kFrameSamples; n++) {
if (n < static_cast<uint64_t>(static_cast<uint32_t>(amp_start)) || static_cast<int64_t>(n) >= amp_end) {
amp = static_cast<int64_t>(n) < amp_end ? amp : amp_cur;
} else if (use_log) {
amp = ratio * amp;
} else {
amp = amp_prev + amp_slope * static_cast<float>(static_cast<int32_t>(n) - amp_start + 1);
}
float t = static_cast<float>(static_cast<uint32_t>(n + 1));
float phi = t * kOneOver160;
phi = phase_wrap * phi;
phi = phase_prev + phi;
float v = t - lo;
v = v / voicing_span;
v = clamp01(v);
v = dvoicing * v;
v = voi_prev + v;
uint64_t x = phase[n] * static_cast<uint64_t>(l);
int64_t offset = to_fixed64(phi, 32);
v = v * amp;
x = x + (static_cast<uint64_t>(offset) << 32);
x = x + (1ull << 50);
x >>= 51;
v = v * kCos[x];
out[n] = out[n] + v;
}
}
}
// Harmonics with any other phase_tracking: one phase accumulator per harmonic, linear frequency glide.
void harmonics_accumulated(SynthState& state, const Options& options, const SynthFrame& frame, float* out,
int count, uint64_t previous_increment) {
const SynthFrame& prev = state.previous;
int cur_active = frame.active;
uint64_t current_increment = previous_increment;
if (cur_active) current_increment = phase_increment(frame.fundamental);
int64_t cur_count = frame.harmonic_count, prev_count = prev.harmonic_count;
int prev_active = prev.active;
for (int64_t l = 1; l <= count; l++) {
bool absent_prev = !(prev_active != 0 && l <= prev_count);
bool absent_cur = !(cur_active != 0 && l <= cur_count);
float amp_prev = 0.0f, amp_cur = 0.0f;
if (!absent_prev) amp_prev = prev.amplitude[l] * prev.voiced[l];
if (!absent_cur) amp_cur = frame.amplitude[l] * frame.voiced[l];
uint64_t freq_cur = current_increment * static_cast<uint64_t>(l);
uint64_t acc = state.harmonic_phase[l];
uint64_t freq_prev = previous_increment * static_cast<uint64_t>(l);
if (absent_prev) freq_prev = freq_cur;
int64_t delta = static_cast<int64_t>(freq_cur - freq_prev);
if (absent_cur) delta = 0;
// delta / 160 and delta / 320, truncating
int64_t h = multiply_high(delta, 0x6666666666666667LL);
int64_t step = (h >> 6) + static_cast<int64_t>(static_cast<uint64_t>(h) >> 63);
if (amp_prev == 0.0f && amp_cur == 0.0f) {
acc = static_cast<uint64_t>(step) * 12800u + freq_prev * 160u + acc;
state.harmonic_phase[l] = acc;
continue;
}
bool fade = false;
if (amp_prev != 0.0f && amp_cur == 0.0f) fade = l > cur_count ? false : cur_active != 0;
uint64_t freq = static_cast<uint64_t>((h >> 7) + static_cast<int64_t>(static_cast<uint64_t>(h) >> 63)) + freq_prev;
float damp = amp_cur - amp_prev;
damp = damp * kOneOver160;
for (uint64_t n = 0; n < kFrameSamples; n++) {
float a;
float fade_end = options.voiced_fade_end, fade_start = options.voiced_fade_start;
if (fade && fade_end > fade_start) {
float x = static_cast<float>(static_cast<uint32_t>(n)) - fade_start;
float y = fade_end - fade_start;
x = x / y;
x = clamp01(x);
x = x * 0.5f;
x = cosine_interpolated(x);
x = x * 0.5f;
x = x + 0.5f;
a = amp_prev * x;
} else {
a = damp * static_cast<float>(static_cast<uint32_t>(n + 1));
a = amp_prev + a;
}
a = a * kCos[(acc + (1ull << 50)) >> 51];
out[n] = out[n] + a;
acc = freq + acc;
freq = freq + static_cast<uint64_t>(step);
}
state.harmonic_phase[l] = acc;
}
}
// noise_method 1: noise in four 40-sample blocks, 128-point FFTs, 80-sample sine windows, overlap-add.
void noise_blocks(SynthState& state, const Options& options, const SynthFrame& frame, float* out) {
float amp0[65], band0[65], amp1[65], band1[65], log0[65], log1[65];
noise_bins(options, state.previous, amp0, band0);
noise_bins(options, frame, amp1, band1);
bool none = true;
for (int k = 0; k < 65; k++) {
if (band0[k] == 0.0f && band1[k] == 0.0f) continue;
float x = amp0[k], y = amp1[k];
if (x == 0.0f) { amp0[k] = y; x = y; }
if (y == 0.0f) { amp1[k] = x; y = x; }
log0[k] = x > 0.0f ? fast_log2(x) : 0.0f;
log1[k] = y > 0.0f ? fast_log2(y) : 0.0f;
none = false;
}
if (none) return;
float white_re[128], white_im[128], re[128], im[128];
const float* window = state.window80.data();
for (int block = 0; block < 4; block++) {
float pos = static_cast<float>(static_cast<uint32_t>(40 * block + 40));
float x = (pos - options.voicing_off_start) / (options.voicing_off_end - options.voicing_off_start);
float y = (pos - options.voicing_on_start) / (options.voicing_on_end - options.voicing_on_start);
float fall = clamp01(x);
float rise = clamp01(y);
if (options.noise_white) {
for (int n = 0; n < 128; n++) white_im[n] = 0.0f;
for (int n = 0; n < 128; n++) {
float v = 0.0f;
if (n < 80) {
state.random = next_random(state.random);
v = static_cast<float>(state.random >> 8) * (1.0f / 8388608.0f);
v = v + -1.0f;
v = window[n] * v;
}
white_re[n] = v;
}
fft128(white_re, white_im);
}
float frame_pos = pos * kOneOver160;
for (int n = 0; n < 128; n++) { re[n] = 0.0f; im[n] = 0.0f; }
for (int k = 0; k < 63; k++) {
state.random = next_random(state.random);
uint32_t ph = state.random >> 19;
if (static_cast<int64_t>(k + 1) < static_cast<int64_t>(options.noise_coherent_bins)) {
float jitter = static_cast<float>(static_cast<int32_t>(ph - 4096));
jitter = options.noise_phase_jitter * jitter;
int32_t ji = to_int32_sat(jitter);
uint32_t v = static_cast<uint32_t>(0xa00 * (k + 1)) + state.noise_phase[k];
v = v + static_cast<uint32_t>(ji);
ph = v & 0x1fff;
state.noise_phase[k] = ph;
}
float bp = band0[k + 1];
if (bp == 0.0f && band1[k + 1] == 0.0f) continue;
float x0 = amp0[k + 1], x1 = amp1[k + 1], m;
if (x0 > 0.0f && x1 > 0.0f && options.amplitude_log_interp) {
float d = log1[k + 1] - log0[k + 1];
d = frame_pos * d;
m = fast_exp2(log0[k + 1] + d);
} else {
float d = x1 - x0;
d = frame_pos * d;
m = x0 + d;
}
float bc = band1[k + 1];
float d = bc - bp;
float g = bc > bp ? fall : rise;
g = d * g;
g = bp + g;
g = m * g;
if (g == 0.0f) continue;
float vr, vi;
if (options.noise_white) {
vr = g * white_re[k + 1];
vr = vr * kWhiteNoiseScale;
vi = g * white_im[k + 1];
vi = vi * kWhiteNoiseScale;
} else {
vr = g * kCos[ph];
vi = g * kCos[(ph + 0x1800) & 0x1fff];
}
re[k + 1] = vr;
im[k + 1] = vi;
re[127 - k] = vr;
im[127 - k] = -vi;
}
for (int n = 0; n < 128; n++) im[n] = -im[n];
fft128(re, im);
for (int n = 0; n < 80; n++) {
float w = 1.0f;
if (!options.noise_white) w = window[n];
float v = re[n] * (1.0f / 128.0f);
v = v * w;
int idx = 40 * block + n;
if (idx <= 159) out[idx] = v + out[idx];
else state.overlap[idx - 160] = v + state.overlap[idx - 160];
}
}
}
// noise_method 0: one 512-point FFT of random-phase noise, 320-sample window, added at noise_offset.
void noise_single(SynthState& state, const Options& options, const SynthFrame& frame, float* out) {
float bins[257], noise[320], re[512], im[512];
float gain = options.unvoiced_gain;
gain = gain * fast_sqrt(128.0f / frame.fundamental);
int64_t offset = options.noise_offset; // 0..160 (set_option clamps)
spectrum_bins(frame, frame.unvoiced.data(), options.unvoiced_low_cut, options.unvoiced_top_extend, bins);
bool any = false;
for (int k = 0; k < 257; k++) if (bins[k] != 0.0f) { any = true; break; }
if (!any) {
for (int n = 0; n < 320; n++) noise[n] = 0.0f;
} else {
uint32_t r = state.random;
for (int k = 0; k < 257; k++) {
r = next_random(r);
if ((k & 0xff) == 0) { re[k] = 0.0f; im[k] = 0.0f; continue; }
float a = gain * bins[k];
float c = a * kCos[r >> 19];
re[k] = c;
float s = kCos[((r >> 19) + 0x1800) & 0x1fff];
im[k] = a * s;
re[512 - k] = c;
im[512 - k] = -(a * s);
}
state.random = r;
ifft512(re, im);
for (int n = 0; n < 320; n++) noise[n] = re[n] * state.window320[n];
}
for (int64_t j = 0; j < 320; j++) {
int64_t idx = offset + j;
if (idx <= 159) out[idx] = out[idx] + noise[j];
else state.overlap[idx - 160] = state.overlap[idx - 160] + noise[j];
}
}
// Pulses: a 512-sample pulse shape from the pulse weights (zero or minimum phase), windowed, added at the frame's
// pulse positions.
void add_pulses(SynthState& state, const Options& options, const SynthFrame& frame, float* out) {
float weights[257], re[512], im[512], pulse[512];
float gain = options.pulse_gain;
gain = gain * fast_sqrt(40.0f / frame.fundamental);
spectrum_bins(frame, frame.pulse.data(), 0, options.unvoiced_top_extend, weights);
for (uint32_t k = 0; k < 257; k++) {
float f = static_cast<float>(k) * 15.625f;
if (!(f < options.pulse_highpass_hz)) break;
weights[k] = 0.0f;
}
if (options.pulse_minimum_phase) {
// cepstrum of the log magnitude, folded to make it causal, back to a spectrum, exponentiated
for (int k = 0; k < 257; k++) {
float x = core::max_num(gain * weights[k], kLogFloor);
x = fast_log2(x) * kLn2;
re[k] = x; im[k] = 0.0f;
if (k & 0xff) { re[512 - k] = x; im[512 - k] = 0.0f; }
}
ifft512(re, im);
for (int k = 1; k < 256; k++) {
re[k] = re[k] + re[k];
re[512 - k] = 0.0f;
}
for (int k = 0; k < 512; k++) im[k] = 0.0f;
core::fft512(re, im);
for (int k = 0; k < 512; k++) {
float m = fast_exp2(re[k] * kLog2E);
float p = im[k] * kOneOverTwoPi;
re[k] = m * cosine_interpolated(p);
p = p + -0.25f;
im[k] = m * cosine_interpolated(p);
}
ifft512(re, im);
for (int n = 0; n < 256; n++) pulse[n] = 0.0f;
for (int n = 0; n < 256; n++) pulse[256 + n] = re[n];
} else {
for (int k = 0; k < 257; k++) {
float x = gain * weights[k];
if (k == 256) x = 0.0f;
re[k] = x; im[k] = 0.0f;
if (k & 0xff) { re[512 - k] = x; im[512 - k] = 0.0f; }
}
ifft512(re, im);
for (int n = 0; n < 512; n++) pulse[n] = re[n ^ 0x100]; // centred on sample 256
}
float length = options.pulse_length;
for (int n = 0; n < 512; n++) {
float d = static_cast<float>(n - 256);
d = n < 256 ? -d : d;
float v = 0.0f;
if (!(d > length)) {
v = pulse[n];
float x = d * 0.5f;
if (d != 0.0f) x = x / length; // the centre is 0 for any length, including pulse_length 0
x = cosine_interpolated(x);
x = x * 0.5f;
x = x + 0.5f;
v = v * x;
}
pulse[n] = v;
}
// sample j of the pulse lands at position + shift + j - 256: in this frame below 160, then the overlap buffer
for (int i = 0; i < frame.pulse_count; i++) {
int64_t pos = static_cast<int32_t>(static_cast<uint32_t>(options.pulse_shift) +
static_cast<uint32_t>(frame.pulse_position[i]));
float pulse_gain = frame.pulse_gain[i];
for (int j = 0; j < 512; j++, pos++) {
float v = pulse[j];
if (v == 0.0f || pos < 256) continue;
if (pos < 256 + kFrameSamples) out[pos - 256] = pulse_gain * v + out[pos - 256];
else if (pos < 256 + 3 * kFrameSamples)
state.overlap[pos - 416] = pulse_gain * v + state.overlap[pos - 416];
}
}
}
} // namespace
float cosine_interpolated(float x) {
float s = x * 8192.0f;
int64_t i = to_fixed64(x, 13) - (s < 0.0f);
float fraction = s - static_cast<float>(i);
float t0 = kCos[static_cast<uint64_t>(i) & 0x1fff];
float t1 = kCos[(static_cast<uint32_t>(i) + 1u) & 0x1fff];
return t0 + fraction * (t1 - t0);
}
void synth_reset(SynthState& state, const Options& options) {
state = SynthState{};
uint32_t r = static_cast<uint32_t>(options.noise_seed) + kSeedOffset;
state.random = r;
for (uint32_t i = 0; i < 320; i++) {
float x = static_cast<float>(i) + 0.5f;
x = x / 640.0f;
x = x + -0.25f;
state.window320[i] = cosine_interpolated(x);
}
for (uint32_t i = 0; i < 80; i++) {
float x = static_cast<float>(i) + 0.5f;
x = x / 160.0f;
x = x + -0.25f;
state.window80[i] = cosine_interpolated(x);
}
uint32_t q = 0;
for (uint64_t j = 0; j < kHarmonicSlots; j++) {
uint64_t v;
if (options.initial_phase == 2) {
v = static_cast<uint64_t>(q * static_cast<uint32_t>(j)) << 32;
} else if (options.initial_phase == 1) {
r = next_random(r);
state.random = r;
v = static_cast<uint64_t>(r) << 32;
} else {
v = 0;
}
state.harmonic_phase[j] = v;
q += 0x9000000u;
}
}
void synth_comfort_noise(SynthState& state, float level, float* out) {
uint32_t r = state.random;
for (int i = 0; i < kFrameSamples; i++) {
r = next_random(r);
float u = static_cast<float>(r >> 8) * (1.0f / 8388608.0f);
u = u + -1.0f;
out[i] = level * u * kSqrt3;
}
state.random = r;
}
void synth_frame(SynthState& state, const Options& options, const SynthFrame& frame, float* out) {
for (int n = 0; n < kFrameSamples; n++) out[n] = state.overlap[n];
for (int n = 0; n < kFrameSamples; n++) {
state.overlap[n] = state.overlap[kFrameSamples + n];
state.overlap[kFrameSamples + n] = 0.0f;
}
if (frame.active == 0 && state.previous.active == 0) {
state.previous = frame;
return;
}
int32_t lc = frame.harmonic_count, lp = state.previous.harmonic_count;
int count = lc > lp ? lc : lp;
const SynthFrame& pitch_from = state.previous.active == 0 ? frame : state.previous;
uint64_t previous_increment = phase_increment(pitch_from.fundamental);
if (options.phase_tracking == 1) harmonics_tracked(state, options, frame, out, count, previous_increment);
else if (count >= 1) harmonics_accumulated(state, options, frame, out, count, previous_increment);
if (options.noise_method == 1) noise_blocks(state, options, frame, out);
if (frame.active != 0) {
if (options.noise_method == 0) noise_single(state, options, frame, out);
if (frame.pulse_count >= 1) add_pulses(state, options, frame, out);
}
for (int n = 0; n < kFrameSamples; n++) {
if (kFlushBelow > std::fabs(out[n])) out[n] = 0.0f;
}
state.previous = frame;
}
} // namespace halfrate::dec
+52
View File
@@ -0,0 +1,52 @@
// The synthesiser: harmonic oscillators, noise from inverse FFTs, pulses, and a 160-sample overlap-add.
// Each frame interpolates from the previous frame's parameters to the current one's.
#pragma once
#include <array>
#include <cstdint>
#include "decoder_options.h"
namespace halfrate::dec {
constexpr int kFrameSamples = 160;
constexpr int kHarmonicSlots = 58; // per-harmonic arrays, indexed by harmonic number 1..L (L <= 56)
// What the synthesiser makes of one frame.
struct SynthFrame {
int32_t active = 0; // nonzero: the frame has sound
float fundamental = 0.0f; // cycles per sample
int32_t harmonic_count = 0; // L
std::array<float, kHarmonicSlots> amplitude{}; // linear
std::array<float, kHarmonicSlots> voiced{}; // weight of the harmonic's oscillator
std::array<float, kHarmonicSlots> unvoiced{}; // weight of noise around the harmonic
std::array<float, kHarmonicSlots> pulse{}; // weight of the harmonic in the pulse shape
std::array<float, kHarmonicSlots> phase{}; // phase offset, cycles
int32_t pulse_count = 0; // 0..2
std::array<int32_t, 2> pulse_position{};
std::array<float, 2> pulse_gain{};
};
struct SynthState {
SynthFrame previous;
std::array<uint64_t, kHarmonicSlots> harmonic_phase{}; // per-harmonic oscillators, 2^64 = one cycle
uint64_t fundamental_phase = 0; // phase_tracking 1: the fundamental's phase
std::array<uint32_t, 63> noise_phase{}; // drifting phases of the low noise bins (13 bits)
std::array<float, 2 * kFrameSamples> overlap{}; // the next frame's first 160 samples, and the rest
uint32_t random = 0; // LCG state
std::array<float, 320> window320{}; // sine windows
std::array<float, 80> window80{};
};
// Clears the state, seeds the noise generator and sets the initial oscillator phases.
void synth_reset(SynthState& state, const Options& options);
// One frame: out[0..159].
void synth_frame(SynthState& state, const Options& options, const SynthFrame& frame, float* out);
// 160 samples of uniform noise of rms level.
void synth_comfort_noise(SynthState& state, float level, float* out);
// cos(2 pi x), x in cycles, linearly interpolated in the cosine table.
float cosine_interpolated(float x);
} // namespace halfrate::dec
+36
View File
@@ -0,0 +1,36 @@
#include "decoder_tables.h"
namespace halfrate::dec::tables {
const char kHarmonicClass[32][17] = {
"VVVVVVVVVVVVVVVV", "VVVVVVVVVVVVVVMM", "VVVVVVVVVVVVVVUU", "VVVVVVVVVVMVVVVV",
"VVVVVVVVVVVVUUUU", "VVVVUVVVVVVVVVVV", "VVVVVVUVVVVVVVVV", "VVVVVVVVVVUVVVVV",
"VVVVVVVVUUUUUUUU", "VVVVVVVVVVUUUUUU", "VVVVVVUUUUUUUUUU", "VVVVVVUUUUUUUMMM",
"VVVVUUUUUUUUUUUU", "VVVVMMUUUUUUUUUU", "VVUUUUUUUUUUUUUU", "MMMMMMUUUUUUUUUU",
"UUUUUUUUUUUUUUUU", "MMUUUUUUUUUUUUUU", "QQUUUUUUUUUUUUUU", "QQQQUUUUUUUUUUUU",
"QQQQQQQQQQQQQQQQ", "UUUQQQUUUUUUUUUU", "UUUUUUUUUUUUUQQQ", "UUUUUUUUUQQQQQQQ",
"PPPPPPPPPPPPPPPP", "UUUPPPPPPPPPPPPP", "QQUUUUUUUUUUUUUU", "UUUQQQUUUUUUUUUU",
"QQQQQQQQQQQQQQQQ", "QQQQUUUUUUUUUUUU", "UUUUUUUUUUUUUQQQ", "UUUUUUUUUUUQQQQQ",
};
const float kPhaseTapWeight[33] = {
0x0p+0f, 0x1p+0f, 0x1p-1f, 0x1.555556p-2f, 0x1p-2f, 0x1.99999ap-3f, 0x1.555556p-3f, 0x1.24924ap-3f,
0x1p-3f, 0x1.c71c72p-4f, 0x1.99999ap-4f, 0x1.745d18p-4f, 0x1.555556p-4f, 0x1.3b13b2p-4f, 0x1.24924ap-4f,
0x1.111112p-4f, 0x1p-4f, 0x1.e1e1e2p-5f, 0x1.c71c72p-5f, 0x1.af286cp-5f, 0x1.99999ap-5f, 0x1.861862p-5f,
0x1.745d18p-5f, 0x1.642c86p-5f, 0x1.555556p-5f, 0x1.47ae14p-5f, 0x1.3b13b2p-5f, 0x1.2f684cp-5f,
0x1.24924ap-5f, 0x1.1a7b96p-5f, 0x1.111112p-5f, 0x1.08421p-5f, 0x1p-5f,
};
const float kKeypadLow[4] = {697.0f, 770.0f, 852.0f, 941.0f};
const float kKeypadHigh[4] = {1209.0f, 1336.0f, 1477.0f, 1633.0f};
const int32_t kKeypadLowIndex[16] = {3, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 1, 2, 3, 3, 3};
const int32_t kKeypadHighIndex[16] = {1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 3, 3, 3, 3, 0, 2};
const float kToneSetLow[4] = {606.0f, 672.0f, 743.0f, 820.0f};
const float kToneSetHigh[4] = {1052.0f, 1162.0f, 1297.0f, 1430.0f};
const int8_t kToneSetIndex[16][2] = {
{3, 1}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0},
{2, 1}, {2, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {3, 0}, {3, 2},
};
} // namespace halfrate::dec::tables
+32
View File
@@ -0,0 +1,32 @@
// Constant tables of the decoder only.
#pragma once
#include <cstdint>
namespace halfrate::dec::tables {
// How a harmonic is synthesised, per voicing index and frequency slot (16 slots of 1/32 cycle per sample).
enum HarmonicClass : char {
kVoiced = 'V',
kUnvoiced = 'U',
kMixed = 'M', // voiced and noise, shared by mixed_voiced_share
kPulse = 'P', // a pulse (noise when pulses are off)
kPulseNoise = 'Q', // a pulse and noise
};
// kHarmonicClass[voicing index][slot]; silence frames use row kSilenceClassRow.
extern const char kHarmonicClass[32][17];
constexpr int kSilenceClassRow = 16;
// Weights of the phase term's taps: [k] = 1/k for k = 1..32 ([0] unused).
extern const float kPhaseTapWeight[33];
// Dual tones, Hz. Tone indices 0x80..0x8f: low kKeypadLow[kKeypadLowIndex[i]], high kKeypadHigh[kKeypadHighIndex[i]].
extern const float kKeypadLow[4];
extern const float kKeypadHigh[4];
extern const int32_t kKeypadLowIndex[16];
extern const int32_t kKeypadHighIndex[16];
// Tone indices 0x90..0x9f: kToneSetLow[kToneSetIndex[i][0]] and kToneSetHigh[kToneSetIndex[i][1]].
extern const float kToneSetLow[4];
extern const float kToneSetHigh[4];
extern const int8_t kToneSetIndex[16][2];
} // namespace halfrate::dec::tables
+167
View File
@@ -0,0 +1,167 @@
#include "dequant.h"
#include <cstring>
#include "numeric.h"
#include "tables.h"
namespace halfrate::core {
namespace {
constexpr float kHalfSqrtHalf = 0x1.6a09e6p-2f; // 1 / (2 sqrt 2)
constexpr float kTwoPi = 0x1.921fb6p+2f;
constexpr int kVoicePitchCount = 120;
constexpr int kTonePitchEnd = 126;
constexpr int kErasurePitchEnd = 124;
constexpr int kToneHarmonicCount = 14;
constexpr float kToneFundamental = 0.03125f;
} // namespace
void dequant_reset(DequantState& state) { state = DequantState{}; }
void dequant_shape_residual(const Codebook& codebook, int harmonic_count, const FrameFields& fields, float* out) {
const float* low = codebook.shape_low[fields[kFieldShapeLow]];
const float* high = codebook.shape_high[fields[kFieldShapeHigh]];
const float coefficients[8] = {0.0f, low[0], low[1], low[2], high[0], high[1], high[2], high[3]};
float pairs[8];
const auto& dct8 = tables::kInverseDct[7];
for (int r = 0; r < 8; r++) {
float s = 0.0f;
for (int c = 0; c < 8; c++) s = s + dct8[r][c] * coefficients[c];
pairs[r] = s;
}
const int32_t* sizes = codebook.band_sizes[harmonic_count];
int pos = 0;
for (int block = 0; block < 4; block++) {
int n = sizes[block];
float block_coefficients[18];
std::memset(block_coefficients + 2, 0, 16 * sizeof(float));
float a = pairs[2 * block], b = pairs[2 * block + 1];
block_coefficients[0] = (a + b) * 0.5f;
block_coefficients[1] = (a - b) * kHalfSqrtHalf;
if (n >= 3) {
int count = n < 6 ? n : 6;
int row = codebook.detail_stride[block] * fields[kFieldDetail1 + block];
std::memcpy(block_coefficients + 2, codebook.detail[block] + row * 4,
static_cast<size_t>(count - 2) * sizeof(float));
} else if (n < 1) {
continue;
}
const auto& dct = tables::kInverseDct[n - 1];
for (int i = 0; i < n; i++) {
float s = 0.0f;
for (int c = 0; c < n; c++) s = s + dct[i][c] * block_coefficients[c];
out[pos++] = s;
}
}
}
void dequant_predict(const Codebook& codebook, const DequantState& state, int harmonic_count, float* out) {
int previous_count = state.harmonic_count;
std::array<float, kMaxHarmonics + 2> previous = state.log2_amplitude;
if (harmonic_count > previous_count) {
float last = previous[previous_count];
for (int i = previous_count + 1; i <= harmonic_count; i++) previous[i] = last;
}
previous[0] = previous[1];
if (harmonic_count < 1) return;
float ratio = static_cast<float>(previous_count) / static_cast<float>(static_cast<unsigned>(harmonic_count));
float rho = codebook.prediction;
for (int k = 1; k <= harmonic_count; k++) {
float x = ratio * static_cast<float>(static_cast<unsigned>(k));
int i = to_int32_sat(x);
float frac = x - static_cast<float>(to_int32_sat(x));
int j = i < kMaxHarmonics ? i : kMaxHarmonics;
float a = (1.0f - frac) * rho;
a = a * previous[i];
float b = frac * rho;
b = b * previous[j + 1];
out[k - 1] = a + b;
}
}
FrameKind dequantize(const Codebook& codebook, DequantState& state, const FrameFields& fields, FrameParams* out) {
int pitch = fields[kFieldPitch];
bool has_pitch = pitch < kVoicePitchCount ? true : pitch < codebook.pitch_count;
FrameKind kind;
int count = 0;
float fundamental = 0.0f;
if (has_pitch) {
kind = FrameKind::voice;
fundamental = codebook.fundamental[pitch];
count = codebook.harmonic_count[pitch];
} else if (static_cast<unsigned>(pitch) < kErasurePitchEnd) {
kind = FrameKind::erasure;
} else if (static_cast<unsigned>(pitch) < kTonePitchEnd) {
kind = FrameKind::tone;
count = kToneHarmonicCount;
fundamental = kToneFundamental;
} else {
kind = FrameKind::reserved;
}
if (kind == FrameKind::erasure || kind == FrameKind::reserved) {
dequant_reset(state);
if (out) {
*out = FrameParams{};
out->kind = kind;
out->fields = fields;
}
return kind;
}
float new_gain = codebook.gain_weight * codebook.gain[fields[kFieldGain]];
float carried_gain = codebook.previous_gain_weight * state.log2_gain;
float residual[60], prediction[58];
dequant_shape_residual(codebook, count, fields, residual);
dequant_predict(codebook, state, count, prediction);
float residual_sum = 0.0f, prediction_sum = 0.0f;
for (int i = 0; i < count; i++) {
residual_sum = residual_sum + residual[i];
prediction_sum = prediction_sum + prediction[i];
}
float half_log2_count = tables::kHalfLog2Count[count];
float reciprocal_count = tables::kReciprocalCount[count];
float gain = new_gain + carried_gain;
state.log2_amplitude.fill(0.0f);
if (count >= 1) {
float offset = gain - half_log2_count;
float residual_mean = residual_sum * reciprocal_count;
offset = offset - residual_mean;
float prediction_mean = prediction_sum * reciprocal_count;
for (int k = 0; k < count; k++) {
float v = residual[k] + prediction[k];
v = v - prediction_mean;
v = offset + v;
state.log2_amplitude[k + 1] = v;
}
}
state.harmonic_count = count;
state.log2_gain = gain;
if (!out) return kind;
out->kind = kind;
out->fundamental = fundamental;
out->omega = fundamental * kTwoPi;
out->harmonic_count = count;
out->log2_gain = gain;
out->fields = fields;
out->harmonic_voiced.fill(0);
out->log2_amplitude.fill(0.0f);
const int32_t* bands = codebook.voicing[fields[kFieldVoicing]];
for (int i = 0; i < count && i <= kMaxHarmonics; i++) {
int voiced = 0;
if (has_pitch) {
int band = to_int32_sat(fundamental * (static_cast<float>(static_cast<unsigned>(i + 1)) * 16.0f));
voiced = bands[band < 7 ? band : 7];
}
out->harmonic_voiced[i] = static_cast<uint8_t>(voiced);
out->log2_amplitude[i] = state.log2_amplitude[i + 1];
}
return kind;
}
FrameKind dequantize(DequantState& state, const FrameFields& fields, FrameParams* out) {
return dequantize(tables::kCodebook, state, fields, out);
}
} // namespace halfrate::core
+73
View File
@@ -0,0 +1,73 @@
// The parameter dequantiser: turns the nine frame fields into the frame parameters (pitch, harmonic count,
// voicing, log2 harmonic amplitudes, gain), predicting the spectral shape from the previous frame.
//
// The log2 amplitudes of harmonics 1..L are
// gain - log2(L)/2 + residual[l] + prediction[l] - mean(residual) - mean(prediction)
// where residual is the transformed shape codebook vector (dequant_shape_residual) and prediction is the previous
// frame's amplitudes resampled to L harmonics and scaled (dequant_predict).
//
// Both the decoder and the encoder (which runs the dequantiser to track what the decoder will predict) keep one
// DequantState per stream.
#pragma once
#include <array>
#include <cstdint>
#include "codebook.h"
#include "frame.h"
namespace halfrate::core {
constexpr int kMaxHarmonics = 56; // largest harmonic count L of any pitch
constexpr int kResetHarmonicCount = 30; // previous harmonic count after a reset or erasure
// What the dequantiser decided a frame is (dequantize's result and FrameParams::kind).
enum class FrameKind : int32_t {
voice = 0, // pitch index 0..119
tone = 1, // pitch index 124..125: fixed fundamental 1/32, 14 harmonics, all unvoiced
erasure = 2, // pitch index 120..123: the parameters are cleared and the predictor reset
reserved = 3, // pitch index 126..127: treated as an erasure
};
// Predictor state: the previous frame's parameters.
struct DequantState {
int32_t harmonic_count = kResetHarmonicCount; // L of the previous frame
float log2_gain = 0.0f; // its gain
// [l] for l = 1..L: its log2 harmonic amplitudes. [0] and the entries above L are 0.
std::array<float, kMaxHarmonics + 2> log2_amplitude{};
};
// The decoded frame parameters.
struct FrameParams {
FrameKind kind = FrameKind::voice;
float fundamental = 0.0f; // cycles per sample
float omega = 0.0f; // fundamental * 2 pi, radians per sample
int32_t harmonic_count = 0; // L
// [k] for harmonic k + 1, k < L: voiced (1) or unvoiced (0), from the voicing band the harmonic falls in.
// Entries from L on are 0.
std::array<uint8_t, kMaxHarmonics + 1> harmonic_voiced{};
// [k] for harmonic k + 1, k < L: log2 amplitude. Entries from L on are 0.
std::array<float, kMaxHarmonics + 1> log2_amplitude{};
float log2_gain = 0.0f;
FrameFields fields{}; // the fields the frame was decoded from, as given
};
// Resets the predictor (as at the start of a stream).
void dequant_reset(DequantState& state);
// The shape residual of L harmonics from the shape fields (3..8): the 8-point coefficient vector (0, shape_low,
// shape_high) through the inverse 8-point DCT, then for each of the 4 blocks a pair sum/difference plus the
// block's higher-order coefficients through the inverse DCT of the block size. out[0..L-1].
void dequant_shape_residual(const Codebook& codebook, int harmonic_count, const FrameFields& fields, float* out);
// The prediction: the state's amplitudes resampled from its harmonic count to L, times codebook.prediction.
// out[0..L-1].
void dequant_predict(const Codebook& codebook, const DequantState& state, int harmonic_count, float* out);
// Decodes one frame: updates the state and, if out is not null, fills *out (every field). Every field index
// must be within its bit width (frame.h); any such set is valid.
FrameKind dequantize(const Codebook& codebook, DequantState& state, const FrameFields& fields, FrameParams* out);
// The same with the codec's codebook (tables::kCodebook).
FrameKind dequantize(DequantState& state, const FrameFields& fields, FrameParams* out);
} // namespace halfrate::core
+122
View File
@@ -0,0 +1,122 @@
// The encoder's analysis tables: the two analysis windows and the voicing decision tables.
// Generated data; do not edit.
#include "enc_tables.h"
namespace halfrate::tables {
// Analysis window of the pitch spectrum, 256 points.
const float kLongWindow[256] = {
0x1.306b9ep-11f, 0x1.30f3a4p-11f, 0x1.328bap-11f, 0x1.353352p-11f, 0x1.38ea52p-11f, 0x1.3db00ap-11f,
0x1.4383bep-11f, 0x1.4a6486p-11f, 0x1.52514ep-11f, 0x1.5b48dep-11f, 0x1.6549dp-11f, 0x1.705296p-11f,
0x1.7c6178p-11f, 0x1.897498p-11f, 0x1.9789ecp-11f, 0x1.a69f46p-11f, 0x1.b6b24ap-11f, 0x1.c7c07cp-11f,
0x1.d9c734p-11f, 0x1.ecc3a6p-11f, 0x1.00596ep-10f, 0x1.0ac8ep-10f, 0x1.15ae88p-10f, 0x1.2108b8p-10f,
0x1.2cd5a8p-10f, 0x1.391384p-10f, 0x1.45c066p-10f, 0x1.52da54p-10f, 0x1.605f48p-10f, 0x1.6e4d24p-10f,
0x1.7ca1cp-10f, 0x1.8b5ae2p-10f, 0x1.9a763ep-10f, 0x1.a9f17ep-10f, 0x1.b9ca38p-10f, 0x1.c9fdf8p-10f,
0x1.da8a36p-10f, 0x1.eb6c62p-10f, 0x1.fca1dap-10f, 0x1.0713fap-9f, 0x1.0ffdfap-9f, 0x1.190d8cp-9f,
0x1.224146p-9f, 0x1.2b97bcp-9f, 0x1.350f78p-9f, 0x1.3ea702p-9f, 0x1.485cdcp-9f, 0x1.522f86p-9f,
0x1.5c1d76p-9f, 0x1.662524p-9f, 0x1.7044fep-9f, 0x1.7a7b74p-9f, 0x1.84c6ecp-9f, 0x1.8f25dp-9f,
0x1.999682p-9f, 0x1.a41764p-9f, 0x1.aea6dp-9f, 0x1.b94326p-9f, 0x1.c3eabep-9f, 0x1.ce9bf2p-9f,
0x1.d95516p-9f, 0x1.e4148p-9f, 0x1.eed886p-9f, 0x1.f99f7ap-9f, 0x1.0233d8p-8f, 0x1.0797bcp-8f,
0x1.0cfa96p-8f, 0x1.125b8cp-8f, 0x1.17b9cap-8f, 0x1.1d147ap-8f, 0x1.226ac8p-8f, 0x1.27bbdep-8f,
0x1.2d06eap-8f, 0x1.324b18p-8f, 0x1.378796p-8f, 0x1.3cbb96p-8f, 0x1.41e646p-8f, 0x1.4706dcp-8f,
0x1.4c1c88p-8f, 0x1.512684p-8f, 0x1.562404p-8f, 0x1.5b1442p-8f, 0x1.5ff67cp-8f, 0x1.64c9eep-8f,
0x1.698dd6p-8f, 0x1.6e417ap-8f, 0x1.72e41ep-8f, 0x1.777506p-8f, 0x1.7bf382p-8f, 0x1.805edcp-8f,
0x1.84b664p-8f, 0x1.88f96cp-8f, 0x1.8d274ep-8f, 0x1.913f62p-8f, 0x1.954106p-8f, 0x1.992b98p-8f,
0x1.9cfe8p-8f, 0x1.a0b922p-8f, 0x1.a45aecp-8f, 0x1.a7e34cp-8f, 0x1.ab51b8p-8f, 0x1.aea5a4p-8f,
0x1.b1de8ep-8f, 0x1.b4fbf6p-8f, 0x1.b7fd5ep-8f, 0x1.bae25p-8f, 0x1.bdaa58p-8f, 0x1.c0550ap-8f,
0x1.c2e1f8p-8f, 0x1.c550bep-8f, 0x1.c7a0fcp-8f, 0x1.c9d256p-8f, 0x1.cbe474p-8f, 0x1.cdd704p-8f,
0x1.cfa9b6p-8f, 0x1.d15c46p-8f, 0x1.d2ee6ep-8f, 0x1.d45ffp-8f, 0x1.d5b092p-8f, 0x1.d6e02p-8f,
0x1.d7ee6cp-8f, 0x1.d8db48p-8f, 0x1.d9a694p-8f, 0x1.da502ep-8f, 0x1.dad7fcp-8f, 0x1.db3deap-8f,
0x1.db81e6p-8f, 0x1.dba3e6p-8f, 0x1.dba3e6p-8f, 0x1.db81e6p-8f, 0x1.db3deap-8f, 0x1.dad7fcp-8f,
0x1.da502ep-8f, 0x1.d9a694p-8f, 0x1.d8db48p-8f, 0x1.d7ee6cp-8f, 0x1.d6e02p-8f, 0x1.d5b092p-8f,
0x1.d45ffp-8f, 0x1.d2ee6ep-8f, 0x1.d15c46p-8f, 0x1.cfa9b6p-8f, 0x1.cdd704p-8f, 0x1.cbe474p-8f,
0x1.c9d256p-8f, 0x1.c7a0fcp-8f, 0x1.c550bep-8f, 0x1.c2e1f8p-8f, 0x1.c0550ap-8f, 0x1.bdaa58p-8f,
0x1.bae25p-8f, 0x1.b7fd5ep-8f, 0x1.b4fbf6p-8f, 0x1.b1de8ep-8f, 0x1.aea5a4p-8f, 0x1.ab51b8p-8f,
0x1.a7e34cp-8f, 0x1.a45aecp-8f, 0x1.a0b922p-8f, 0x1.9cfe8p-8f, 0x1.992b98p-8f, 0x1.954106p-8f,
0x1.913f62p-8f, 0x1.8d274ep-8f, 0x1.88f96cp-8f, 0x1.84b664p-8f, 0x1.805edcp-8f, 0x1.7bf382p-8f,
0x1.777506p-8f, 0x1.72e41ep-8f, 0x1.6e417ap-8f, 0x1.698dd6p-8f, 0x1.64c9eep-8f, 0x1.5ff67cp-8f,
0x1.5b1442p-8f, 0x1.562404p-8f, 0x1.512684p-8f, 0x1.4c1c88p-8f, 0x1.4706dcp-8f, 0x1.41e646p-8f,
0x1.3cbb96p-8f, 0x1.378796p-8f, 0x1.324b18p-8f, 0x1.2d06eap-8f, 0x1.27bbdep-8f, 0x1.226ac8p-8f,
0x1.1d147ap-8f, 0x1.17b9cap-8f, 0x1.125b8cp-8f, 0x1.0cfa96p-8f, 0x1.0797bcp-8f, 0x1.0233d8p-8f,
0x1.f99f7ap-9f, 0x1.eed886p-9f, 0x1.e4148p-9f, 0x1.d95516p-9f, 0x1.ce9bf2p-9f, 0x1.c3eabep-9f,
0x1.b94326p-9f, 0x1.aea6dp-9f, 0x1.a41764p-9f, 0x1.999682p-9f, 0x1.8f25dp-9f, 0x1.84c6ecp-9f,
0x1.7a7b74p-9f, 0x1.7044fep-9f, 0x1.662524p-9f, 0x1.5c1d76p-9f, 0x1.522f86p-9f, 0x1.485cdcp-9f,
0x1.3ea702p-9f, 0x1.350f78p-9f, 0x1.2b97bcp-9f, 0x1.224146p-9f, 0x1.190d8cp-9f, 0x1.0ffdfap-9f,
0x1.0713fap-9f, 0x1.fca1dap-10f, 0x1.eb6c62p-10f, 0x1.da8a36p-10f, 0x1.c9fdf8p-10f, 0x1.b9ca38p-10f,
0x1.a9f17ep-10f, 0x1.9a763ep-10f, 0x1.8b5ae2p-10f, 0x1.7ca1cp-10f, 0x1.6e4d24p-10f, 0x1.605f48p-10f,
0x1.52da54p-10f, 0x1.45c066p-10f, 0x1.391384p-10f, 0x1.2cd5a8p-10f, 0x1.2108b8p-10f, 0x1.15ae88p-10f,
0x1.0ac8ep-10f, 0x1.00596ep-10f, 0x1.ecc3a6p-11f, 0x1.d9c734p-11f, 0x1.c7c07cp-11f, 0x1.b6b24ap-11f,
0x1.a69f46p-11f, 0x1.9789ecp-11f, 0x1.897498p-11f, 0x1.7c6178p-11f, 0x1.705296p-11f, 0x1.6549dp-11f,
0x1.5b48dep-11f, 0x1.52514ep-11f, 0x1.4a6486p-11f, 0x1.4383bep-11f, 0x1.3db00ap-11f, 0x1.38ea52p-11f,
0x1.353352p-11f, 0x1.328bap-11f, 0x1.30f3a4p-11f, 0x1.306b9ep-11f,
};
// Analysis window of the amplitude spectrum, 160 points.
const float kShortWindow[160] = {
0x1.e80cdep-11f, 0x1.ea3dbap-11f, 0x1.f0cf6ap-11f, 0x1.fbbf52p-11f, 0x1.058488p-10f, 0x1.0f5344p-10f,
0x1.1b47f2p-10f, 0x1.295dcap-10f, 0x1.398f2ap-10f, 0x1.4bd59ap-10f, 0x1.6029cep-10f, 0x1.7683a2p-10f,
0x1.8eda28p-10f, 0x1.a923a8p-10f, 0x1.c5559ep-10f, 0x1.e364c4p-10f, 0x1.01a28cp-9f, 0x1.1274eep-9f,
0x1.2422cep-9f, 0x1.36a51cp-9f, 0x1.49f47p-9f, 0x1.5e0916p-9f, 0x1.72db02p-9f, 0x1.8861e6p-9f,
0x1.9e9526p-9f, 0x1.b56bep-9f, 0x1.ccdcf6p-9f, 0x1.e4df08p-9f, 0x1.fd687cp-9f, 0x1.0b37c4p-8f,
0x1.17f512p-8f, 0x1.24e714p-8f, 0x1.32089ap-8f, 0x1.3f5466p-8f, 0x1.4cc528p-8f, 0x1.5a558p-8f,
0x1.680004p-8f, 0x1.75bf3ap-8f, 0x1.838da4p-8f, 0x1.9165cp-8f, 0x1.9f4202p-8f, 0x1.ad1ce2p-8f,
0x1.baf0d8p-8f, 0x1.c8b858p-8f, 0x1.d66de4p-8f, 0x1.e40bfep-8f, 0x1.f18d38p-8f, 0x1.feec2ap-8f,
0x1.0611bep-7f, 0x1.0c96f4p-7f, 0x1.13031ap-7f, 0x1.1953ap-7f, 0x1.1f86p-7f, 0x1.2597bep-7f,
0x1.2b866ep-7f, 0x1.314fb2p-7f, 0x1.36f13ap-7f, 0x1.3c68c4p-7f, 0x1.41b422p-7f, 0x1.46d136p-7f,
0x1.4bbdf4p-7f, 0x1.507864p-7f, 0x1.54fea4p-7f, 0x1.594ee4p-7f, 0x1.5d676ap-7f, 0x1.614692p-7f,
0x1.64ead4p-7f, 0x1.6852b6p-7f, 0x1.6b7cdep-7f, 0x1.6e680ap-7f, 0x1.71130cp-7f, 0x1.737cd6p-7f,
0x1.75a46ep-7f, 0x1.7788fap-7f, 0x1.7929b8p-7f, 0x1.7a86p-7f, 0x1.7b9d46p-7f, 0x1.7c6f1ep-7f,
0x1.7cfb32p-7f, 0x1.7d414ap-7f, 0x1.7d414ap-7f, 0x1.7cfb32p-7f, 0x1.7c6f1ep-7f, 0x1.7b9d46p-7f,
0x1.7a86p-7f, 0x1.7929b8p-7f, 0x1.7788fap-7f, 0x1.75a46ep-7f, 0x1.737cd6p-7f, 0x1.71130cp-7f,
0x1.6e680ap-7f, 0x1.6b7cdep-7f, 0x1.6852b6p-7f, 0x1.64ead4p-7f, 0x1.614692p-7f, 0x1.5d676ap-7f,
0x1.594ee4p-7f, 0x1.54fea4p-7f, 0x1.507864p-7f, 0x1.4bbdf4p-7f, 0x1.46d136p-7f, 0x1.41b422p-7f,
0x1.3c68c4p-7f, 0x1.36f13ap-7f, 0x1.314fb2p-7f, 0x1.2b866ep-7f, 0x1.2597bep-7f, 0x1.1f86p-7f,
0x1.1953ap-7f, 0x1.13031ap-7f, 0x1.0c96f4p-7f, 0x1.0611bep-7f, 0x1.feec2ap-8f, 0x1.f18d38p-8f,
0x1.e40bfep-8f, 0x1.d66de4p-8f, 0x1.c8b858p-8f, 0x1.baf0d8p-8f, 0x1.ad1ce2p-8f, 0x1.9f4202p-8f,
0x1.9165cp-8f, 0x1.838da4p-8f, 0x1.75bf3ap-8f, 0x1.680004p-8f, 0x1.5a558p-8f, 0x1.4cc528p-8f,
0x1.3f5466p-8f, 0x1.32089ap-8f, 0x1.24e714p-8f, 0x1.17f512p-8f, 0x1.0b37c4p-8f, 0x1.fd687cp-9f,
0x1.e4df08p-9f, 0x1.ccdcf6p-9f, 0x1.b56bep-9f, 0x1.9e9526p-9f, 0x1.8861e6p-9f, 0x1.72db02p-9f,
0x1.5e0916p-9f, 0x1.49f47p-9f, 0x1.36a51cp-9f, 0x1.2422cep-9f, 0x1.1274eep-9f, 0x1.01a28cp-9f,
0x1.e364c4p-10f, 0x1.c5559ep-10f, 0x1.a923a8p-10f, 0x1.8eda28p-10f, 0x1.7683a2p-10f, 0x1.6029cep-10f,
0x1.4bd59ap-10f, 0x1.398f2ap-10f, 0x1.295dcap-10f, 0x1.1b47f2p-10f, 0x1.0f5344p-10f, 0x1.058488p-10f,
0x1.fbbf52p-11f, 0x1.f0cf6ap-11f, 0x1.ea3dbap-11f, 0x1.e80cdep-11f,
};
// Pitch index where each row of the voicing threshold tables starts (rows 0..8).
const int32_t kVoicingRowStart[9] = {
0, 30, 50, 65, 80, 92, 104, 112, 116,
};
// Voicing threshold per row and band (with the voicing table option on).
const float kVoicingThresholdBase[9 * 8] = {
0x1.a66666p-1f, 0x1.d9999ap-1f, 0x1p+0f, 0x1.99999ap-1f, 0x1.cp-1f, 0x1.99999ap-1f, 0x1.8p-1f, 0x1.666666p-1f,
0x1.0cccccp+0f, 0x1.2cccccp+0f, 0x1.f33334p-1f, 0x1.733334p-1f, 0x1.8p-1f, 0x1.4cccccp-1f, 0x1.4cccccp-1f, 0x1.4p-1f,
0x1.ccccccp-1f, 0x1.f33334p-1f, 0x1.a66666p-1f, 0x1.4cccccp-1f, 0x1.59999ap-1f, 0x1.266666p-1f, 0x1.19999ap-1f, 0x1.0cccccp-1f,
0x1.4cccccp-1f, 0x1.59999ap-1f, 0x1.4cccccp-1f, 0x1p-1f, 0x1.19999ap-1f, 0x1.e66666p-2f, 0x1.e66666p-2f, 0x1.99999ap-2f,
0x1.19999ap-1f, 0x1.19999ap-1f, 0x1p-1f, 0x1.b33334p-2f, 0x1.b33334p-2f, 0x1.99999ap-2f, 0x1.8p-2f, 0x1.4cccccp-2f,
0x1.8p-2f, 0x1.99999ap-2f, 0x1.8p-2f, 0x1.19999ap-2f, 0x1.4cccccp-2f, 0x1.ccccccp-3f, 0x1.19999ap-2f, 0x1.666666p-3f,
0x1.19999ap-2f, 0x1.333334p-2f, 0x1.19999ap-2f, 0x1.ccccccp-3f, 0x1.ccccccp-3f, 0x1.99999ap-3f, 0x1.333334p-3f, 0x1p-3f,
0x1.ccccccp-3f, 0x1p-2f, 0x1.99999ap-3f, 0x1.333334p-3f, 0x1.666666p-3f, 0x1.666666p-3f, 0x1.99999ap-3f, 0x1.666666p-3f,
0x1.99999ap-4f, 0x1.99999ap-3f, 0x1.666666p-3f, 0x1.666666p-3f, 0x1.333334p-4f, 0x1.333334p-3f, 0x1.333334p-3f, 0x1p-3f,
};
// Voicing threshold slope on the pitch's fit cost, per row and band.
const float kVoicingThresholdSlope[9 * 8] = {
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.ccccccp-1f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.99999ap-1f, -0x1p+0f, -0x1p+0f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.99999ap-1f, -0x1.99999ap-1f, -0x1p+0f,
-0x1.ccccccp-1f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.ccccccp-1f, -0x1p+0f, -0x1.666666p-1f, -0x1.ccccccp-1f, -0x1.99999ap-1f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.99999ap-1f, -0x1.666666p-1f,
-0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.666666p-1f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1p+0f,
0.0f, -0x1p+0f, -0x1p+0f, -0x1p+0f, -0x1.99999ap-4f, -0x1p+0f, -0x1p+0f, -0x1p+0f,
};
// Upper edges (Hz) of the 18 bands of the candidate re-scoring histogram; entry 0 is 0.
const float kRescoreBandEdge[18] = {
0.0f, 0x1.9p+6f, 0x1.9p+7f, 0x1.2cp+8f, 0x1.9p+8f, 0x1.fep+8f, 0x1.3bp+9f, 0x1.81p+9f, 0x1.ccp+9f,
0x1.0ep+10f, 0x1.3d8p+10f, 0x1.72p+10f, 0x1.aep+10f, 0x1.f4p+10f, 0x1.22p+11f, 0x1.518p+11f, 0x1.89cp+11f, 0x1.ce8p+11f,
};
} // namespace halfrate::tables
+10884
View File
File diff suppressed because it is too large. Load diff
+29
View File
@@ -0,0 +1,29 @@
// Constant tables of the encoder (generated data, see the files named below). The pitch grid the encoder searches
// is the codebook's pitch table (tables.h: kPitchFundamental, kPitchHarmonicCount).
#pragma once
#include <cstdint>
namespace halfrate::tables {
// enc_pitch_net.cc: the pitch network (an int8 MLP 1830 -> 160 -> 96 -> 121). Weights are [output][input].
extern const float kPitchNetMean[305]; // feature normalisation, per feature of one frame
extern const float kPitchNetInvStd[305];
extern const int8_t kPitchNetWeights1[160 * 1830];
extern const float kPitchNetBias1[160];
extern const float kPitchNetScale1[160];
extern const int8_t kPitchNetWeights2[96 * 160];
extern const float kPitchNetBias2[96];
extern const float kPitchNetScale2[96];
extern const int8_t kPitchNetWeights3[121 * 96];
extern const float kPitchNetBias3[121];
extern const float kPitchNetScale3[121];
// enc_analysis.cc: analysis windows and voicing tables.
extern const float kLongWindow[256]; // window of the pitch spectrum
extern const float kShortWindow[160]; // window of the amplitude spectrum
extern const int32_t kVoicingRowStart[9]; // pitch index where each threshold row starts
extern const float kVoicingThresholdBase[9 * 8]; // [row][band]
extern const float kVoicingThresholdSlope[9 * 8]; // [row][band], times the pitch's fit cost
extern const float kRescoreBandEdge[18]; // Hz; band j is [edge[j], edge[j + 1]), the last one open
} // namespace halfrate::tables
+880
View File
@@ -0,0 +1,880 @@
#include "encoder.h"
#include <cmath>
#include <cstring>
#include "enhance.h"
#include "fastmath.h"
#include "numeric.h"
#include "constants.h"
#include "enc_tables.h"
#include "tables.h"
namespace halfrate::enc {
namespace {
using core::fast_exp2;
using core::fast_log2;
using core::to_int32_sat;
constexpr int kAllUnvoiced = 16; // voicing index with no band voiced (indices 0..15 voice at least one)
constexpr int kTonePitch = 124; // pitch field of a silent frame: the fixed-pitch frame of 14 harmonics
constexpr int kToneHarmonics = 14;
constexpr int kAnalysisCenter = 944; // window centre for an offset option of 0
constexpr int kNewSamples = kHistorySamples - kFrameSamples; // first sample of the newest frame
constexpr int kYinMaxLag = 126;
constexpr int kRescoreBands = 18;
// Options that place the analysis windows are clamped so that every read stays inside the history block.
constexpr int kPitchOffsetLow = -781, kPitchOffsetHigh = 3372;
constexpr int kAmplitudeOffsetLow = -864, kAmplitudeOffsetHigh = 3455;
const core::Codebook& codebook() { return tables::kCodebook; }
inline bool is_voiced(int voicing) { return voicing < kAllUnvoiced; }
inline int clamp_int(int v, int low, int high) { return v < low ? low : (v > high ? high : v); }
// The voicing field of silent frames, clamped to the field's 5 bits.
int silent_voicing_index(const EncoderOptions& o) { return clamp_int(to_int32_sat(o.silent_voicing_index), 0, 31); }
float fundamental_of(int pitch) { return tables::kPitchFundamental[pitch]; }
int harmonics_of(int pitch) { return tables::kPitchHarmonicCount[pitch]; }
// The noise floor rises by floor_rise of the level's excess plus a fixed creep per frame and follows the level
// down. The gate is open while the level is above gate_level_db and gate_above_floor_db over the floor, and stays
// open gate_hang_frames frames after. Returns true when the gate is closed (the frame is silent).
bool update_gate(Encoder& e, float level) {
const EncoderOptions& o = e.options;
float floor = e.noise_floor_db;
float up = level - floor;
up = up < 0.0f ? 0.0f : up;
floor = floor + o.floor_rise * up;
floor = floor + kFloorCreep;
floor = level < floor ? level : floor;
e.noise_floor_db = floor;
if (level > o.gate_level_db && level > floor + o.gate_above_floor_db) {
e.gate_hang = to_int32_sat(o.gate_hang_frames);
return false;
}
int h = e.gate_hang;
if (h < 1) return true;
e.gate_hang = h - 1;
return false;
}
// Starts the noise floor at the lowest level of the first three frames.
void init_floor(Encoder& e) {
float s0 = e.history.slots[0].level_db;
e.noise_floor_db = s0;
for (int j = 1; j < 3; j++) {
float s1 = e.history.slots[j].level_db;
if (s1 < s0) {
e.noise_floor_db = s1;
s0 = s1;
}
}
}
// log2 amplitudes of harmonics 1..n from a harmonic fit: voiced ones (flags[k] set) blend the fitted amplitude
// toward the spectrum's rms, unvoiced ones take the rms; each gets its offset and a per-band offset interpolated
// over 500 Hz steps, unvoiced ones a pitch slope, and harmonics above the high-pass corner region get the
// compensation that undoes the input high-pass.
void harmonic_amplitudes(const Encoder& e, const HarmonicFit& fit, const uint8_t* flags, int n, float* out,
float fundamental) {
if (n < 1) return;
const EncoderOptions& o = e.options;
float f32 = fundamental * 32.0f;
for (int k = 0; k < n; k++) {
float hz = (fundamental * static_cast<float>(static_cast<unsigned>(k + 1))) * 8000.0f;
float t = hz * kHzToVoicingBand;
float v, s;
if (flags[k]) {
if (t <= 0.0f)
v = o.voiced_band_offset[0];
else if (t >= 8.0f)
v = o.voiced_band_offset[8];
else {
int i = to_int32_sat(t);
float fraction = t - static_cast<float>(to_int32_sat(t));
float a0 = o.voiced_band_offset[i], a1 = o.voiced_band_offset[i + 1];
v = a0 + fraction * (a1 - a0);
}
float q = fit.log2_amplitude[k];
float r = q + o.voiced_amplitude_blend * (fit.log2_rms[k] - q);
s = (o.voiced_offset + r) + v;
} else {
if (t <= 0.0f)
v = o.unvoiced_band_offset[0];
else if (t >= 8.0f)
v = o.unvoiced_band_offset[8];
else {
int i = to_int32_sat(t);
float fraction = t - static_cast<float>(to_int32_sat(t));
float a0 = o.unvoiced_band_offset[i], a1 = o.unvoiced_band_offset[i + 1];
v = a0 + fraction * (a1 - a0);
}
s = (fit.log2_rms[k] + o.unvoiced_offset) + v;
s = s + o.unvoiced_pitch_slope * fast_log2(f32);
}
out[k] = s;
float fc = o.highpass_hz, comp = o.highpass_compensation;
if (fc > 0.0f && comp > 0.0f && hz >= o.highpass_compensation_from_hz) {
float r = hz / fc;
float r4 = r * (r * (r * r));
float c = comp * -0.5f;
float q = r4 / (r4 + 1.0f);
q = q + kCompensationFloor;
out[k] = s + c * fast_log2(q);
}
}
}
// The voicing index for a harmonic fit at grid pitch `pitch` (whose fit cost is fit_cost): per band, the cost of
// calling it voiced or unvoiced against a threshold, weighted by the band's energy share; the best of the first
// code_count voicing patterns, with an optional cost for each band that changes from the previous frame.
int choose_voicing(const Encoder& e, const HarmonicFit& fit, int code_count, int pitch, float fit_cost) {
const EncoderOptions& o = e.options;
const core::Codebook& cb = codebook();
int previous = e.previous_voicing;
int row = 0;
while (row < 8) {
if (pitch < tables::kVoicingRowStart[row + 1]) break;
row++;
}
float sum = 0.0f;
for (int i = 0; i < 8; i++) sum = sum + fit.band_energy[i];
float pitch_index = static_cast<float>(pitch);
float total = sum + kEnergyFloor;
const float* base = tables::kVoicingThresholdBase + 8 * row;
const float* slope = tables::kVoicingThresholdSlope + 8 * row;
float voiced_cost[8], unvoiced_cost[8], weight[8];
for (int k = 0; k < 8; k++) {
float fk = static_cast<float>(static_cast<unsigned>(k));
float t;
if (o.voicing_table > 0.0f)
t = o.voicing_table_offset + (base[k] + fit_cost * slope[k]);
else
t = o.voicing_threshold + ((fk + -3.5f) * o.voicing_threshold_slope) * kTwoSevenths;
float strength = o.low_pitch_voicing_strength;
if (strength > 0.0f && fit_cost < o.low_pitch_voicing_fit && o.low_pitch_voicing_index > pitch_index) {
float d = strength * (o.low_pitch_voicing_fit - fit_cost);
t = t + (fk * kOneSeventh) * d;
}
bool has_data = fit.band_has_data[k] != 0;
float v = has_data ? fit.band_ratio[k] : t;
voiced_cost[k] = v > t ? (v - t) + kVoicingCostFloor : kVoicingCostFloor;
unvoiced_cost[k] = t > v ? t - v : 0.0f;
float w = 0.0f;
if (has_data) {
if (k == 0 && e.low_band && o.low_band_ignore_first > 0.0f) {
w = 0.0f;
} else {
float exponent = o.voicing_band_weight_exponent;
w = 1.0f;
if (!(exponent == 0.0f)) {
float r = fit.band_energy[k] / total;
w = 0.0f;
if (r > 0.0f) w = fast_exp2(exponent * fast_log2(r));
}
}
}
weight[k] = w;
}
float weight_sum = 0.0f;
for (int k = 0; k < 8; k++) weight_sum = weight_sum + weight[k];
if (weight_sum <= 0.0f && code_count >= 17) return kAllUnvoiced;
float best = kHuge;
int result = 0;
for (int i = 0; i < code_count; i++) {
const int32_t* c = cb.voicing[i];
float acc = 0.0f;
for (int b = 0; b < 8; b++) acc = acc + (c[b] == 0 ? unvoiced_cost[b] : voiced_cost[b]) * weight[b];
float penalty = o.voicing_hysteresis;
if (previous >= 0 && penalty > 0.0f) {
const int32_t* p = cb.voicing[previous];
for (int b = 0; b < 8; b++) acc = c[b] == p[b] ? acc : penalty + acc;
}
if (acc < best) {
best = acc;
result = i;
}
}
return result;
}
// Voicing of harmonics 1..n from the 500 Hz bands of voicing pattern `voicing`.
void voicing_flags(int voicing, int n, float fundamental, uint8_t* flags) {
const int32_t* row = codebook().voicing[voicing];
for (int i = 0; i < n; i++) {
float v = static_cast<float>(static_cast<uint32_t>(i + 1)) * 16.0f;
int b = to_int32_sat(fundamental * v);
b = b < 7 ? b : 7;
flags[i] = static_cast<uint8_t>(row[b]);
}
}
// The rescoring input: each harmonic's band and weight (the window weight energy, scaled down by the amplitude
// offset the harmonic got), and the reference band energies from the amplitudes or from the amplitude spectrum.
void band_energies(const Encoder& e, RescoreInput& out, const float* amps, int n, const uint8_t* flags,
const float* weights, const AnalysisSlot& slot, float fundamental) {
const EncoderOptions& o = e.options;
out.band_energy.fill(0.0f);
const float* edge = tables::kRescoreBandEdge;
for (int k = 0; k < n; k++) {
float offset = (flags && flags[k]) ? o.voiced_offset : o.unvoiced_offset;
float hz = (fundamental * static_cast<float>(static_cast<unsigned>(k + 1))) * 8000.0f;
int j = 0;
while (j < 17) {
if (!(hz >= edge[j + 1])) break;
j++;
}
out.band[k] = j;
out.weight[k] = weights[k] * fast_exp2(offset * -2.0f);
if (o.rescore_from_spectrum == 0.0f) {
float b = out.band_energy[j];
float g = fast_exp2(amps[k] + amps[k]);
out.band_energy[j] = b + g * out.weight[k];
}
}
if (!(o.rescore_from_spectrum == 0.0f)) {
const Spectrum& s = slot.amplitude_spectrum;
for (int i = 1; i < kSpectrumBins; i++) {
float hz = static_cast<float>(static_cast<unsigned>(i)) * 15.625f;
int j = 0;
while (j < 17) {
if (!(hz >= edge[j + 1])) break;
j++;
}
float b = out.band_energy[j];
b = b + s.re[i] * s.re[i];
b = b + s.im[i] * s.im[i];
out.band_energy[j] = b;
}
}
}
// Chooses the gain and shape fields for the target log2 amplitudes amps[L], fills fields and runs the
// dequantiser on them. The gain is the nearest to the target mean, optionally looking two frames ahead; the shape
// is searched on the target minus the prediction, with optional rescoring of the best candidates (and the gains
// around the chosen one) by band energies or by the enhanced decoded amplitudes.
void quantize(Encoder& e, int pitch_field, int voicing_field, const float* amps, int L, const RescoreInput* rescore,
core::FrameFields& fields) {
const EncoderOptions& o = e.options;
const core::Codebook& cb = codebook();
core::DequantState& state = e.predictor;
float prediction[kMaxFitHarmonics], target[kMaxFitHarmonics], weight[kMaxFitHarmonics];
float recon[kMaxFitHarmonics], decoded[kMaxFitHarmonics], enhanced[kMaxFitHarmonics + 2], coef[kBasisRows];
ShapeCandidate list[kMaxShapeResults];
core::FrameParams frame{};
core::dequant_predict(cb, state, L, prediction);
float sum = 0.0f, sum_prediction = 0.0f, peak = kVeryNegative;
for (int i = 0; i < L; i++) {
sum = sum + amps[i];
sum_prediction = sum_prediction + prediction[i];
if (amps[i] > peak) peak = amps[i];
}
float reciprocal = tables::kReciprocalCount[L];
float mean = sum * reciprocal;
float bias = tables::kHalfLog2Count[L];
float gain_target = mean + bias;
fields = core::FrameFields{};
fields[core::kFieldPitch] = pitch_field;
fields[core::kFieldVoicing] = voicing_field;
fields[core::kFieldGain] = 0;
const float gain_bias = o.gain_bias;
const int gain_count = cb.gain_count;
const float* gain = cb.gain;
int chosen_gain = 0;
if (gain_count >= 1) {
float w = cb.gain_weight, p = cb.previous_gain_weight * state.log2_gain;
float best = kHuge;
for (int k = 0; k < gain_count; k++) {
float v = (((w * gain[k]) + p) - gain_target) - gain_bias;
v = v < 0.0f ? -v : v;
if (v < best) {
fields[core::kFieldGain] = k;
chosen_gain = k;
best = v;
}
}
}
float lookahead_weight = o.gain_lookahead_weight;
int lookahead = e.lookahead_count;
if (lookahead_weight > 0.0f && lookahead >= 1) {
float best = kHuge;
for (int g = chosen_gain - 3, high = chosen_gain + 3;; g++) {
if (g >= 0 && g < gain_count) {
float w = cb.gain_weight, pw = cb.previous_gain_weight;
float level = (w * gain[g]) + (pw * state.log2_gain);
float c = (level - gain_target) - gain_bias;
c = c * c;
for (int m = 0; m < lookahead; m++) {
float carried = pw * level, next_target = e.lookahead_gain[m], nearest = kHuge;
for (int k = 0; k < gain_count; k++) {
float candidate = carried + (w * gain[k]);
float d = (candidate - next_target) - gain_bias;
d = d < 0.0f ? -d : d;
if (d < nearest) {
nearest = d;
level = candidate;
}
}
c = c + (nearest * (lookahead_weight * nearest));
}
if (c < best) {
best = c;
chosen_gain = g;
}
}
if (!(g < high)) break;
}
fields[core::kFieldGain] = chosen_gain;
}
float mean_prediction = sum_prediction * reciprocal;
if (L >= 1) {
float fL = static_cast<float>(static_cast<uint32_t>(L)), weight_sum = 0.0f;
for (int i = 0; i < L; i++) {
float a = amps[i];
target[i] = (a - mean) - (prediction[i] - mean_prediction);
float w = 1.0f;
if (o.shape_weight_exponent > 0.0f) w = fast_exp2((a - peak) * o.shape_weight_exponent);
weight[i] = w;
float high = o.shape_high_weight;
if (high != 0.0f) {
float r = static_cast<float>(static_cast<uint32_t>(i + 1)) / fL;
if (r > kHighShapeStart) {
w = w * fast_exp2(((r + kMinusHighShapeStart) * high) * kHighShapeScale);
weight[i] = w;
}
}
weight_sum = weight_sum + w;
}
// every weight can underflow to 0 with a large shape_weight_exponent; they stay 0 then
float scale = weight_sum != 0.0f ? fL / weight_sum : 1.0f;
for (int i = 0; i < L; i++) weight[i] = scale * weight[i];
}
int candidates = 1;
if (rescore) {
int v = to_int32_sat(o.rescore_candidates);
if (v >= 1 && rescore->enabled != 0) candidates = v;
}
if (static_cast<uint32_t>(candidates) >= 16) candidates = 16;
int beam = to_int32_sat(o.shape_search_beam);
int results = search_shape(e.shape_basis, cb, L, target, weight, beam, beam, list, candidates);
fields[core::kFieldShapeLow] = results >= 1 ? list[0].low : 0;
fields[core::kFieldShapeHigh] = results >= 1 ? list[0].high : 0;
for (int b = 0; b < 4; b++) fields[core::kFieldDetail1 + b] = results >= 1 ? list[0].detail[b] : 0;
if (rescore && rescore->enabled != 0 && results >= 1) {
const float* rows = e.shape_basis.rows(L);
int span = to_int32_sat(o.rescore_gain_span);
span = span < gain_count ? span : gain_count; // a wider span reaches no further gain index
int center = fields[core::kFieldGain];
float denominator = list[0].distortion + kRescoreDistanceFloor;
float best = kHuge;
int best_gain = center, best_candidate = 0;
const float* reference = rescore->band_energy.data();
for (int c = 0; c < results; c++) {
const ShapeCandidate& candidate = list[c];
const float* low = cb.shape_low[candidate.low];
const float* high = cb.shape_high[candidate.high];
for (int k = 0; k < 3; k++) coef[k] = low[k];
for (int k = 0; k < 4; k++) coef[3 + k] = high[k];
for (int b = 0; b < 4; b++) {
const float* detail = cb.detail[b] + static_cast<long>(cb.detail_stride[b] * candidate.detail[b]) * 4;
for (int k = 0; k < 4; k++) coef[7 + 4 * b + k] = detail[k];
}
for (int i = 0; i < L; i++) recon[i] = 0.0f;
for (int r = 0; r < kBasisRows; r++)
for (int i = 0; i < L; i++) recon[i] = recon[i] + coef[r] * rows[static_cast<long>(r) * L + i];
float error = 0.0f;
for (int i = 0; i < L; i++) {
float d = target[i] - recon[i];
error = error + d * (weight[i] * d);
}
if (span < 0) continue;
float normalised_error = error / denominator;
for (int g = center - span, high_g = center + span;; g++) {
if (g >= 0 && g < gain_count) {
float level = (cb.gain_weight * gain[g]) + (cb.previous_gain_weight * state.log2_gain);
for (int i = 0; i < L; i++)
decoded[i] = (level - bias) + (recon[i] + (prediction[i] - mean_prediction));
float gain_error = (level - gain_target) - gain_bias;
float score;
if (o.rescore_enhanced > 0.0f) {
frame.harmonic_count = L;
float f = pitch_field <= 119 ? cb.fundamental[pitch_field] : kToneFundamental;
frame.fundamental = f;
frame.omega = f * kTwoPi;
for (int i = 0; i < L; i++) frame.log2_amplitude[i] = decoded[i];
core::shape_log2(frame, o.rescore_enhanced > 1.5f, enhanced);
float s = 0.0f;
if (L > 0) {
float a6 = o.gain_bias, a7 = o.rescore_enhanced_offset;
for (int i = 0; i < L; i++) {
float d = enhanced[1 + i] - ((amps[i] + a6) + a7);
s = s + d * (weight[i] * d);
}
}
score = (reciprocal * s) + (gain_error * (gain_error * o.rescore_gain_weight));
} else {
float histogram[kRescoreBands] = {};
for (int i = 0; i < L; i++) {
int band = rescore->band[i];
float h = histogram[band];
float d = decoded[i] - o.gain_bias;
float v = fast_exp2(d + d) * rescore->weight[i];
histogram[band] = h + v;
}
float top = 0.0f;
for (int b = 0; b < kRescoreBands; b++)
if (reference[b] > top) top = reference[b];
float floor = (top * fast_exp2(-o.rescore_floor)) + kRescoreBandFloor;
float total = top + floor;
float acc = 0.0f;
for (int b = 0; b < kRescoreBands; b++) {
if (reference[b] <= 0.0f && histogram[b] <= 0.0f) continue; // NaN goes on
float coded = fast_log2(floor + histogram[b]);
float wanted = fast_log2(floor + reference[b]);
float w = 1.0f;
if (o.rescore_weight_exponent > 0.0f)
w = fast_exp2(o.rescore_weight_exponent * fast_log2((floor + reference[b]) / total));
float dl = coded - wanted, wf = 1.0f;
if (dl > 0.0f) wf = o.rescore_asymmetry;
acc = acc + ((dl * (dl * w)) * wf);
}
score = (acc + (normalised_error * o.rescore_shape_mix)) +
(gain_error * (gain_error * o.rescore_gain_weight));
}
if (score < best) {
best_gain = g;
best_candidate = c;
best = score;
}
}
if (!(g < high_g)) break;
}
}
const ShapeCandidate& chosen = list[best_candidate];
fields[core::kFieldShapeLow] = chosen.low;
fields[core::kFieldShapeHigh] = chosen.high;
for (int b = 0; b < 4; b++) fields[core::kFieldDetail1 + b] = chosen.detail[b];
fields[core::kFieldGain] = best_gain;
}
core::dequantize(cb, state, fields, nullptr);
}
// The input frame: scaled to +-1, through the high-pass when its corner is above 0.
void filter_input(Encoder& e, const int16_t* pcm, float* x) {
HighPass& hp = e.highpass;
float corner = e.options.highpass_hz;
if (corner > 0.0f) {
if (!(hp.designed_hz == corner)) {
int w8 = to_int32_sat((corner * 8192.0f) / 8000.0f + 0.5f);
float c = tables::kCosine[static_cast<uint32_t>(w8) & 0x1fff];
float alpha = tables::kCosine[(static_cast<uint32_t>(w8) + 0x1800u) & 0x1fff];
alpha = alpha * kInvSqrt2;
float denominator = alpha + 1.0f;
float one_plus_cos = c + 1.0f;
float half = one_plus_cos * 0.5f;
float minus = -one_plus_cos;
float a1 = c * -2.0f;
hp.b0 = half / denominator;
hp.b1 = minus / denominator;
hp.b2 = half / denominator;
hp.a1 = a1 / denominator;
hp.a2 = (1.0f - alpha) / denominator;
hp.designed_hz = corner;
}
for (int i = 0; i < kFrameSamples; i++) {
float in = static_cast<float>(pcm[i]) * 0x1p-15f;
float y = hp.b0 * in;
y = y + hp.b1 * hp.x1;
y = y + hp.b2 * hp.x2;
y = y - hp.a1 * hp.y1;
y = y - hp.a2 * hp.y2;
float x1 = hp.x1, y1 = hp.y1;
hp.x2 = x1;
hp.x1 = in;
hp.y1 = (kHighPassFlush > std::fabs(y)) ? 0.0f : y;
hp.y2 = (kHighPassFlush > std::fabs(y1)) ? 0.0f : y1;
x[i] = y;
}
} else {
for (int i = 0; i < kFrameSamples; i++) x[i] = static_cast<float>(pcm[i]) * 0x1p-15f;
}
}
void update_transitions(Encoder& e) {
const EncoderOptions& o = e.options;
const float* lf = e.log2_fundamental.data();
for (int r = 0; r < kPitchCount; r++) {
float* row = &e.transition[static_cast<size_t>(r) * kPitchCount];
for (int c = 0; c < kPitchCount; c++) {
float d = lf[r] - lf[c];
float ad = d < 0.0f ? -d : d;
float s1 = (ad + -1.0f) * kOctaveJumpScale;
float octave = 0.0f, jump = 0.0f;
if (std::fabs(s1) < 3.0f) octave = o.octave_jump_cost * fast_exp2(s1 * (s1 * kOctaveJumpExponent));
float f = ad - o.pitch_jump_free;
if (f > 0.0f) jump = f * o.pitch_jump_cost;
row[c] = octave + jump;
}
}
}
// Analyses the newest frame (already in the history) into its slot and pushes its pitch costs to the tracker.
void analyse_frame(Encoder& e, int64_t count) {
const EncoderOptions& o = e.options;
SignalHistory& history = e.history;
AnalysisSlot& slot = history.slots[static_cast<size_t>(count % 3)];
int pitch_offset = clamp_int(to_int32_sat(o.pitch_window_offset), kPitchOffsetLow, kPitchOffsetHigh);
int amplitude_offset =
clamp_int(to_int32_sat(o.amplitude_window_offset), kAmplitudeOffsetLow, kAmplitudeOffsetHigh);
// The writes into the slot happen in this order: with positive offsets the later reads see the earlier writes.
analysis_spectrum(e.long_window, history, pitch_offset + kAnalysisCenter, slot.pitch_spectrum);
analysis_spectrum(e.short_window, history, amplitude_offset + kAnalysisCenter, slot.amplitude_spectrum);
slot.peak = 0.0f;
{
int i0 = amplitude_offset + kNewSamples, i1 = amplitude_offset + kHistorySamples - 1;
float top = 0.0f;
for (int i = i0; i <= i1; i++) {
float s = history.sample(i);
if (s < 0.0f) s = -s;
if (s > top) {
slot.peak = s;
top = s;
}
}
}
float energy;
harmonic_fit_cost(e.long_window, slot.pitch_spectrum, e.fit_cost.data(), &energy);
slot.fit_cost = e.fit_cost;
slot.level_db = fast_log2(energy + kEnergyFloor) * kDbPerLog2;
yin_difference(history, pitch_offset + kAnalysisCenter, e.inverse_fundamental.data(), kPitchCount, kYinMaxLag,
e.yin_cost.data());
float low_pitch_penalty = o.low_pitch_penalty;
if (o.low_band_threshold_db < 0.0f) {
const Spectrum& s = slot.pitch_spectrum;
float a = kTiny;
for (int i = 2; i < 17; i++) {
a = a + s.re[i] * s.re[i];
a = a + s.im[i] * s.im[i];
}
float b = kTiny;
for (int i = 22; i < 65; i++) {
b = b + s.re[i] * s.re[i];
b = b + s.im[i] * s.im[i];
}
float t = fast_log2(a / b) * kDbPerLog2;
int low = t < o.low_band_threshold_db;
slot.low_band = low;
if (low) low_pitch_penalty = low_pitch_penalty * o.low_band_penalty_scale;
}
if (o.onset_db > 0.0f && count >= 2) {
float d = slot.level_db - history.slots[static_cast<size_t>((count + 1) % 3)].level_db;
if (d > o.onset_db) low_pitch_penalty = low_pitch_penalty * o.onset_low_pitch_scale;
}
for (int i = 0; i < kPitchCount; i++)
e.pitch_cost[i] = (e.fit_cost[i] + e.yin_cost[i] * o.yin_weight) + e.log2_ratio_to_lowest[i] * low_pitch_penalty;
// pitch network features: 120 fit costs, 120 YIN values, 64 band log energies (relative to the largest), level
std::array<float, kNetFeatures>& features = e.features[static_cast<size_t>(count % 6)];
std::memcpy(&features[0], e.fit_cost.data(), sizeof(float) * kPitchCount);
std::memcpy(&features[kPitchCount], e.yin_cost.data(), sizeof(float) * kPitchCount);
{
float* band = &features[2 * kPitchCount];
float top = kVeryNegative;
const Spectrum& s = slot.pitch_spectrum;
for (int k = 0; k < 64; k++) {
float sum = kTiny;
for (int j = 0; j < 4; j++) {
sum = sum + s.re[4 * k + j] * s.re[4 * k + j];
sum = sum + s.im[4 * k + j] * s.im[4 * k + j];
}
float v = fast_log2(sum);
band[k] = v;
top = v > top ? v : top;
}
for (int k = 0; k < 64; k++) band[k] = band[k] - top;
features[kNetFeatures - 1] = slot.level_db * kLevelFeatureScale;
}
if (o.pitch_net_weight > 0.0f) {
for (int i = 0; i < kNetFrames; i++) {
int64_t m = count - 5 + i;
float* in = &e.net_input[static_cast<size_t>(i) * kNetFeatures];
if (m < 0)
std::memset(in, 0, sizeof(float) * kNetFeatures);
else
std::memcpy(in, e.features[static_cast<size_t>(m % 6)].data(), sizeof(float) * kNetFeatures);
}
pitch_net_forward(e.net_input.data(), e.net_output.data());
float w = o.pitch_net_weight;
for (int i = 0; i < kPitchCount; i++) e.pitch_cost[i] = e.pitch_cost[i] - e.net_output[i] * w;
}
if (e.transitions_dirty) {
update_transitions(e);
e.transitions_dirty = false;
}
tracker_push(e.tracker, e.pitch_cost.data(), e.transition.data());
history.frame_count = count + 1;
}
// The expected gain target of the frame `ahead` (1 or 2) frames after `frame`, for the gain look-ahead.
float lookahead_target(Encoder& e, int64_t frame, int ahead) {
const EncoderOptions& o = e.options;
const AnalysisSlot& slot = e.history.slots[static_cast<size_t>((ahead + frame) % 3)];
int pitch = tracker_decide(e.tracker, 2 - ahead);
float level = slot.level_db;
if ((level > o.gate_level_db && level > e.noise_floor_db + o.gate_above_floor_db) || !(ahead >= e.gate_hang)) {
HarmonicFit fit;
uint8_t flags[kMaxFitHarmonics];
float amps[kMaxFitHarmonics];
fit_harmonics(e.long_window, slot.pitch_spectrum, pitch, harmonics_of(pitch), fundamental_of(pitch), fit);
e.low_band = slot.low_band;
const float cost = slot.fit_cost[pitch];
int voicing = choose_voicing(e, fit, 32, pitch, cost);
if (!is_voiced(voicing) && cost < o.unvoiced_fit_guard) voicing = choose_voicing(e, fit, 16, pitch, cost);
float f0 = fundamental_of(pitch);
int n = harmonics_of(pitch);
fit_harmonics(e.short_window, slot.amplitude_spectrum, pitch, harmonics_of(pitch), fundamental_of(pitch), fit);
float s = 0.0f;
if (n >= 1) {
voicing_flags(voicing, n, f0, flags);
harmonic_amplitudes(e, fit, flags, n, amps, f0);
for (int i = 0; i < n; i++) s = s + amps[i];
}
return s * tables::kReciprocalCount[n] + tables::kHalfLog2Count[n];
}
float amps[kSilenceBands];
silence_amplitudes(e.short_window, slot.amplitude_spectrum, amps);
float s = 0.0f;
for (int i = 0; i < kSilenceBands; i++) s = o.silence_offset + (o.unvoiced_offset + (s + amps[i]));
return tables::kHalfLog2Count[kToneHarmonics] + tables::kReciprocalCount[kToneHarmonics] * s;
}
} // namespace
void encoder_reset(Encoder& e) {
e.history.samples.fill(0.0f);
e.history.frame_count = 0;
for (AnalysisSlot& slot : e.history.slots) std::memset(&slot, 0, sizeof slot);
tracker_reset(e.tracker, kPitchCount, kEncoderDelay);
e.noise_floor_db = 0.0f;
e.gate_hang = 0;
core::dequant_reset(e.predictor);
e.lookahead_gain.fill(0.0f);
e.lookahead_count = 0;
e.previous_amplitudes.fill(0.0f);
e.previous_fundamental = kInitialPreviousFundamental;
e.previous_harmonic_count = 0;
e.have_previous = 0;
e.previous_voicing = -1;
e.after_silence = 0;
e.low_band = 0;
e.previous_unvoiced = 1;
e.highpass.x1 = e.highpass.x2 = e.highpass.y1 = e.highpass.y2 = 0.0f;
e.highpass.designed_hz = -1.0f;
for (auto& f : e.features) f.fill(0.0f);
e.net_input.fill(0.0f);
e.net_output.fill(0.0f);
e.transitions_dirty = true;
}
void encoder_init(Encoder& e) {
for (int i = 0; i < kPitchCount; i++) {
float f0 = fundamental_of(i);
e.inverse_fundamental[i] = 1.0f / f0;
e.log2_fundamental[i] = fast_log2(f0);
}
for (int i = 0; i < kPitchCount; i++) e.log2_ratio_to_lowest[i] = e.log2_fundamental[0] - e.log2_fundamental[i];
for (int i = 0; i < kPitchCount; i++) {
float t = e.log2_fundamental[i] + -1.0f;
int best = -1;
float m = kOctaveMatch;
for (int k = 0; k < kPitchCount; k++) {
float d = e.log2_fundamental[k] - t;
d = d < 0.0f ? -d : d;
if (d < m) {
best = k;
m = d;
}
}
e.octave_below[i] = best;
}
init_analysis_window(e.long_window, tables::kLongWindow, 256);
init_analysis_window(e.short_window, tables::kShortWindow, 160);
init_shape_basis(e.shape_basis, codebook());
encoder_reset(e);
}
void encode_frame(Encoder& e, const int16_t* pcm, core::FrameFields& fields) {
const EncoderOptions& o = e.options;
SignalHistory& history = e.history;
std::memmove(&history.samples[0], &history.samples[kFrameSamples], sizeof(float) * kNewSamples);
filter_input(e, pcm, &history.samples[kNewSamples]);
int64_t count = history.frame_count;
analyse_frame(e, count);
float amps[kMaxFitHarmonics];
uint8_t flags[kMaxFitHarmonics] = {};
int64_t frame = count - kEncoderDelay;
if (frame < 0) {
// the frames before the first: silent, at a fixed low level
e.lookahead_count = 0;
for (float& a : amps) a = kStartupAmplitude;
quantize(e, kTonePitch, silent_voicing_index(o), amps, kToneHarmonics, nullptr, fields);
return;
}
int pitch = tracker_decide(e.tracker, 2);
// take the octave below when its fit is nearly as good
if (o.octave_down_tolerance > 0.0f) {
int below = e.octave_below[pitch];
float divisor = e.previous_unvoiced == 0 ? 1.0f : o.octave_down_after_unvoiced;
if (below >= 0) {
float s0 = o.octave_down_tolerance / divisor;
const std::array<float, kPitchCount>& cost = history.slots[static_cast<size_t>(frame % 3)].fit_cost;
float s1 = cost[below];
s0 = cost[pitch] - s0;
if (!(s1 > s0 || s1 != s1 || s0 != s0)) pitch = below;
}
}
AnalysisSlot& slot = history.slots[static_cast<size_t>(frame % 3)];
// after an unvoiced frame, take the octave below when one of the later frames is already there
if (o.octave_down_lookback > 0.0f && e.previous_unvoiced != 0) {
int below = e.octave_below[pitch];
if (below >= 0) {
for (int lag = 1; lag != -1; lag--) {
int r = tracker_decide(e.tracker, lag);
if (std::fabs(e.log2_fundamental[r] - e.log2_fundamental[below]) < kOctaveLookbackTolerance) {
pitch = below;
break;
}
}
}
}
if (frame == 0) init_floor(e);
bool gate = update_gate(e, slot.level_db);
e.lookahead_count = 0;
if (o.gain_lookahead_weight > 0.0f) {
for (int ahead = 1; ahead != 3; ahead++) e.lookahead_gain[ahead - 1] = lookahead_target(e, frame, ahead);
e.lookahead_count = 2;
}
// voicing at the decided pitch
HarmonicFit fit;
fit_harmonics(e.long_window, slot.pitch_spectrum, pitch, harmonics_of(pitch), fundamental_of(pitch), fit);
e.low_band = slot.low_band;
const float cost = slot.fit_cost[pitch];
int voicing = choose_voicing(e, fit, 32, pitch, cost);
if (!is_voiced(voicing) && cost < o.unvoiced_fit_guard) voicing = choose_voicing(e, fit, 16, pitch, cost);
RescoreInput rescore;
rescore.enabled = o.rescore_candidates > 1.0f ? 1 : 0;
if (gate && !(o.gated_as_unvoiced > 0.0f)) {
// silent frame
silence_amplitudes(e.short_window, slot.amplitude_spectrum, amps);
for (int i = 0; i < kSilenceBands; i++) amps[i] = o.silence_offset + (o.unvoiced_offset + amps[i]);
e.have_previous = 0;
if (rescore.enabled)
band_energies(e, rescore, amps, kSilenceBands, nullptr, e.short_window.silence_norm.data(), slot,
kToneFundamental);
e.previous_voicing = -1;
e.after_silence = 1;
e.previous_unvoiced = 1;
quantize(e, kTonePitch, silent_voicing_index(o), amps, kToneHarmonics, &rescore, fields);
return;
}
if (gate) voicing = kAllUnvoiced;
int after_silence = e.after_silence;
e.after_silence = 0;
if (after_silence && o.onset_unvoiced > 0.0f) voicing = kAllUnvoiced;
if (!is_voiced(voicing)) {
float s0 = o.unvoiced_pitch_index;
int v = to_int32_sat(s0);
if (!(s0 < 0.0f || s0 != s0)) pitch = v < kPitchCount ? v : kPitchCount - 1;
}
int n = harmonics_of(pitch);
float f0 = fundamental_of(pitch);
fit_harmonics(e.short_window, slot.amplitude_spectrum, pitch, n, f0, fit);
if (n > 0) {
voicing_flags(voicing, n, f0, flags);
harmonic_amplitudes(e, fit, flags, n, amps, f0);
if (gate)
for (int i = 0; i < n; i++) amps[i] = o.gated_offset + amps[i];
if (after_silence)
for (int i = 0; i < n; i++) amps[i] = o.onset_offset + amps[i];
}
// smoothing with the previous frame and the next one
float next_weight = o.smooth_next;
if (!(next_weight == 0.0f) || !(o.smooth_previous == 0.0f)) {
float previous_weight = e.have_previous ? o.smooth_previous : 0.0f;
float next[kMaxFitHarmonics];
if (!(next_weight == 0.0f)) {
const AnalysisSlot& following = history.slots[static_cast<size_t>((count - 1) % 3)];
HarmonicFit next_fit;
fit_harmonics(e.short_window, following.amplitude_spectrum, pitch, n, f0, next_fit);
harmonic_amplitudes(e, next_fit, flags, n, next, f0);
}
if (n >= 1) {
float own = (1.0f - previous_weight) - next_weight;
const float* prev = e.previous_amplitudes.data();
for (int i = 0; i < n; i++) {
float a = amps[i];
float s3 = own * a;
if (!(previous_weight == 0.0f)) {
float pos = (f0 * static_cast<float>(static_cast<uint32_t>(i + 1))) / e.previous_fundamental;
pos = pos + -1.0f;
pos = pos < 0.0f ? 0.0f : pos;
int32_t pn = e.previous_harmonic_count;
float last = static_cast<float>(pn - 1);
pos = pos > last ? last : pos;
int k = to_int32_sat(pos);
float p;
if (k + 1 < pn) {
float p0 = prev[k];
p = p0 + (pos - static_cast<float>(k)) * (prev[k + 1] - p0);
} else {
p = prev[k];
}
s3 = s3 + previous_weight * p;
}
if (!(next_weight == 0.0f)) {
float b = next[i];
a = a - o.smooth_next_margin;
a = b > a ? b : a;
s3 = s3 + next_weight * a;
}
amps[i] = s3;
}
}
}
if (n >= 1) std::memcpy(e.previous_amplitudes.data(), amps, sizeof(float) * static_cast<size_t>(n));
e.previous_fundamental = f0;
e.previous_harmonic_count = n;
e.have_previous = 1;
if (n >= 1 && slot.peak >= o.peak_threshold)
for (int i = 0; i < n; i++) amps[i] = amps[i] - o.peak_reduction;
if (rescore.enabled) {
int first = e.short_window.first_harmonic[pitch];
band_energies(e, rescore, amps, n, flags, &e.short_window.weight_energy[first], slot, f0);
}
bool unvoiced = !is_voiced(voicing);
int keep;
if (unvoiced)
keep = (!gate && o.voicing_hold_unvoiced > 0.0f) ? voicing : -1;
else
keep = gate ? -1 : voicing;
e.previous_voicing = keep;
e.previous_unvoiced = (gate || unvoiced) ? 1 : 0;
quantize(e, pitch, voicing, amps, n, &rescore, fields);
}
} // namespace halfrate::enc
+97
View File
@@ -0,0 +1,97 @@
// The encoder: 160 input samples in, the nine fields of one frame out, two frames behind the input.
//
// Each input frame is high-passed, analysed (two windowed spectra, the per-pitch harmonic fit and YIN costs, the
// pitch network) and pushed into the pitch tracker. The frame two frames back is then decided: pitch from the
// tracker, the noise gate, voicing from the harmonic fit, harmonic amplitudes from the amplitude spectrum, and the
// quantiser chooses gain and shape fields against the decoder's own prediction, which it tracks with a
// dequantiser state of its own.
#pragma once
#include <array>
#include <cstdint>
#include "dequant.h"
#include "frame.h"
#include "analysis.h"
#include "history.h"
#include "options.h"
#include "pitch.h"
#include "shape_quantizer.h"
namespace halfrate::enc {
constexpr int kEncoderDelay = 2;
// Input high-pass: a second-order section (coefficients for the corner the options name).
struct HighPass {
float b0 = 0.0f, b1 = 0.0f, b2 = 0.0f, a1 = 0.0f, a2 = 0.0f;
float x1 = 0.0f, x2 = 0.0f, y1 = 0.0f, y2 = 0.0f;
float designed_hz = -1.0f; // corner the coefficients are for
};
// What the band-energy rescoring of the shape candidates compares against: per harmonic its band (of the 18
// rescoring bands) and weight, and the reference energy per band.
struct RescoreInput {
int32_t enabled = 0;
std::array<int32_t, kMaxFitHarmonics> band{};
std::array<float, kMaxFitHarmonics> weight{};
std::array<float, 18> band_energy{};
};
struct Encoder {
EncoderOptions options;
bool transitions_dirty = true; // the tracker's transition costs depend on options
// pitch grid (the codebook's pitch indices)
std::array<float, kPitchCount> inverse_fundamental{}; // samples per period
std::array<float, kPitchCount> log2_fundamental{};
std::array<float, kPitchCount> log2_ratio_to_lowest{}; // log2 f[0] - log2 f[i]: octaves below index 0
std::array<int32_t, kPitchCount> octave_below{}; // index one octave lower (-1: none within 0.06)
std::array<float, kPitchCount * kPitchCount> transition{}; // tracker cost [from * 120 + to]
AnalysisWindow long_window; // 256 points: pitch
AnalysisWindow short_window; // 160 points: amplitudes
ShapeBasis shape_basis;
SignalHistory history;
PitchTracker tracker;
// gate
float noise_floor_db = 0.0f;
int32_t gate_hang = 0;
// the decoder's predictor state, and what the gain choice looks ahead to
core::DequantState predictor;
std::array<float, 2> lookahead_gain{}; // expected gain targets of the next two frames
int32_t lookahead_count = 0; // how many of them are set (0 or 2)
// previous frame
std::array<float, kMaxFitHarmonics> previous_amplitudes{};
float previous_fundamental = 0.0f;
int32_t previous_harmonic_count = 0;
int32_t have_previous = 0; // previous_amplitudes hold a speech frame
int32_t previous_voicing = -1; // its voicing index for the hysteresis, -1: none
int32_t after_silence = 0; // the previous frame was silent
int32_t low_band = 0; // the frame being voiced has a weak low band
int32_t previous_unvoiced = 1; // the previous frame was unvoiced or gated
HighPass highpass;
// pitch network input: the last six frames' features (frame n in features[n % 6])
std::array<std::array<float, kNetFeatures>, kNetFrames> features{};
std::array<float, kNetInputs> net_input{};
std::array<float, kNetOutputs> net_output{};
// per grid pitch, this frame
std::array<float, kPitchCount> fit_cost{};
std::array<float, kPitchCount> yin_cost{};
std::array<float, kPitchCount> pitch_cost{};
};
// Builds the tables and resets the state (the options keep their values).
void encoder_init(Encoder& e);
void encoder_reset(Encoder& e);
// Encodes 160 samples; fields gets the frame of the input two frames back.
void encode_frame(Encoder& e, const int16_t* pcm, core::FrameFields& fields);
} // namespace halfrate::enc
+47
View File
@@ -0,0 +1,47 @@
// The encoder half of the C API (include/halfrate.h).
#include <cstring>
#include <new>
#include "frame.h"
#include "numeric.h"
#include "encoder.h"
#include "halfrate.h"
struct hr_encoder {
halfrate::enc::Encoder encoder;
};
extern "C" {
hr_encoder* hr_encoder_new(void) {
hr_encoder* e = new (std::nothrow) hr_encoder;
if (!e) return nullptr;
halfrate::enc::encoder_init(e->encoder);
return e;
}
void hr_encoder_free(hr_encoder* e) { delete e; }
int hr_encoder_set(hr_encoder* e, const char* name, double value) {
if (!e || !name) return -1;
if (!halfrate::enc::set_option(e->encoder.options, name, value)) return -1;
e->encoder.transitions_dirty = true;
return 0;
}
void hr_encode(hr_encoder* e, const int16_t* pcm, uint8_t* frame) {
using namespace halfrate;
if (!e || !pcm || !frame) return;
core::FrameFields fields;
enc::encode_frame(e->encoder, pcm, fields);
uint8_t bits[core::kFrameBits], rotated[core::kFrameBits];
core::bits_from_fields(fields, bits);
if (core::to_int32_sat(e->encoder.options.rotated_layout) == 1) {
core::rotated_from_standard(bits, rotated);
core::bytes_from_bits(rotated, frame);
} else {
core::bytes_from_bits(bits, frame);
}
}
} // extern "C"
+101
View File
@@ -0,0 +1,101 @@
#include "enhance.h"
#include "fastmath.h"
#include "numeric.h"
#include "tables.h"
namespace halfrate::core {
namespace {
constexpr float kResponseScale = 0x1.8209f6p+1f; // 3.01592898
constexpr float kLog2Floor = 0x1.197998p-40f; // 1e-12, keeps log2 finite
constexpr float kTiltStart = -0x1.4cccccp-1f; // tilt starts at harmonic 0.65 L ...
constexpr float kTiltWidth = 0x1.666668p-2f; // ... reaches full at 0.65 L + 0.35 L
constexpr float kTiltLog2Gain = 0x1.19999ap-1f; // full tilt: 2^0.55
// cos(2 pi f l) from the table, f in cycles per sample
inline float harmonic_cosine(float fundamental, float harmonic) {
float phase = fundamental * harmonic;
phase = phase * 8192.0f;
phase = phase + 0.5f;
return tables::kCosine[to_int64_sat(phase) & 8191];
}
} // namespace
void enhance_formants(const EnhanceOptions& options, float fundamental, float omega, int harmonic_count,
float* amplitude) {
if (options.mode != 1) return;
int count = harmonic_count;
if (count < 1) return;
float r0 = 0.0f, r1 = 0.0f;
for (int l = 1; l <= count; l++) {
float power = amplitude[l] * amplitude[l];
r0 = r0 + power;
r1 = r1 + power * harmonic_cosine(fundamental, static_cast<float>(static_cast<unsigned>(l)));
}
if (r0 <= 0.0f) return;
float r0_squared = r0 * r0, r1_squared = r1 * r1;
float squares = r1_squared + r0_squared;
float twice_product = r1 * (r0 + r0);
float difference = r0_squared - r1_squared;
float weight[kMaxHarmonics + 8];
int32_t every = options.bypass_divisor;
uint32_t bypass_limit = static_cast<uint32_t>(every); // l * every, wrapping like a 32-bit counter
for (int l = 1; l <= count; l++, bypass_limit += static_cast<uint32_t>(every)) {
float w = 1.0f;
float denominator = difference * (r0 * omega);
float harmonic = static_cast<float>(static_cast<unsigned>(l));
float numerator;
if (denominator <= 0.0f) goto store;
numerator = squares - twice_product * harmonic_cosine(fundamental, harmonic);
numerator = numerator * kResponseScale;
if (numerator <= 0.0f) goto store;
if (amplitude[l] <= 0.0f) goto store;
{
float log2_amplitude = fast_log2(amplitude[l]);
float log2_response = fast_log2(numerator / denominator);
log2_response = log2_response * 0.5f;
float raw = fast_exp2(options.strength * (log2_amplitude + log2_response));
if (every >= 1 && static_cast<int32_t>(bypass_limit) <= count)
w = 1.0f;
else
w = raw > options.max_weight ? options.max_weight : (raw < options.min_weight ? options.min_weight : raw);
float exponent = fundamental * harmonic < options.split_frequency ? options.power_low : options.power_high;
if (exponent != 1.0f) w = fast_exp2(exponent * fast_log2(w));
}
store:
weight[l] = w;
}
float energy = 0.0f;
for (int l = 1; l <= count; l++) {
float v = amplitude[l] * weight[l];
amplitude[l] = v;
energy = energy + v * v;
}
float scale = 1.0f;
if (energy > 0.0f) scale = fast_sqrt(r0 / energy);
for (int l = 1; l <= count; l++) amplitude[l] = scale * amplitude[l];
}
void shape_log2(const FrameParams& frame, bool tilt, float* out) {
int count = frame.harmonic_count;
float amplitude[kMaxHarmonics + 8];
amplitude[0] = 0.0f;
for (int i = 0; i < count; i++) amplitude[1 + i] = fast_exp2(frame.log2_amplitude[i]);
enhance_formants(EnhanceOptions{}, frame.fundamental, frame.omega, count, amplitude);
if (tilt) {
for (int l = 1; l <= count; l++) {
float x = static_cast<float>(static_cast<unsigned>(l)) / static_cast<float>(count);
x = x + kTiltStart;
x = x / kTiltWidth;
if (!(x > 0.0f)) continue;
float a = amplitude[l];
x = x > 1.0f ? 1.0f : x;
x = x * kTiltLog2Gain;
amplitude[l] = a * fast_exp2(x);
}
}
for (int i = 0; i < count; i++) out[1 + i] = fast_log2(amplitude[1 + i] + kLog2Floor);
}
} // namespace halfrate::core
+33
View File
@@ -0,0 +1,33 @@
// Spectral (formant) enhancement of the harmonic amplitudes, shared by the decoder (before synthesis) and the
// encoder (which scores candidate frames by what the decoder will produce).
#pragma once
#include <cstdint>
#include "dequant.h"
namespace halfrate::core {
// Enhancement settings. The defaults are the decoder's defaults and what shape_log2 uses.
struct EnhanceOptions {
int32_t mode = 1; // the enhancement runs only when this is exactly 1
float strength = 0.5f; // exponent applied to the raw weight
float min_weight = 0.3f; // weights are clamped to [min_weight, max_weight]
float max_weight = 2.0f;
int32_t bypass_divisor = 0; // >= 1: harmonics l with l * bypass_divisor <= L keep weight 1
float power_high = 0.85f; // the weight is raised to this power at or above split_frequency
float power_low = 0.6f; // and to this one below it
float split_frequency = 0.125f; // cycles per sample (1 kHz)
};
// Enhances amplitude[1..L] (linear; amplitude[0] is not used) in place. Each harmonic gets a weight
// (amplitude * sqrt(R(w)))^strength, where R is the response of a one-pole model fitted to the spectrum's
// autocorrelation R0, R1, clamped and raised to a frequency-dependent power; the result is rescaled to the
// original energy. fundamental is in cycles per sample, omega in radians per sample.
void enhance_formants(const EnhanceOptions& options, float fundamental, float omega, int harmonic_count,
float* amplitude);
// log2 harmonic amplitudes of frame after enhancement with the default options and, if tilt, a gain rising toward
// the top harmonics: out[1..L] (out[0] is not written). Reads fundamental, omega, harmonic_count and log2_amplitude.
void shape_log2(const FrameParams& frame, bool tilt, float* out);
} // namespace halfrate::core
+73
View File
@@ -0,0 +1,73 @@
#include "fastmath.h"
#include <cstdint>
#include "numeric.h"
namespace halfrate::core {
namespace {
constexpr float kSmallestNormal = 0x1p-126f;
constexpr float kSqrt2 = 0x1.6a09e6p+0f; // 1.41421354
constexpr float kTwoOverLn2 = 0x1.715476p+1f; // 2 / ln 2
constexpr float kLn2 = 0x1.62e43p-1f; // ln 2
} // namespace
float fast_log2(float x) {
if (!(x >= kSmallestNormal)) return -126.0f;
uint32_t bits = float_bits(x);
float m = float_from_bits(0x3f800000u | (bits & 0x7fffffu)); // mantissa in [1, 2)
float half = m * 0.5f;
bool fold = m > kSqrt2;
m = fold ? half : m;
int exponent = (fold ? -126 : -127) + static_cast<int>(bits >> 23);
// log(m) = 2 atanh(t), t = (m - 1) / (m + 1); series t (1 + t^2/3 + t^4/5 + t^6/7 + t^8/9)
float t = (m + -1.0f) / (m + 1.0f);
float t2 = t * t;
float p = t2 * (1.0f / 9.0f);
p = p + 1.0f / 7.0f;
p = t2 * p;
p = p + 1.0f / 5.0f;
p = t2 * p;
p = p + 1.0f / 3.0f;
p = t2 * p;
p = p + 1.0f;
float r = t * p;
r = r * kTwoOverLn2;
return r + static_cast<float>(exponent);
}
float fast_exp2(float x) {
if (x != x) return 0.0f;
x = x < -126.0f ? -126.0f : x;
x = x > 127.0f ? 127.0f : x;
// round half up: x + 200.5 is positive, so truncation is floor
int whole = static_cast<int>(x + 0.5f + 200.0f) - 200;
float f = x - static_cast<float>(whole);
f = f * kLn2;
// e^f = 1 + f (1 + f (1/2 + f (1/6 + f (1/24 + f (1/120 + f (1/720 + f / 5040))))))
float p = f * (1.0f / 5040.0f);
p = p + 1.0f / 720.0f;
p = f * p;
p = p + 1.0f / 120.0f;
p = f * p;
p = p + 1.0f / 24.0f;
p = f * p;
p = p + 1.0f / 6.0f;
p = f * p;
p = p + 0.5f;
p = f * p;
p = p + 1.0f;
float r = f * p;
r = r + 1.0f;
whole = whole > -126 ? whole : -126;
whole = whole < 127 ? whole : 127;
return r * float_from_bits(0x3f800000u + (static_cast<uint32_t>(whole) << 23));
}
float fast_sqrt(float x) {
if (!(x > kSmallestNormal)) return 0.0f;
return fast_exp2(fast_log2(x) * 0.5f);
}
} // namespace halfrate::core
+18
View File
@@ -0,0 +1,18 @@
// The codec's own log2, exp2 and square root. They are approximations (series on the mantissa), and the codec's
// output depends on their exact results, so the codec uses these, never the C library's.
#pragma once
namespace halfrate::core {
// log2(x): an atanh series on the mantissa folded into [sqrt(1/2), sqrt(2)). For x below the smallest normal
// float (including 0, negatives, subnormals and NaN) it returns -126. log2(+inf) is 128.
float fast_log2(float x);
// 2^x: x clamped to [-126, 127], the integer part (rounded half up) into the exponent, and a degree-7 Taylor
// series of e^(f ln 2) for the rest. NaN gives 0.
float fast_exp2(float x);
// sqrt(x) = fast_exp2(fast_log2(x) / 2) for x above the smallest normal float; 0 otherwise (and for NaN).
float fast_sqrt(float x);
} // namespace halfrate::core
+103
View File
@@ -0,0 +1,103 @@
#include "fec.h"
#include <cstring>
#include "tables.h"
namespace halfrate::core {
namespace {
// channel position of code bit i (DMR interleave)
inline int channel_position(int i) { return 4 * i - 71 * (i / 18); }
inline int popcount(uint32_t v) { return __builtin_popcount(v); }
} // namespace
uint32_t golay23_parity(uint32_t data) {
uint32_t parity = 0;
for (int i = 0; i < 12; i++)
if ((data >> (11 - i)) & 1) parity ^= tables::kGolayParityRows[i];
return parity;
}
uint32_t golay23_decode(uint32_t code, int* errors) {
uint32_t data = (code >> 11) & 0xfff;
uint32_t syndrome = (golay23_parity(data) ^ code) & 0x7ff;
uint32_t correction = tables::kGolaySyndromeCorrection[syndrome] & 0xfff;
data ^= correction;
if (errors) {
uint32_t left = (golay23_parity(data) ^ code) & 0x7ff;
*errors = popcount(left) + popcount(correction);
}
return data;
}
uint32_t fec_scramble_mask(uint32_t first_data) {
uint32_t state = first_data << 4, mask = 0;
for (int i = 0; i < 23; i++) {
uint32_t v = state * 173 + 13849;
state = v & 0xffff;
mask = (mask << 1) | ((v >> 15) & 1);
}
return mask;
}
void fec_encode_bits(const uint8_t* bits49, uint8_t* bits72) {
uint32_t d0 = 0, d1 = 0;
for (int i = 0; i < 12; i++) d0 = (d0 << 1) | (bits49[i] & 1);
for (int i = 0; i < 12; i++) d1 = (d1 << 1) | (bits49[12 + i] & 1);
uint8_t code[72];
uint32_t word0 = (golay23_parity(d0) & 0x7ff) | (d0 << 11), parity = 0;
for (int i = 0; i < 23; i++) {
code[i] = (word0 >> (22 - i)) & 1;
parity ^= code[i];
}
code[23] = static_cast<uint8_t>(parity);
uint32_t word1 = fec_scramble_mask(d0) ^ ((golay23_parity(d1) & 0x7ff) | (d1 << 11));
for (int i = 0; i < 23; i++) code[24 + i] = (word1 >> (22 - i)) & 1;
for (int i = 0; i < 25; i++) code[47 + i] = bits49[24 + i] & 1;
for (int i = 0; i < 72; i++) bits72[channel_position(i)] = code[i];
}
int fec_decode_bits(const uint8_t* bits72, uint8_t* bits49, FecErrors* errors) {
uint8_t code[72];
for (int i = 0; i < 72; i++) code[i] = bits72[channel_position(i)] & 1;
uint32_t word = 0;
for (int i = 0; i < 23; i++) word = (word << 1) | code[i];
int e0, e1;
uint32_t d0 = golay23_decode(word, &e0);
uint32_t corrected0 = (golay23_parity(d0) & 0x7ff) | (d0 << 11);
uint32_t parity = popcount(corrected0 & 0x7fffff) & 1;
word = 0;
for (int i = 24; i < 47; i++) word = (word << 1) | code[i];
uint32_t d1 = golay23_decode(fec_scramble_mask(d0) ^ word, &e1);
for (int i = 0; i < 12; i++) bits49[i] = (d0 >> (11 - i)) & 1;
for (int i = 0; i < 12; i++) bits49[12 + i] = (d1 >> (11 - i)) & 1;
std::memcpy(bits49 + 24, code + 47, 25);
if (errors) {
errors->first_word = e0;
errors->second_word = e1;
}
return e0 >= 3 && parity != code[23] ? -1 : e1 + e0;
}
void fec_encode_frame(const uint8_t* frame, uint8_t* air) {
uint8_t bits49[49], bits72[72];
for (int i = 0; i < 49; i++) bits49[i] = (frame[i >> 3] >> (7 - (i & 7))) & 1;
fec_encode_bits(bits49, bits72);
std::memset(air, 0, 9);
for (int i = 0; i < 72; i++) air[i >> 3] |= static_cast<uint8_t>(bits72[i] << (7 - (i & 7)));
}
int fec_decode_frame(const uint8_t* air, uint8_t* frame, FecErrors* errors) {
uint8_t bits72[72], bits49[49];
for (int i = 0; i < 72; i++) bits72[i] = (air[i >> 3] >> (7 - (i & 7))) & 1;
int result = fec_decode_bits(bits72, bits49, errors);
std::memset(frame, 0, 7);
for (int i = 0; i < 49; i++) frame[i >> 3] |= static_cast<uint8_t>(bits49[i] << (7 - (i & 7)));
return result;
}
} // namespace halfrate::core
+41
View File
@@ -0,0 +1,41 @@
// Forward error correction of the 49-bit frame into the 72-bit DMR on-air frame.
//
// The 72 code bits are: a Golay (24,12) word of frame bits 0..11 (the (23,12) code plus an overall parity bit),
// a Golay (23,12) word of frame bits 12..23 XORed with a 23-bit scrambling sequence seeded by bits 0..11, and frame
// bits 24..48 uncoded. Code bit i goes to channel bit 4 i - 71 floor(i / 18) (the DMR 4 x 18 interleave).
// Bit arrays hold one bit per byte (only bit 0 is read); byte frames are MSB first.
#pragma once
#include <cstdint>
namespace halfrate::core {
// The 11 parity bits of 12 data bits.
uint32_t golay23_parity(uint32_t data);
// Decodes a (23,12) code word (data in bits 22..11, parity in bits 10..0) and returns the 12 corrected data bits.
// If errors is given it receives the corrected data bits plus the parity mismatches left after correction.
uint32_t golay23_decode(uint32_t code, int* errors);
// 23-bit scrambling mask of the second Golay word, from the first word's 12 data bits.
uint32_t fec_scramble_mask(uint32_t first_data);
// Error counts of one decoded frame.
struct FecErrors {
int first_word = 0; // errors of the Golay (24,12) word (frame bits 0..11)
int second_word = 0; // errors of the scrambled Golay (23,12) word (frame bits 12..23)
};
// 49 frame bits -> 72 channel bits.
void fec_encode_bits(const uint8_t* bits49, uint8_t* bits72);
// 72 channel bits -> 49 frame bits. Returns first_word + second_word, or -1 (frame unreliable) when the first
// word had 3 errors or more and its overall parity bit disagrees with the corrected word.
int fec_decode_bits(const uint8_t* bits72, uint8_t* bits49, FecErrors* errors);
// 7-byte frame -> 9-byte on-air frame.
void fec_encode_frame(const uint8_t* frame, uint8_t* air);
// 9-byte on-air frame -> 7-byte frame. Returns what fec_decode_bits returns; errors as there (may be null).
int fec_decode_frame(const uint8_t* air, uint8_t* frame, FecErrors* errors);
} // namespace halfrate::core
+49
View File
@@ -0,0 +1,49 @@
#include "fft.h"
#include "tables.h"
namespace halfrate::core {
void fft512(float* re, float* im) {
// bit-reversal permutation
unsigned j = 0;
for (int i = 0; i < 512; i++) {
if (i < static_cast<int>(j)) {
float r = re[i], m = im[i];
re[i] = re[j];
im[i] = im[j];
re[j] = r;
im[j] = m;
}
unsigned bit = 0x100;
while (bit && (j & bit)) {
j ^= bit;
bit >>= 1;
}
j |= bit;
}
const float* cosine = tables::kCosine;
for (int size = 2, shift = 1; size <= 512; size <<= 1, shift++) {
int half = size >> 1;
unsigned step = 8192u >> shift; // table entries per twiddle step
for (int start = 0; start < 512; start += size) {
for (int k = 0; k < half; k++) {
float c = cosine[k * step];
float s = cosine[(6144 + k * step) & 8191]; // sin
int top = start + k, bottom = start + half + k;
float xr = re[bottom], xi = im[bottom];
// t = x e^-j(angle) = (c xr + s xi) + j (c xi - s xr)
float tr = c * xr;
tr = tr + s * xi;
float ti = c * xi;
ti = ti - s * xr;
re[bottom] = re[top] - tr;
im[bottom] = im[top] - ti;
re[top] = tr + re[top];
im[top] = ti + im[top];
}
}
}
}
} // namespace halfrate::core
+10
View File
@@ -0,0 +1,10 @@
// 512-point complex FFT.
#pragma once
namespace halfrate::core {
// In-place forward transform (kernel e^-j), unscaled, of re[512] + j im[512]: radix 2, decimation in time, with
// twiddles from the 8192-entry cosine table.
void fft512(float* re, float* im);
} // namespace halfrate::core
+54
View File
@@ -0,0 +1,54 @@
#include "frame.h"
#include <cstring>
#include "tables.h"
namespace halfrate::core {
void bits_from_bytes(const uint8_t* frame, uint8_t* bits49) {
for (int i = 0; i < kFrameBits; i++) bits49[i] = (frame[i >> 3] >> (7 - (i & 7))) & 1;
}
void bytes_from_bits(const uint8_t* bits49, uint8_t* frame) {
std::memset(frame, 0, kFrameBytes);
for (int i = 0; i < kFrameBits; i++) frame[i >> 3] |= static_cast<uint8_t>((bits49[i] & 1) << (7 - (i & 7)));
}
FrameFields fields_from_bits(const uint8_t* bits49) {
FrameFields fields{};
for (int f = 0; f < kFieldCount; f++) {
uint32_t v = 0;
for (int k = 0; k < tables::kFieldWidth[f]; k++) v = (v << 1) | (bits49[tables::kFieldBitPosition[f][k]] & 1);
fields[f] = static_cast<int32_t>(v);
}
return fields;
}
void bits_from_fields(const FrameFields& fields, uint8_t* bits49) {
for (int f = 0; f < kFieldCount; f++) {
int width = tables::kFieldWidth[f];
uint32_t v = static_cast<uint32_t>(fields[f]);
for (int k = 0; k < width; k++) bits49[tables::kFieldBitPosition[f][k]] = (v >> (width - 1 - k)) & 1;
}
}
void standard_from_rotated(const uint8_t* rotated49, uint8_t* standard49) {
uint8_t b40 = rotated49[40], b41 = rotated49[41], b42 = rotated49[42], b43 = rotated49[43];
std::memmove(standard49, rotated49, kFrameBits);
standard49[40] = b43;
standard49[41] = b40;
standard49[42] = b41;
standard49[43] = b42;
}
void rotated_from_standard(const uint8_t* standard49, uint8_t* rotated49) {
uint8_t b40 = standard49[40], b41 = standard49[41], b42 = standard49[42], b43 = standard49[43];
std::memmove(rotated49, standard49, kFrameBits);
rotated49[40] = b41;
rotated49[41] = b42;
rotated49[42] = b43;
rotated49[43] = b40;
}
} // namespace halfrate::core
+50
View File
@@ -0,0 +1,50 @@
// The 49-bit frame: nine parameter fields at fixed bit positions.
//
// field bits meaning
// 0 7 pitch index (0..119 voice; 120..123 erasure; 124..125 tone/fixed pitch; 126..127 reserved)
// 1 5 voicing index (16/17 silence, 18 and up pulse codes, see the decoder)
// 2 5 gain index
// 3 9 shape, 8-point transform coefficients 1..3
// 4 7 shape, 8-point transform coefficients 4..7
// 5..8 5 4 4 3 higher-order shape coefficients of blocks 1..4
//
// Field bits are spread over the frame (tables::kFieldBitPosition). A bit array holds one bit per byte, frame
// bit 0 first; a byte frame is 7 bytes, MSB first, the last byte holding bit 48 in its top bit.
//
// The rotated layout (a decoder/encoder option) is the same frame with bits 40..43 rotated: what the standard
// layout has at 40, 41, 42, 43 is sent at 43, 40, 41, 42.
#pragma once
#include <array>
#include <cstdint>
namespace halfrate::core {
constexpr int kFrameBits = 49;
constexpr int kFrameBytes = 7;
constexpr int kFieldCount = 9;
// Field indices
enum Field : int {
kFieldPitch = 0,
kFieldVoicing = 1,
kFieldGain = 2,
kFieldShapeLow = 3,
kFieldShapeHigh = 4,
kFieldDetail1 = 5, // 5..8: blocks 1..4
};
using FrameFields = std::array<int32_t, kFieldCount>;
// 7 bytes <-> 49 bits
void bits_from_bytes(const uint8_t* frame, uint8_t* bits49);
void bytes_from_bits(const uint8_t* bits49, uint8_t* frame);
// 49 bits <-> the nine fields (standard layout). bits_from_fields writes every bit of the frame.
FrameFields fields_from_bits(const uint8_t* bits49);
void bits_from_fields(const FrameFields& fields, uint8_t* bits49);
// Rotated layout <-> standard layout (bits 40..43); the other bits are copied.
void standard_from_rotated(const uint8_t* rotated49, uint8_t* standard49);
void rotated_from_standard(const uint8_t* standard49, uint8_t* rotated49);
} // namespace halfrate::core
+21
View File
@@ -0,0 +1,21 @@
// The 49-bit frame layout: width of each of the nine parameter fields, and the frame bit that carries each field
// bit, most significant first (unused entries are 0). Bits 0..23 are the two Golay-protected words.
#include "tables.h"
namespace halfrate::tables {
const int32_t kFieldWidth[9] = {7, 5, 5, 9, 7, 5, 4, 4, 3};
const int8_t kFieldBitPosition[9][9] = {
{0, 1, 2, 3, 37, 38, 39, 0, 0},
{4, 5, 6, 7, 35, 0, 0, 0, 0},
{8, 9, 10, 11, 36, 0, 0, 0, 0},
{12, 13, 14, 15, 16, 17, 18, 19, 40},
{20, 21, 22, 23, 41, 42, 43, 0, 0},
{24, 25, 26, 27, 44, 0, 0, 0, 0},
{28, 29, 30, 45, 0, 0, 0, 0, 0},
{31, 32, 33, 46, 0, 0, 0, 0, 0},
{34, 47, 48, 0, 0, 0, 0, 0, 0},
};
} // namespace halfrate::tables
+272
View File
@@ -0,0 +1,272 @@
// Generated table data. Do not edit.
#include "tables.h"
namespace halfrate::tables {
// Golay (23,12): the 11 parity bits contributed by each data bit, most significant data bit first.
const uint32_t kGolayParityRows[12] = {
0x63a, 0x31d, 0x7b4, 0x3da, 0x1ed, 0x6cc,
0x366, 0x1b3, 0x6e3, 0x54b, 0x49f, 0x475,
};
// Golay (23,12): error pattern for each 11-bit syndrome; the low 12 bits correct the data bits.
const uint32_t kGolaySyndromeCorrection[2048] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x824,
0x0, 0x0, 0x0, 0x301, 0x0, 0x400, 0x90, 0x2,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48,
0x0, 0x0, 0x0, 0x48, 0x0, 0x48, 0x48, 0x48,
0x0, 0x0, 0x0, 0x10, 0x0, 0x1, 0x602, 0x180,
0x0, 0x86, 0x800, 0x420, 0x120, 0xa10, 0x5, 0x48,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x500,
0x0, 0x0, 0x0, 0x4, 0x0, 0x222, 0x90, 0x801,
0x0, 0x0, 0x0, 0x42, 0x0, 0x1, 0x90, 0x208,
0x0, 0x808, 0x90, 0x420, 0x90, 0x144, 0x90, 0x90,
0x0, 0x0, 0x0, 0xa80, 0x0, 0x1, 0x20, 0x16,
0x0, 0x110, 0x3, 0x420, 0xc04, 0x80, 0x300, 0x48,
0x0, 0x1, 0x10c, 0x420, 0x1, 0x1, 0x840, 0x1,
0x240, 0x420, 0x420, 0x420, 0xa, 0x1, 0x90, 0x420,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x500,
0x0, 0x0, 0x0, 0xa0, 0x0, 0x15, 0xa00, 0x2,
0x0, 0x0, 0x0, 0x10, 0x0, 0x2c0, 0x9, 0x2,
0x0, 0x808, 0x444, 0x2, 0x120, 0x2, 0x2, 0x2,
0x0, 0x0, 0x0, 0x10, 0x0, 0x802, 0x84, 0x221,
0x0, 0x600, 0x3, 0x904, 0x120, 0x80, 0x410, 0x48,
0x0, 0x10, 0x10, 0x10, 0x120, 0x40c, 0x840, 0x10,
0x120, 0x41, 0x288, 0x10, 0x120, 0x120, 0x120, 0x2,
0x0, 0x0, 0x0, 0x500, 0x0, 0x500, 0x500, 0x500,
0x0, 0x808, 0x3, 0x250, 0x40, 0x80, 0x2c, 0x500,
0x0, 0x808, 0x220, 0x85, 0x6, 0x30, 0x840, 0x500,
0x808, 0x808, 0x100, 0x808, 0x601, 0x808, 0x90, 0x2,
0x0, 0x64, 0x3, 0x8, 0x218, 0x80, 0x840, 0x500,
0x3, 0x80, 0x3, 0x3, 0x80, 0x80, 0x3, 0x80,
0x480, 0x302, 0x840, 0x10, 0x840, 0x1, 0x840, 0x840,
0x14, 0x808, 0x3, 0x420, 0x120, 0x80, 0x840, 0x204,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83,
0x0, 0x0, 0x0, 0x4, 0x0, 0x400, 0xa00, 0x130,
0x0, 0x0, 0x0, 0x10, 0x0, 0x400, 0x140, 0x208,
0x0, 0x400, 0x2a, 0x8c0, 0x400, 0x400, 0x5, 0x400,
0x0, 0x0, 0x0, 0x10, 0x0, 0x304, 0x20, 0xc00,
0x0, 0x821, 0x580, 0x202, 0x12, 0x80, 0x5, 0x48,
0x0, 0x10, 0x10, 0x10, 0x888, 0x62, 0x5, 0x10,
0x240, 0x108, 0x5, 0x10, 0x5, 0x400, 0x5, 0x5,
0x0, 0x0, 0x0, 0x4, 0x0, 0x850, 0x20, 0x208,
0x0, 0x4, 0x4, 0x4, 0x109, 0x80, 0x442, 0x4,
0x0, 0x1a0, 0xc01, 0x208, 0x6, 0x208, 0x208, 0x208,
0x240, 0x13, 0x100, 0x4, 0x820, 0x400, 0x90, 0x208,
0x0, 0x40a, 0x20, 0x141, 0x20, 0x80, 0x20, 0x20,
0x240, 0x80, 0x818, 0x4, 0x80, 0x80, 0x20, 0x80,
0x240, 0x804, 0x82, 0x10, 0x510, 0x1, 0x20, 0x208,
0x240, 0x240, 0x240, 0x420, 0x240, 0x80, 0x5, 0x902,
0x0, 0x0, 0x0, 0x10, 0x0, 0x28, 0xa00, 0x44,
0x0, 0x142, 0xa00, 0x409, 0xa00, 0x80, 0xa00, 0xa00,
0x0, 0x10, 0x10, 0x10, 0x6, 0x901, 0x4a0, 0x10,
0x81, 0x224, 0x100, 0x10, 0x58, 0x400, 0xa00, 0x2,
0x0, 0x10, 0x10, 0x10, 0x441, 0x80, 0x10a, 0x10,
0xc, 0x80, 0x60, 0x10, 0x80, 0x80, 0xa00, 0x80,
0x10, 0x10, 0x10, 0x10, 0x200, 0x10, 0x10, 0x10,
0xc02, 0x10, 0x10, 0x10, 0x120, 0x80, 0x5, 0x10,
0x0, 0x201, 0xc8, 0x822, 0x6, 0x80, 0x11, 0x500,
0x430, 0x80, 0x100, 0x4, 0x80, 0x80, 0xa00, 0x80,
0x6, 0x440, 0x100, 0x10, 0x6, 0x6, 0x6, 0x208,
0x100, 0x808, 0x100, 0x100, 0x6, 0x80, 0x100, 0x61,
0x900, 0x80, 0x604, 0x10, 0x80, 0x80, 0x20, 0x80,
0x80, 0x80, 0x3, 0x80, 0x80, 0x80, 0x80, 0x80,
0x29, 0x10, 0x10, 0x10, 0x6, 0x80, 0x840, 0x10,
0x240, 0x80, 0x100, 0x10, 0x80, 0x80, 0x408, 0x80,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210,
0x0, 0x0, 0x0, 0xa0, 0x0, 0x400, 0x106, 0x801,
0x0, 0x0, 0x0, 0x42, 0x0, 0x400, 0x9, 0x180,
0x0, 0x400, 0x800, 0x1c, 0x400, 0x400, 0x260, 0x400,
0x0, 0x0, 0x0, 0x405, 0x0, 0x802, 0x20, 0x180,
0x0, 0x110, 0x800, 0x202, 0x281, 0x24, 0x410, 0x48,
0x0, 0x228, 0x800, 0x180, 0x54, 0x180, 0x180, 0x180,
0x800, 0x41, 0x800, 0x800, 0xa, 0x400, 0x800, 0x180,
0x0, 0x0, 0x0, 0x42, 0x0, 0x8c, 0x20, 0x801,
0x0, 0x110, 0x608, 0x801, 0x40, 0x801, 0x801, 0x801,
0x0, 0x42, 0x42, 0x42, 0xb00, 0x30, 0x404, 0x42,
0x25, 0x280, 0x100, 0x42, 0xa, 0x400, 0x90, 0x801,
0x0, 0x110, 0x20, 0x8, 0x20, 0x640, 0x20, 0x20,
0x110, 0x110, 0xc4, 0x110, 0xa, 0x110, 0x20, 0x801,
0x480, 0x804, 0x211, 0x42, 0xa, 0x1, 0x20, 0x180,
0xa, 0x110, 0x800, 0x420, 0xa, 0xa, 0xa, 0x204,
0x0, 0x0, 0x0, 0xa0, 0x0, 0x802, 0x9, 0x44,
0x0, 0xa0, 0xa0, 0xa0, 0x40, 0x308, 0x410, 0xa0,
0x0, 0x104, 0x9, 0xe00, 0x9, 0x30, 0x9, 0x9,
0x212, 0x41, 0x100, 0xa0, 0x884, 0x400, 0x9, 0x2,
0x0, 0x802, 0x340, 0x8, 0x802, 0x802, 0x410, 0x802,
0xc, 0x41, 0x410, 0xa0, 0x410, 0x802, 0x410, 0x410,
0x480, 0x41, 0x26, 0x10, 0x200, 0x802, 0x9, 0x180,
0x41, 0x41, 0x800, 0x41, 0x120, 0x41, 0x410, 0x204,
0x0, 0x201, 0x814, 0x8, 0x40, 0x30, 0x282, 0x500,
0x40, 0x406, 0x100, 0xa0, 0x40, 0x40, 0x40, 0x801,
0x480, 0x30, 0x100, 0x42, 0x30, 0x30, 0x9, 0x30,
0x100, 0x808, 0x100, 0x100, 0x40, 0x30, 0x100, 0x204,
0x480, 0x8, 0x8, 0x8, 0x105, 0x802, 0x20, 0x8,
0xa20, 0x110, 0x3, 0x8, 0x40, 0x80, 0x410, 0x204,
0x480, 0x480, 0x480, 0x8, 0x480, 0x30, 0x840, 0x204,
0x480, 0x41, 0x100, 0x204, 0xa, 0x204, 0x204, 0x204,
0x0, 0x0, 0x0, 0x908, 0x0, 0x400, 0x20, 0x44,
0x0, 0x400, 0x51, 0x202, 0x400, 0x400, 0x88, 0x400,
0x0, 0x400, 0x284, 0x21, 0x400, 0x400, 0x812, 0x400,
0x400, 0x400, 0x100, 0x400, 0x400, 0x400, 0x400, 0x400,
0x0, 0xc0, 0x20, 0x202, 0x20, 0x19, 0x20, 0x20,
0xc, 0x202, 0x202, 0x202, 0x940, 0x400, 0x20, 0x202,
0x103, 0x804, 0x448, 0x10, 0x200, 0x400, 0x20, 0x180,
0xb0, 0x400, 0x800, 0x202, 0x400, 0x400, 0x5, 0x400,
0x0, 0x201, 0x20, 0x490, 0x20, 0x102, 0x20, 0x20,
0x882, 0x68, 0x100, 0x4, 0x214, 0x400, 0x20, 0x801,
0x18, 0x804, 0x100, 0x42, 0xc1, 0x400, 0x20, 0x208,
0x100, 0x400, 0x100, 0x100, 0x400, 0x400, 0x100, 0x400,
0x20, 0x804, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x401, 0x110, 0x20, 0x202, 0x20, 0x80, 0x20, 0x20,
0x804, 0x804, 0x20, 0x804, 0x20, 0x804, 0x20, 0x20,
0x240, 0x804, 0x100, 0x89, 0xa, 0x400, 0x20, 0x50,
0x0, 0x201, 0x402, 0x44, 0x190, 0x44, 0x44, 0x44,
0xc, 0x810, 0x100, 0xa0, 0x23, 0x400, 0xa00, 0x44,
0x860, 0x8a, 0x100, 0x10, 0x200, 0x400, 0x9, 0x44,
0x100, 0x400, 0x100, 0x100, 0x400, 0x400, 0x100, 0x400,
0xc, 0x520, 0x881, 0x10, 0x200, 0x802, 0x20, 0x44,
0xc, 0xc, 0xc, 0x202, 0xc, 0x80, 0x410, 0x101,
0x200, 0x10, 0x10, 0x10, 0x200, 0x200, 0x200, 0x10,
0xc, 0x41, 0x100, 0x10, 0x200, 0x400, 0xc2, 0x828,
0x201, 0x201, 0x100, 0x201, 0xc08, 0x201, 0x20, 0x44,
0x100, 0x201, 0x100, 0x100, 0x40, 0x80, 0x100, 0x1a,
0x100, 0x201, 0x100, 0x100, 0x6, 0x30, 0x100, 0x880,
0x100, 0x100, 0x100, 0x100, 0x100, 0x400, 0x100, 0x100,
0x52, 0x201, 0x20, 0x8, 0x20, 0x80, 0x20, 0x20,
0xc, 0x80, 0x100, 0xc40, 0x80, 0x80, 0x20, 0x80,
0x480, 0x804, 0x100, 0x10, 0x200, 0x148, 0x20, 0x403,
0x100, 0x22, 0x100, 0x100, 0x811, 0x80, 0x100, 0x204,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210,
0x0, 0x0, 0x0, 0x4, 0x0, 0x980, 0x421, 0x2,
0x0, 0x0, 0x0, 0x488, 0x0, 0x1, 0x140, 0x2,
0x0, 0x70, 0x800, 0x2, 0x20c, 0x2, 0x2, 0x2,
0x0, 0x0, 0x0, 0x122, 0x0, 0x1, 0x84, 0xc00,
0x0, 0x600, 0x800, 0x91, 0x12, 0x24, 0x300, 0x48,
0x0, 0x1, 0x800, 0x244, 0x1, 0x1, 0x38, 0x1,
0x800, 0x108, 0x800, 0x800, 0x4c0, 0x1, 0x800, 0x2,
0x0, 0x0, 0x0, 0x4, 0x0, 0x1, 0x80a, 0xe0,
0x0, 0x4, 0x4, 0x4, 0x40, 0x418, 0x300, 0x4,
0x0, 0x1, 0x220, 0x910, 0x1, 0x1, 0x404, 0x1,
0x502, 0x280, 0x49, 0x4, 0x820, 0x1, 0x90, 0x2,
0x0, 0x1, 0x450, 0x8, 0x1, 0x1, 0x300, 0x1,
0xa8, 0x842, 0x300, 0x4, 0x300, 0x1, 0x300, 0x300,
0x1, 0x1, 0x82, 0x1, 0x1, 0x1, 0x1, 0x1,
0x14, 0x1, 0x800, 0x420, 0x1, 0x1, 0x300, 0x1,
0x0, 0x0, 0x0, 0x841, 0x0, 0x28, 0x84, 0x2,
0x0, 0x600, 0x118, 0x2, 0x40, 0x2, 0x2, 0x2,
0x0, 0x104, 0x220, 0x2, 0xc10, 0x2, 0x2, 0x2,
0x81, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x0, 0x600, 0x84, 0x8, 0x84, 0x150, 0x84, 0x84,
0x600, 0x600, 0x60, 0x600, 0x809, 0x600, 0x84, 0x2,
0x4a, 0x8a0, 0x501, 0x10, 0x200, 0x1, 0x84, 0x2,
0x14, 0x600, 0x800, 0x2, 0x120, 0x2, 0x2, 0x2,
0x0, 0x92, 0x220, 0x8, 0x40, 0xa04, 0x11, 0x500,
0x40, 0x121, 0xc80, 0x4, 0x40, 0x40, 0x40, 0x2,
0x220, 0x440, 0x220, 0x220, 0x188, 0x1, 0x220, 0x2,
0x14, 0x808, 0x220, 0x2, 0x40, 0x2, 0x2, 0x2,
0x900, 0x8, 0x8, 0x8, 0x422, 0x1, 0x84, 0x8,
0x14, 0x600, 0x3, 0x8, 0x40, 0x80, 0x300, 0x830,
0x14, 0x1, 0x220, 0x8, 0x1, 0x1, 0x840, 0x1,
0x14, 0x14, 0x14, 0x1c0, 0x14, 0x1, 0x408, 0x2,
0x0, 0x0, 0x0, 0x4, 0x0, 0x28, 0x140, 0xc00,
0x0, 0x4, 0x4, 0x4, 0x12, 0x241, 0x88, 0x4,
0x0, 0xa02, 0x140, 0x21, 0x140, 0x94, 0x140, 0x140,
0x81, 0x108, 0x610, 0x4, 0x820, 0x400, 0x140, 0x2,
0x0, 0xc0, 0x209, 0xc00, 0x12, 0xc00, 0xc00, 0xc00,
0x12, 0x108, 0x60, 0x4, 0x12, 0x12, 0x12, 0xc00,
0x424, 0x108, 0x82, 0x10, 0x200, 0x1, 0x140, 0xc00,
0x108, 0x108, 0x800, 0x108, 0x12, 0x108, 0x5, 0x2a0,
0x0, 0x4, 0x4, 0x4, 0x680, 0x102, 0x11, 0x4,
0x4, 0x4, 0x4, 0x4, 0x820, 0x4, 0x4, 0x4,
0x18, 0x440, 0x82, 0x4, 0x820, 0x1, 0x140, 0x208,
0x820, 0x4, 0x4, 0x4, 0x820, 0x820, 0x820, 0x4,
0x900, 0x230, 0x82, 0x4, 0x4c, 0x1, 0x20, 0xc00,
0x401, 0x4, 0x4, 0x4, 0x12, 0x80, 0x300, 0x4,
0x82, 0x1, 0x82, 0x82, 0x1, 0x1, 0x82, 0x1,
0x240, 0x108, 0x82, 0x4, 0x820, 0x1, 0x408, 0x50,
0x0, 0x28, 0x402, 0x380, 0x28, 0x28, 0x11, 0x28,
0x81, 0x810, 0x60, 0x4, 0x504, 0x28, 0xa00, 0x2,
0x81, 0x440, 0x80c, 0x10, 0x200, 0x28, 0x140, 0x2,
0x81, 0x81, 0x81, 0x2, 0x81, 0x2, 0x2, 0x2,
0x900, 0x7, 0x60, 0x10, 0x200, 0x28, 0x84, 0xc00,
0x60, 0x600, 0x60, 0x60, 0x12, 0x80, 0x60, 0x101,
0x200, 0x10, 0x10, 0x10, 0x200, 0x200, 0x200, 0x10,
0x81, 0x108, 0x60, 0x10, 0x200, 0x844, 0x408, 0x2,
0x900, 0x440, 0x11, 0x4, 0x11, 0x28, 0x11, 0x11,
0x20a, 0x4, 0x4, 0x4, 0x40, 0x80, 0x11, 0x4,
0x440, 0x440, 0x220, 0x440, 0x6, 0x440, 0x11, 0x880,
0x81, 0x440, 0x100, 0x4, 0x820, 0x310, 0x408, 0x2,
0x900, 0x900, 0x900, 0x8, 0x900, 0x80, 0x11, 0x242,
0x900, 0x80, 0x60, 0x4, 0x80, 0x80, 0x408, 0x80,
0x900, 0x440, 0x82, 0x10, 0x200, 0x1, 0x408, 0x124,
0x14, 0x22, 0x408, 0xa01, 0x408, 0x80, 0x408, 0x408,
0x0, 0x0, 0x0, 0x210, 0x0, 0x210, 0x210, 0x210,
0x0, 0xb, 0x800, 0x540, 0x40, 0x24, 0x88, 0x210,
0x0, 0x104, 0x800, 0x21, 0xa2, 0x848, 0x404, 0x210,
0x800, 0x280, 0x800, 0x800, 0x111, 0x400, 0x800, 0x2,
0x0, 0xc0, 0x800, 0x8, 0x508, 0x24, 0x43, 0x210,
0x800, 0x24, 0x800, 0x800, 0x24, 0x24, 0x800, 0x24,
0x800, 0x412, 0x800, 0x800, 0x200, 0x1, 0x800, 0x180,
0x800, 0x800, 0x800, 0x800, 0x800, 0x24, 0x800, 0x800,
0x0, 0xc20, 0x181, 0x8, 0x40, 0x102, 0x404, 0x210,
0x40, 0x280, 0x32, 0x4, 0x40, 0x40, 0x40, 0x801,
0x18, 0x280, 0x404, 0x42, 0x404, 0x1, 0x404, 0x404,
0x280, 0x280, 0x800, 0x280, 0x40, 0x280, 0x404, 0x128,
0x206, 0x8, 0x8, 0x8, 0x890, 0x1, 0x20, 0x8,
0x401, 0x110, 0x800, 0x8, 0x40, 0x24, 0x300, 0x482,
0x160, 0x1, 0x800, 0x8, 0x1, 0x1, 0x404, 0x1,
0x800, 0x280, 0x800, 0x800, 0xa, 0x1, 0x800, 0x50,
0x0, 0x104, 0x402, 0x8, 0x40, 0x481, 0x920, 0x210,
0x40, 0x810, 0x205, 0xa0, 0x40, 0x40, 0x40, 0x2,
0x104, 0x104, 0xd0, 0x104, 0x200, 0x104, 0x9, 0x2,
0x428, 0x104, 0x800, 0x2, 0x40, 0x2, 0x2, 0x2,
0x31, 0x8, 0x8, 0x8, 0x200, 0x802, 0x84, 0x8,
0x182, 0x600, 0x800, 0x8, 0x40, 0x24, 0x410, 0x101,
0x200, 0x104, 0x800, 0x8, 0x200, 0x200, 0x200, 0x460,
0x800, 0x41, 0x800, 0x800, 0x200, 0x98, 0x800, 0x2,
0x40, 0x8, 0x8, 0x8, 0x40, 0x40, 0x40, 0x8,
0x40, 0x40, 0x40, 0x8, 0x40, 0x40, 0x40, 0x40,
0x803, 0x104, 0x220, 0x8, 0x40, 0x30, 0x404, 0x880,
0x40, 0x280, 0x100, 0x411, 0x40, 0x40, 0x40, 0x2,
0x8, 0x8, 0x8, 0x8, 0x40, 0x8, 0x8, 0x8,
0x40, 0x8, 0x8, 0x8, 0x40, 0x40, 0x40, 0x8,
0x480, 0x8, 0x8, 0x8, 0x200, 0x1, 0x112, 0x8,
0x14, 0x22, 0x800, 0x8, 0x40, 0xd00, 0xa1, 0x204,
0x0, 0xc0, 0x402, 0x21, 0x805, 0x102, 0x88, 0x210,
0x320, 0x810, 0x88, 0x4, 0x88, 0x400, 0x88, 0x88,
0x18, 0x21, 0x21, 0x21, 0x200, 0x400, 0x140, 0x21,
0x46, 0x400, 0x800, 0x21, 0x400, 0x400, 0x88, 0x400,
0xc0, 0xc0, 0x114, 0xc0, 0x200, 0xc0, 0x20, 0xc00,
0x401, 0xc0, 0x800, 0x202, 0x12, 0x24, 0x88, 0x101,
0x200, 0xc0, 0x800, 0x21, 0x200, 0x200, 0x200, 0xe,
0x800, 0x108, 0x800, 0x800, 0x200, 0x400, 0x800, 0x50,
0x18, 0x102, 0xa40, 0x4, 0x102, 0x102, 0x20, 0x102,
0x401, 0x4, 0x4, 0x4, 0x40, 0x102, 0x88, 0x4,
0x18, 0x18, 0x18, 0x21, 0x18, 0x102, 0x404, 0x880,
0x18, 0x280, 0x100, 0x4, 0x820, 0x400, 0x203, 0x50,
0x401, 0xc0, 0x20, 0x8, 0x20, 0x102, 0x20, 0x20,
0x401, 0x401, 0x401, 0x4, 0x401, 0xa08, 0x20, 0x50,
0x18, 0x804, 0x82, 0x700, 0x200, 0x1, 0x20, 0x50,
0x401, 0x22, 0x800, 0x50, 0x184, 0x50, 0x50, 0x50,
0x402, 0x810, 0x402, 0x402, 0x200, 0x28, 0x402, 0x44,
0x810, 0x810, 0x402, 0x810, 0x40, 0x810, 0x88, 0x101,
0x200, 0x104, 0x402, 0x21, 0x200, 0x200, 0x200, 0x880,
0x81, 0x810, 0x100, 0x248, 0x200, 0x400, 0x34, 0x2,
0x200, 0xc0, 0x402, 0x8, 0x200, 0x200, 0x200, 0x101,
0xc, 0x810, 0x60, 0x101, 0x200, 0x101, 0x101, 0x101,
0x200, 0x200, 0x200, 0x10, 0x200, 0x200, 0x200, 0x200,
0x200, 0x22, 0x800, 0x484, 0x200, 0x200, 0x200, 0x101,
0xa4, 0x201, 0x402, 0x8, 0x40, 0x102, 0x11, 0x880,
0x40, 0x810, 0x100, 0x4, 0x40, 0x40, 0x40, 0x620,
0x18, 0x440, 0x100, 0x880, 0x200, 0x880, 0x880, 0x880,
0x100, 0x22, 0x100, 0x100, 0x40, 0xd, 0x100, 0x880,
0x900, 0x8, 0x8, 0x8, 0x200, 0x414, 0x20, 0x8,
0x401, 0x22, 0x290, 0x8, 0x40, 0x80, 0x806, 0x101,
0x200, 0x22, 0x45, 0x8, 0x200, 0x200, 0x200, 0x880,
0x22, 0x22, 0x100, 0x22, 0x200, 0x22, 0x408, 0x50,
};
} // namespace halfrate::tables
+67
View File
@@ -0,0 +1,67 @@
/* halfrate: AMBE+2 compatible half-rate (2450 bit/s voice, DMR) speech encoder and decoder.
*
* Audio is 8 kHz, 16-bit mono, 160 samples (20 ms) per frame. A frame is 49 bits; the API carries it as 7 bytes,
* MSB first (the last byte holds one bit, in its top bit). hr_fec_encode / hr_fec_decode turn it into the 9-byte
* on-air frame with FEC and interleave that DMR carries. hr_bits_from_frame / hr_frame_from_bits convert to and
* from one bit per byte (on-air order), as other codecs' APIs use.
*
* One encoder or decoder per stream: both keep state from frame to frame. Objects are independent and may run on
* separate threads; one object is not thread-safe.
*/
#ifndef HALFRATE_H
#define HALFRATE_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define HR_SAMPLES 160
#define HR_FRAME_BYTES 7
#define HR_AIR_BYTES 9
#define HR_BITS 49
typedef struct hr_encoder hr_encoder;
typedef struct hr_decoder hr_decoder;
hr_encoder *hr_encoder_new(void);
void hr_encoder_free(hr_encoder *e);
/* Sets a tuning option by name (docs/options.md). Returns 0, or -1 for an unknown name or a NaN or infinite
* value (the option is then left as it was). */
int hr_encoder_set(hr_encoder *e, const char *name, double value);
/* pcm: 160 samples in, frame: 7 bytes out. The output runs HR_ENCODER_DELAY frames behind the input. */
void hr_encode(hr_encoder *e, const int16_t *pcm, uint8_t *frame);
#define HR_ENCODER_DELAY 2
hr_decoder *hr_decoder_new(void);
void hr_decoder_free(hr_decoder *d);
/* Sets an option by name (docs/options.md). Returns 0, or -1 for an unknown name or a NaN or infinite value
* (the option is then left as it was). */
int hr_decoder_set(hr_decoder *d, const char *name, double value);
/* frame: 7 bytes in, pcm: 160 samples out (must not be null). Returns the frame kind (HR_VOICE, ...), or -1 if d is
* null.
* frame may be null for a frame that was not received: it is concealed like an erasure. */
int hr_decode(hr_decoder *d, const uint8_t *frame, int16_t *pcm);
/* Frame kinds hr_decode returns */
#define HR_VOICE 0 /* speech */
#define HR_SILENCE 1 /* a silence frame (background level) */
#define HR_TONE 2 /* a tone frame: single, dual (keypad) or call-progress tone */
#define HR_ERASURE 3 /* not decodable: concealed by repeating the last frame, then muting, then comfort noise */
/* The same on a 9-byte on-air frame: FEC decode, then hr_decode. A null air frame is concealed. */
int hr_decode_air(hr_decoder *d, const uint8_t *air, int16_t *pcm);
/* 7-byte frame <-> 9-byte on-air frame. hr_fec_decode returns the number of bit errors corrected, or -1 when the
* first code word has more errors than it can correct (the frame is still written, as decoded). The frame helpers'
* pointers must not be null. */
void hr_fec_encode(const uint8_t *frame, uint8_t *air);
int hr_fec_decode(const uint8_t *air, uint8_t *frame);
/* 7-byte frame <-> 49 bytes of one bit each */
void hr_bits_from_frame(const uint8_t *frame, uint8_t *bits);
void hr_frame_from_bits(const uint8_t *bits, uint8_t *frame);
const char *hr_version(void);
#ifdef __cplusplus
}
#endif
#endif
+52
View File
@@ -0,0 +1,52 @@
// The encoder's input history and the analysis of its last three frames, kept together in one block.
//
// The analysis reads the input around a position set by two options (pitch_window_offset and
// amplitude_window_offset). With the defaults the reads stay within the 1024 input samples; with positive
// offsets they run past the newest sample into what follows in this block (the frame counter, then the analysis
// slots), and the output depends on those values. sample() returns them the same way: index i reads the i-th
// 32-bit word of the block as a float. The options are clamped so that every read stays inside the block.
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "analysis.h"
namespace halfrate::enc {
constexpr int kHistorySamples = 1024;
constexpr int kFrameSamples = 160;
// The analysis of one input frame.
struct AnalysisSlot {
Spectrum pitch_spectrum; // 256-point window: pitch search
Spectrum amplitude_spectrum; // 160-point window: harmonic amplitudes
float level_db; // energy of the pitch spectrum's fitted bins, dB
float peak; // largest sample magnitude around the amplitude window
int32_t low_band; // 1: weak energy below 250 Hz (only set with low_band_threshold_db < 0)
std::array<float, kPitchCount> fit_cost; // harmonic fit cost per grid pitch
};
struct SignalHistory {
std::array<float, kHistorySamples> samples; // newest 160 at the end, scaled to +-1
int64_t frame_count; // frames pushed so far
std::array<AnalysisSlot, 3> slots; // frame n is in slots[n % 3]
static constexpr int kWords = kHistorySamples + 2 + 3 * (sizeof(AnalysisSlot) / 4);
// 32-bit word i of this block as a float, 0 <= i < kWords.
float sample(int i) const {
if (static_cast<unsigned>(i) < static_cast<unsigned>(kHistorySamples)) return samples[i];
float f;
std::memcpy(&f, reinterpret_cast<const unsigned char*>(this) + 4 * static_cast<size_t>(i), 4);
return f;
}
};
static_assert(sizeof(AnalysisSlot) == 1151 * 4, "analysis slot: 1151 words, no padding");
static_assert(sizeof(SignalHistory) >= SignalHistory::kWords * 4, "history block holds every word");
static_assert(offsetof(SignalHistory, frame_count) == kHistorySamples * 4, "frame counter right after the samples");
static_assert(offsetof(SignalHistory, slots) == (kHistorySamples + 2) * 4, "slots right after the counter");
} // namespace halfrate::enc
+88 -68
View File
@@ -79,6 +79,8 @@ static inline int load_reg(const uint8_t val[], int len)
initMbeParms();
memset(ambe_d, 0, 49);
m_dstar_gain_s = -99.0f;
d = ambe2_decoder_new();
e = ambe2_encoder_new();
}
MBEVocoder::~MBEVocoder()
@@ -97,6 +99,8 @@ static inline int load_reg(const uint8_t val[], int len)
void MBEVocoder::decode_2450x1150(int16_t *pcm, uint8_t *ambe)
{
ambe2_decode_packed(d, ambe, pcm);
return;
int samples = 0;
process_2450x1150(ambe);
int16_t *p = getAudio(samples);
@@ -106,11 +110,23 @@ static inline int load_reg(const uint8_t val[], int len)
void MBEVocoder::decode_2450(int16_t *pcm, uint8_t *ambe)
{
int samples = 0;
process_2450(ambe);
int16_t *p = getAudio(samples);
memcpy(pcm, p, samples * sizeof(int16_t));
resetAudio();
uint8_t ambe_data[56];
//char dvsi_data[7];
//memset(dvsi_data, 0, 7);
for(int i = 0; i < 6; ++i){
for(int j = 0; j < 8; j++){
ambe_data[j+(8*i)] = (1 & (ambe[i] >> (7 - j)));
}
}
ambe_data[48] = (1 & (ambe[6] >> 7));
//int samples = 0;
ambe2_decode(d, ambe_data, pcm);
//process_2450(ambe);
//int16_t *p = getAudio(samples);
//memcpy(pcm, p, samples * sizeof(int16_t));
//resetAudio();
}
void MBEVocoder::encode_2400x1200(int16_t *pcm, uint8_t *ambe)
@@ -215,70 +231,74 @@ static inline int load_reg(const uint8_t val[], int len)
void MBEVocoder::encode_2450(int16_t *pcm, uint8_t *ambe)
{
int b[9];
int16_t frame_vector[8]; // result ignored
uint8_t ambe_frame[56];
memset(ambe, 0, 9);
vocoder.imbe_encode(frame_vector, pcm);
encode_ambe(vocoder.param(), b, m_mbelibParms->m_cur_mp, m_mbelibParms->m_prev_mp, false, 1.0);
//int b[9];
//int16_t frame_vector[8]; // result ignored
uint8_t ambe_bits[56];
memset(ambe, 0, 9);
ambe2_encode(e, pcm, ambe_bits);
//ambe2_pack(ambe_bits, ambe);
//return;
/*
vocoder.imbe_encode(frame_vector, pcm);
encode_ambe(vocoder.param(), b, m_mbelibParms->m_cur_mp, m_mbelibParms->m_prev_mp, false, 1.0);
memset(ambe_frame, 0, 56);
ambe_frame[0] = (b[0] >> 6) & 1;
ambe_frame[1] = (b[0] >> 5) & 1;
ambe_frame[2] = (b[0] >> 4) & 1;
ambe_frame[3] = (b[0] >> 3) & 1;
ambe_frame[4] = (b[1] >> 4) & 1;
ambe_frame[5] = (b[1] >> 3) & 1;
ambe_frame[6] = (b[1] >> 2) & 1;
ambe_frame[7] = (b[1] >> 1) & 1;
ambe_frame[8] = (b[2] >> 4) & 1;
ambe_frame[9] = (b[2] >> 3) & 1;
ambe_frame[10] = (b[2] >> 2) & 1;
ambe_frame[11] = (b[2] >> 1) & 1;
ambe_frame[12] = (b[3] >> 8) & 1;
ambe_frame[13] = (b[3] >> 7) & 1;
ambe_frame[14] = (b[3] >> 6) & 1;
ambe_frame[15] = (b[3] >> 5) & 1;
ambe_frame[16] = (b[3] >> 4) & 1;
ambe_frame[17] = (b[3] >> 3) & 1;
ambe_frame[18] = (b[3] >> 2) & 1;
ambe_frame[19] = (b[3] >> 1) & 1;
ambe_frame[20] = (b[4] >> 6) & 1;
ambe_frame[21] = (b[4] >> 5) & 1;
ambe_frame[22] = (b[4] >> 4) & 1;
ambe_frame[23] = (b[4] >> 3) & 1;
ambe_frame[24] = (b[5] >> 4) & 1;
ambe_frame[25] = (b[5] >> 3) & 1;
ambe_frame[26] = (b[5] >> 2) & 1;
ambe_frame[27] = (b[5] >> 1) & 1;
ambe_frame[28] = (b[6] >> 3) & 1;
ambe_frame[29] = (b[6] >> 2) & 1;
ambe_frame[30] = (b[6] >> 1) & 1;
ambe_frame[31] = (b[7] >> 3) & 1;
ambe_frame[32] = (b[7] >> 2) & 1;
ambe_frame[33] = (b[7] >> 1) & 1;
ambe_frame[34] = (b[8] >> 2) & 1;
ambe_frame[35] = b[1] & 1;
ambe_frame[36] = b[2] & 1;
ambe_frame[37] = (b[0] >> 2) & 1;
ambe_frame[38] = (b[0] >> 1) & 1;
ambe_frame[39] = b[0] & 1;
ambe_frame[40] = b[3] & 1;
ambe_frame[41] = (b[4] >> 2) & 1;
ambe_frame[42] = (b[4] >> 1) & 1;
ambe_frame[43] = b[4] & 1;
ambe_frame[44] = b[5] & 1;
ambe_frame[45] = b[6] & 1;
ambe_frame[46] = b[7] & 1;
ambe_frame[47] = (b[8] >> 1) & 1;
ambe_frame[48] = b[8] & 1;
for(int i = 0; i < 7; ++i){
for(int j = 0; j < 8; ++j){
ambe[i] |= (ambe_frame[(i*8)+j] << (7-j));
}
}
ambe_frame[0] = (b[0] >> 6) & 1;
ambe_frame[1] = (b[0] >> 5) & 1;
ambe_frame[2] = (b[0] >> 4) & 1;
ambe_frame[3] = (b[0] >> 3) & 1;
ambe_frame[4] = (b[1] >> 4) & 1;
ambe_frame[5] = (b[1] >> 3) & 1;
ambe_frame[6] = (b[1] >> 2) & 1;
ambe_frame[7] = (b[1] >> 1) & 1;
ambe_frame[8] = (b[2] >> 4) & 1;
ambe_frame[9] = (b[2] >> 3) & 1;
ambe_frame[10] = (b[2] >> 2) & 1;
ambe_frame[11] = (b[2] >> 1) & 1;
ambe_frame[12] = (b[3] >> 8) & 1;
ambe_frame[13] = (b[3] >> 7) & 1;
ambe_frame[14] = (b[3] >> 6) & 1;
ambe_frame[15] = (b[3] >> 5) & 1;
ambe_frame[16] = (b[3] >> 4) & 1;
ambe_frame[17] = (b[3] >> 3) & 1;
ambe_frame[18] = (b[3] >> 2) & 1;
ambe_frame[19] = (b[3] >> 1) & 1;
ambe_frame[20] = (b[4] >> 6) & 1;
ambe_frame[21] = (b[4] >> 5) & 1;
ambe_frame[22] = (b[4] >> 4) & 1;
ambe_frame[23] = (b[4] >> 3) & 1;
ambe_frame[24] = (b[5] >> 4) & 1;
ambe_frame[25] = (b[5] >> 3) & 1;
ambe_frame[26] = (b[5] >> 2) & 1;
ambe_frame[27] = (b[5] >> 1) & 1;
ambe_frame[28] = (b[6] >> 3) & 1;
ambe_frame[29] = (b[6] >> 2) & 1;
ambe_frame[30] = (b[6] >> 1) & 1;
ambe_frame[31] = (b[7] >> 3) & 1;
ambe_frame[32] = (b[7] >> 2) & 1;
ambe_frame[33] = (b[7] >> 1) & 1;
ambe_frame[34] = (b[8] >> 2) & 1;
ambe_frame[35] = b[1] & 1;
ambe_frame[36] = b[2] & 1;
ambe_frame[37] = (b[0] >> 2) & 1;
ambe_frame[38] = (b[0] >> 1) & 1;
ambe_frame[39] = b[0] & 1;
ambe_frame[40] = b[3] & 1;
ambe_frame[41] = (b[4] >> 2) & 1;
ambe_frame[42] = (b[4] >> 1) & 1;
ambe_frame[43] = b[4] & 1;
ambe_frame[44] = b[5] & 1;
ambe_frame[45] = b[6] & 1;
ambe_frame[46] = b[7] & 1;
ambe_frame[47] = (b[8] >> 1) & 1;
ambe_frame[48] = b[8] & 1;
*/
for(int i = 0; i < 7; ++i){
for(int j = 0; j < 8; ++j){
ambe[i] |= (ambe_bits[(i*8)+j] << (7-j));
}
}
}
void MBEVocoder::initMbeParms()
+4 -1
View File
@@ -16,7 +16,8 @@
#define MBEVOCODER_H
#include <cinttypes>
#include <imbe_vocoder_api.h>
#include "ambe2.h"
#include "../imbe_vocoder/imbe_vocoder.h"
extern "C" {
struct mbe_parameters
@@ -114,6 +115,8 @@ public:
private:
imbe_vocoder vocoder;
ambe2_decoder *d;
ambe2_encoder *e;
mbelibParms *m_mbelibParms;
int m_errs2;
char m_err_str[64];
+61
View File
@@ -0,0 +1,61 @@
// Numeric helpers with the conversion and rounding semantics the codec depends on.
//
// Float to integer conversions truncate toward zero and saturate at the target type's range; NaN converts to 0.
// (A plain C++ cast is undefined out of range; these are defined everywhere.) Rounding helpers name the rounding
// they do. Everything here is exact: no approximation, no rounding beyond what the name says.
#pragma once
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>
namespace halfrate::core {
// The bits of a float and back.
inline uint32_t float_bits(float f) { uint32_t u; std::memcpy(&u, &f, 4); return u; }
inline float float_from_bits(uint32_t u) { float f; std::memcpy(&f, &u, 4); return f; }
// Truncate toward zero, saturating; NaN gives 0.
inline int32_t to_int32_sat(float x) {
if (x != x) return 0;
if (x >= 2147483648.0f) return std::numeric_limits<int32_t>::max();
if (x <= -2147483648.0f) return std::numeric_limits<int32_t>::min();
return static_cast<int32_t>(x);
}
inline int32_t to_int32_sat(double x) {
if (x != x) return 0;
if (x >= 2147483648.0) return std::numeric_limits<int32_t>::max();
if (x <= -2147483648.0) return std::numeric_limits<int32_t>::min();
return static_cast<int32_t>(x);
}
inline int64_t to_int64_sat(float x) {
if (x != x) return 0;
if (x >= 9223372036854775808.0f) return std::numeric_limits<int64_t>::max();
if (x <= -9223372036854775808.0f) return std::numeric_limits<int64_t>::min();
return static_cast<int64_t>(x);
}
inline int64_t to_int64_sat(double x) {
if (x != x) return 0;
if (x >= 9223372036854775808.0) return std::numeric_limits<int64_t>::max();
if (x <= -9223372036854775808.0) return std::numeric_limits<int64_t>::min();
return static_cast<int64_t>(x);
}
// Integer results of the four roundings, saturating (NaN gives 0).
inline int32_t floor_to_int32(float x) { return to_int32_sat(std::floor(x)); }
inline int32_t ceil_to_int32(float x) { return to_int32_sat(std::ceil(x)); }
inline int32_t round_half_away_to_int32(float x) { return to_int32_sat(std::round(x)); }
inline int32_t round_half_even_to_int32(float x) { return to_int32_sat(std::nearbyint(x)); }
// Rounding to a whole float.
inline float floor_f(float x) { return std::floor(x); }
inline float ceil_f(float x) { return std::ceil(x); }
inline float trunc_f(float x) { return std::trunc(x); }
inline float round_half_away_f(float x) { return std::round(x); } // 2.5 -> 3, -2.5 -> -3
inline float round_half_even_f(float x) { return std::nearbyint(x); } // default rounding mode: 2.5 -> 2
// Minimum and maximum that ignore a NaN operand (IEEE minNum / maxNum).
inline float min_num(float a, float b) { return std::fmin(a, b); }
inline float max_num(float a, float b) { return std::fmax(a, b); }
} // namespace halfrate::core
+142
View File
@@ -0,0 +1,142 @@
#include "options.h"
#include <cmath>
#include <cstring>
namespace halfrate::enc {
namespace {
// One named option: a float field, or element `index` of a per-band array.
struct OptionEntry {
const char* name;
float EncoderOptions::*field;
std::array<float, 9> EncoderOptions::*bands;
int index;
};
constexpr OptionEntry real(const char* name, float EncoderOptions::*field) { return {name, field, nullptr, 0}; }
constexpr OptionEntry band(const char* name, std::array<float, 9> EncoderOptions::*bands, int index) {
return {name, nullptr, bands, index};
}
const OptionEntry kOptionTable[] = {
real("rotated_layout", &EncoderOptions::rotated_layout),
real("highpass_hz", &EncoderOptions::highpass_hz),
real("highpass_compensation", &EncoderOptions::highpass_compensation),
real("highpass_compensation_from_hz", &EncoderOptions::highpass_compensation_from_hz),
real("pitch_window_offset", &EncoderOptions::pitch_window_offset),
real("amplitude_window_offset", &EncoderOptions::amplitude_window_offset),
real("yin_weight", &EncoderOptions::yin_weight),
real("low_pitch_penalty", &EncoderOptions::low_pitch_penalty),
real("pitch_jump_cost", &EncoderOptions::pitch_jump_cost),
real("pitch_jump_free", &EncoderOptions::pitch_jump_free),
real("octave_jump_cost", &EncoderOptions::octave_jump_cost),
real("pitch_net_weight", &EncoderOptions::pitch_net_weight),
real("low_band_threshold_db", &EncoderOptions::low_band_threshold_db),
real("low_band_penalty_scale", &EncoderOptions::low_band_penalty_scale),
real("low_band_ignore_first", &EncoderOptions::low_band_ignore_first),
real("onset_db", &EncoderOptions::onset_db),
real("onset_low_pitch_scale", &EncoderOptions::onset_low_pitch_scale),
real("octave_down_tolerance", &EncoderOptions::octave_down_tolerance),
real("octave_down_after_unvoiced", &EncoderOptions::octave_down_after_unvoiced),
real("octave_down_lookback", &EncoderOptions::octave_down_lookback),
real("voicing_threshold", &EncoderOptions::voicing_threshold),
real("voicing_threshold_slope", &EncoderOptions::voicing_threshold_slope),
real("voicing_table", &EncoderOptions::voicing_table),
real("voicing_table_offset", &EncoderOptions::voicing_table_offset),
real("voicing_band_weight_exponent", &EncoderOptions::voicing_band_weight_exponent),
real("voicing_hysteresis", &EncoderOptions::voicing_hysteresis),
real("voicing_hold_unvoiced", &EncoderOptions::voicing_hold_unvoiced),
real("unvoiced_fit_guard", &EncoderOptions::unvoiced_fit_guard),
real("low_pitch_voicing_strength", &EncoderOptions::low_pitch_voicing_strength),
real("low_pitch_voicing_fit", &EncoderOptions::low_pitch_voicing_fit),
real("low_pitch_voicing_index", &EncoderOptions::low_pitch_voicing_index),
real("gate_level_db", &EncoderOptions::gate_level_db),
real("gate_above_floor_db", &EncoderOptions::gate_above_floor_db),
real("floor_rise", &EncoderOptions::floor_rise),
real("gate_hang_frames", &EncoderOptions::gate_hang_frames),
real("gated_as_unvoiced", &EncoderOptions::gated_as_unvoiced),
real("gated_offset", &EncoderOptions::gated_offset),
real("silent_voicing_index", &EncoderOptions::silent_voicing_index),
real("silence_offset", &EncoderOptions::silence_offset),
real("onset_unvoiced", &EncoderOptions::onset_unvoiced),
real("onset_offset", &EncoderOptions::onset_offset),
real("voiced_offset", &EncoderOptions::voiced_offset),
real("unvoiced_offset", &EncoderOptions::unvoiced_offset),
real("voiced_amplitude_blend", &EncoderOptions::voiced_amplitude_blend),
band("voiced_band_offset_0", &EncoderOptions::voiced_band_offset, 0),
band("voiced_band_offset_1", &EncoderOptions::voiced_band_offset, 1),
band("voiced_band_offset_2", &EncoderOptions::voiced_band_offset, 2),
band("voiced_band_offset_3", &EncoderOptions::voiced_band_offset, 3),
band("voiced_band_offset_4", &EncoderOptions::voiced_band_offset, 4),
band("voiced_band_offset_5", &EncoderOptions::voiced_band_offset, 5),
band("voiced_band_offset_6", &EncoderOptions::voiced_band_offset, 6),
band("voiced_band_offset_7", &EncoderOptions::voiced_band_offset, 7),
band("voiced_band_offset_8", &EncoderOptions::voiced_band_offset, 8),
band("unvoiced_band_offset_0", &EncoderOptions::unvoiced_band_offset, 0),
band("unvoiced_band_offset_1", &EncoderOptions::unvoiced_band_offset, 1),
band("unvoiced_band_offset_2", &EncoderOptions::unvoiced_band_offset, 2),
band("unvoiced_band_offset_3", &EncoderOptions::unvoiced_band_offset, 3),
band("unvoiced_band_offset_4", &EncoderOptions::unvoiced_band_offset, 4),
band("unvoiced_band_offset_5", &EncoderOptions::unvoiced_band_offset, 5),
band("unvoiced_band_offset_6", &EncoderOptions::unvoiced_band_offset, 6),
band("unvoiced_band_offset_7", &EncoderOptions::unvoiced_band_offset, 7),
band("unvoiced_band_offset_8", &EncoderOptions::unvoiced_band_offset, 8),
real("unvoiced_pitch_slope", &EncoderOptions::unvoiced_pitch_slope),
real("unvoiced_pitch_index", &EncoderOptions::unvoiced_pitch_index),
real("smooth_previous", &EncoderOptions::smooth_previous),
real("smooth_next", &EncoderOptions::smooth_next),
real("smooth_next_margin", &EncoderOptions::smooth_next_margin),
real("peak_threshold", &EncoderOptions::peak_threshold),
real("peak_reduction", &EncoderOptions::peak_reduction),
real("gain_bias", &EncoderOptions::gain_bias),
real("gain_lookahead_weight", &EncoderOptions::gain_lookahead_weight),
real("shape_weight_exponent", &EncoderOptions::shape_weight_exponent),
real("shape_high_weight", &EncoderOptions::shape_high_weight),
real("shape_search_beam", &EncoderOptions::shape_search_beam),
real("rescore_candidates", &EncoderOptions::rescore_candidates),
real("rescore_gain_span", &EncoderOptions::rescore_gain_span),
real("rescore_enhanced", &EncoderOptions::rescore_enhanced),
real("rescore_enhanced_offset", &EncoderOptions::rescore_enhanced_offset),
real("rescore_from_spectrum", &EncoderOptions::rescore_from_spectrum),
real("rescore_floor", &EncoderOptions::rescore_floor),
real("rescore_weight_exponent", &EncoderOptions::rescore_weight_exponent),
real("rescore_asymmetry", &EncoderOptions::rescore_asymmetry),
real("rescore_shape_mix", &EncoderOptions::rescore_shape_mix),
real("rescore_gain_weight", &EncoderOptions::rescore_gain_weight),
real("other_voicing_offset_low", &EncoderOptions::other_voicing_offset_low),
real("other_voicing_offset_high", &EncoderOptions::other_voicing_offset_high),
real("other_gated_pitch_zero", &EncoderOptions::other_gated_pitch_zero),
real("other_onset_bridge", &EncoderOptions::other_onset_bridge),
real("other_onset_bridge_db", &EncoderOptions::other_onset_bridge_db),
real("other_voiced_island", &EncoderOptions::other_voiced_island),
real("other_voiced_island_db", &EncoderOptions::other_voiced_island_db),
real("other_voiced_onset", &EncoderOptions::other_voiced_onset),
real("other_voiced_onset_db", &EncoderOptions::other_voiced_onset_db),
real("other_unvoiced_low_cut_hz", &EncoderOptions::other_unvoiced_low_cut_hz),
real("other_unvoiced_low_cut_db", &EncoderOptions::other_unvoiced_low_cut_db),
real("other_low_cut_quiet_db", &EncoderOptions::other_low_cut_quiet_db),
real("other_unvoiced_pitch_index", &EncoderOptions::other_unvoiced_pitch_index),
real("other_voiced_low", &EncoderOptions::other_voiced_low),
real("other_voiced_low_db", &EncoderOptions::other_voiced_low_db),
};
} // namespace
bool set_option(EncoderOptions& options, const char* name, double value) {
// NaN and infinities would stay in the smoothing and noise floor state after the option is set back
if (!std::isfinite(value)) return false;
for (const OptionEntry& entry : kOptionTable) {
if (std::strcmp(name, entry.name) != 0) continue;
float v = static_cast<float>(value);
if (entry.field)
options.*entry.field = v;
else
(options.*entry.bands)[entry.index] = v;
return true;
}
return false;
}
} // namespace halfrate::enc
+106
View File
@@ -0,0 +1,106 @@
// Encoder options (docs/options.md). Every option is a float, set by name with set_option; options that act as
// counts, indices or switches are truncated toward zero where they are used, as the encoder always has.
#pragma once
#include <array>
namespace halfrate::enc {
struct EncoderOptions {
// frame and input
float rotated_layout = 0x1p+0f; // 1: send frame bits 40..43 rotated (frame.h); else standard
float highpass_hz = 0x1.0da482p+7f; // input high-pass corner (Hz), off at 0 or below
float highpass_compensation = 0x1p+0f; // amplitude lift that undoes the high-pass, 0 = off
float highpass_compensation_from_hz = 0.0f; // harmonics below this frequency get no compensation
float pitch_window_offset = -0x1.ep+7f; // pitch analysis centre minus 944 (samples into the history)
float amplitude_window_offset = -0x1.e2p+7f; // amplitude analysis centre, likewise
// pitch search
float yin_weight = 0x1.3ab3ccp-1f; // weight of the YIN cost against the harmonic fit cost
float low_pitch_penalty = 0x1.a0d09ep-3f; // cost per octave below the highest grid pitch
float pitch_jump_cost = 0x1.ccccccp-1f; // tracker cost per octave of pitch change beyond the free range
float pitch_jump_free = 0x1.99999ap-4f; // pitch change (octaves) that costs nothing
float octave_jump_cost = 0x1p+0f; // extra cost of a jump of about one octave
float pitch_net_weight = 0x1.99999ap-4f; // weight of the pitch network's log posterior, 0 = off
float low_band_threshold_db = -0x1.ep+3f; // < 0: a frame whose low band trails by more than -this is "weak low band"
float low_band_penalty_scale = 0x1.333334p-1f; // low_pitch_penalty factor on such frames
float low_band_ignore_first = 0x1p+0f; // > 0: band 0 does not count for voicing on such frames
float onset_db = 0.0f; // > 0: level rise over two frames that counts as an onset
float onset_low_pitch_scale = 0x1p+0f; // low_pitch_penalty factor at an onset
float octave_down_tolerance = 0.0f; // > 0: take the octave below if its fit cost is within this
float octave_down_after_unvoiced = 0x1p+0f; // tolerance divisor after an unvoiced or silent frame
float octave_down_lookback = 0.0f; // > 0: after unvoiced, take the octave below if a later frame has it
// voicing
float voicing_threshold = 0x1.5c28f6p-1f; // band fit ratio below which a band is voiced
float voicing_threshold_slope = 0.0f; // threshold change across the bands
float voicing_table = 0x1p+0f; // > 0: thresholds from the pitch- and fit-dependent table instead
float voicing_table_offset = 0x1.99999ap-5f; // added to the table thresholds
float voicing_band_weight_exponent = 0x1.96966p-3f; // band weight = (band energy share)^this, 0 = equal
float voicing_hysteresis = 0.0f; // cost per band that differs from the previous frame's pattern
float voicing_hold_unvoiced = 0.0f; // > 0: the hysteresis also follows unvoiced frames
float unvoiced_fit_guard = 0.0f; // all-unvoiced choice is retried among the voiced patterns below this fit
float low_pitch_voicing_strength = 0.0f; // > 0: raise the upper bands' thresholds on good low-pitch fits
float low_pitch_voicing_fit = 0x1.333334p-2f; // fit cost below which that applies
float low_pitch_voicing_index = 0x1.ep+6f; // pitch indices below which that applies
// gate
float gate_level_db = -0x1.17fe1ap+6f; // frames below this level are silent
float gate_above_floor_db = 0x1.8bf50ep+2f; // or below the noise floor plus this
float floor_rise = 0x1.47ae14p-6f; // share of the level excess the noise floor rises by per frame
float gate_hang_frames = 0x1p+3f; // frames the gate stays open after speech
float gated_as_unvoiced = 0x1p+0f; // > 0: gated frames are coded as unvoiced speech, not silence
float gated_offset = -0x1.8p+1f; // log2 amplitude change of gated frames coded as speech
float silent_voicing_index = 0x1p+4f; // voicing field of silent frames (0..31)
float silence_offset = 0x1.77c7c6p-1f; // log2 amplitude offset of silent frames
float onset_unvoiced = 0.0f; // > 0: the frame after silence is unvoiced
float onset_offset = 0.0f; // log2 amplitude change of the frame after silence
// harmonic amplitudes
float voiced_offset = 0x1.eb3334p+3f; // log2 offset of voiced harmonics
float unvoiced_offset = 0x1.e471e4p+3f; // log2 offset of unvoiced harmonics
float voiced_amplitude_blend = 0x1p-1f; // voiced amplitude: fitted (0) to spectrum rms (1)
std::array<float, 9> voiced_band_offset = {-0x1p-2f, -0x1.eb851ep-6f, 0, 0, 0, 0, 0, 0, 0}; // per 500 Hz
std::array<float, 9> unvoiced_band_offset = {-0x1.cadc74p-3f, -0x1.4f13cep-5f, -0x1.41b196p-3f, 0,
-0x1.3e425ap-6f, 0x1.a7c7c6p-3f, 0, 0, 0};
float unvoiced_pitch_slope = 0.0f; // unvoiced log2 offset per octave of pitch
float unvoiced_pitch_index = 0x1.dcp+6f; // >= 0: pitch index sent with an all-unvoiced pattern
float smooth_previous = 0x1.99999ap-5f; // weight of the previous frame's amplitudes
float smooth_next = 0.0f; // weight of the next frame's amplitudes
float smooth_next_margin = 0x1p+0f; // the next frame counts as at least this much below this one
float peak_threshold = 0x1.e66666p-1f; // frames with a sample peak at or above this ...
float peak_reduction = 0x1.333334p-3f; // ... are lowered by this (log2)
// quantiser
float gain_bias = -0x1.666666p-3f; // log2 offset of the gain target
float gain_lookahead_weight = 0x1.dbcdc2p-2f; // > 0: gain choice looks two frames ahead, this weight
float shape_weight_exponent = 0x1.155bb2p-1f; // harmonic weight = 2^((a - max) * this), 0 = equal
float shape_high_weight = 0.0f; // log2 weight tilt of the top 45% of the harmonics
float shape_search_beam = 0x1p+3f; // candidates kept per search stage (1..16)
float rescore_candidates = 0x1p+1f; // shape candidates re-scored (1 = none, up to 16)
float rescore_gain_span = 0x1p+0f; // gain indices either side re-scored with them, < 0 = none
float rescore_enhanced = 0.0f; // > 0: score against the enhanced decoded amplitudes (> 1.5 tilted)
float rescore_enhanced_offset = 0.0f; // log2 offset of the target in that score
float rescore_from_spectrum = 0.0f; // != 0: reference band energies from the spectrum, not the amplitudes
float rescore_floor = 0x1.4bbf9p+3f; // band energy floor, log2 below the strongest band
float rescore_weight_exponent = 0.0f; // > 0: band weight = (band energy share)^this
float rescore_asymmetry = 0x1p+0f; // weight of bands coded too loud
float rescore_shape_mix = 0.0f; // weight of the shape distortion in the band score
float rescore_gain_weight = 0.0f; // cost of moving the gain index
// Options that belong to other codec modes: accepted by name and kept, but nothing in this
// codec reads them (see docs/options.md).
float other_voicing_offset_low = 0.0f;
float other_voicing_offset_high = 0.0f;
float other_gated_pitch_zero = 0.0f;
float other_onset_bridge = 0.0f;
float other_onset_bridge_db = 0x1.4p+3f;
float other_voiced_island = 0.0f;
float other_voiced_island_db = 0x1.8p+2f;
float other_voiced_onset = 0.0f;
float other_voiced_onset_db = 0x1.4p+4f;
float other_unvoiced_low_cut_hz = 0.0f;
float other_unvoiced_low_cut_db = 0x1.4p+4f;
float other_low_cut_quiet_db = 0.0f;
float other_unvoiced_pitch_index = -0x1p+0f;
float other_voiced_low = 0.0f;
float other_voiced_low_db = 0x1.ep+3f;
};
// Sets the option `name` to value (converted to float). Returns false for an unknown name.
bool set_option(EncoderOptions& options, const char* name, double value);
} // namespace halfrate::enc
+109
View File
@@ -0,0 +1,109 @@
#include "pitch.h"
#include "fastmath.h"
#include "numeric.h"
#include "constants.h"
#include "enc_tables.h"
namespace halfrate::enc {
void tracker_reset(PitchTracker& t, int states, int depth) {
t = PitchTracker{};
t.state_count = states;
t.depth = depth < kTrackerMaxDepth ? depth : kTrackerMaxDepth;
}
void tracker_push(PitchTracker& t, const float* cost, const float* transition) {
int n = t.state_count;
if (t.frames == 0) {
for (int i = 0; i < n; i++) t.score[i] = cost[i];
t.frames = 1;
return;
}
if (n >= 1) {
std::array<uint8_t, kTrackerStates>& back = t.predecessor[t.frames % (t.depth + 1)];
float next[kTrackerStates];
float lowest = kHuge;
for (int j = 0; j < n; j++) {
float best = kHuge;
int from = 0;
for (int i = 0; i < n; i++) {
float c = t.score[i] + transition[static_cast<long>(i) * n + j];
if (c < best) {
best = c;
from = i;
}
}
back[j] = static_cast<uint8_t>(from);
best = best + cost[j];
next[j] = best;
if (best < lowest) lowest = best;
}
for (int i = 0; i < n; i++) t.score[i] = next[i] - lowest;
}
t.frames = t.frames + 1;
}
int tracker_decide(const PitchTracker& t, int lag) {
int state = 0;
float best = kHuge;
for (int i = 0; i < t.state_count; i++)
if (t.score[i] < best) {
best = t.score[i];
state = i;
}
int stop = t.frames - lag - 1;
for (int f = t.frames - 1; f > stop && f >= 0; f--) state = t.predecessor[f % (t.depth + 1)][state];
return state;
}
namespace {
// Rounds half away from zero and saturates to [-127, 127].
int8_t to_int8(float x) {
x = x + (x >= 0.0f ? 0.5f : -0.5f);
int q = core::to_int32_sat(x);
q = q > -127 ? q : -127;
q = q < 127 ? q : 127;
return static_cast<int8_t>(q);
}
} // namespace
void pitch_net_forward(const float* in, float* out) {
using namespace tables;
int8_t input[kNetInputs], hidden1_q[160], hidden2_q[96];
float hidden1[160], hidden2[96];
for (int i = 0; i < kNetInputs; i++) {
int j = i % kNetFeatures;
input[i] = to_int8((in[i] - kPitchNetMean[j]) * kPitchNetInvStd[j] * kNetInputScale);
}
for (int o = 0; o < 160; o++) {
int32_t acc = 0;
for (int i = 0; i < kNetInputs; i++) acc += input[i] * kPitchNetWeights1[o * kNetInputs + i];
float y = kPitchNetBias1[o] + (kPitchNetScale1[o] * kNetLayer1Scale) * static_cast<float>(acc);
hidden1[o] = y < 0.0f ? 0.0f : y;
}
for (int o = 0; o < 160; o++) hidden1_q[o] = to_int8(hidden1[o] * kNetHidden1Scale);
for (int o = 0; o < 96; o++) {
int32_t acc = 0;
for (int i = 0; i < 160; i++) acc += hidden1_q[i] * kPitchNetWeights2[o * 160 + i];
float y = kPitchNetBias2[o] + (kPitchNetScale2[o] * kNetLayer2Scale) * static_cast<float>(acc);
hidden2[o] = y < 0.0f ? 0.0f : y;
}
for (int o = 0; o < 96; o++) hidden2_q[o] = to_int8(hidden2[o] * kNetHidden2Scale);
for (int o = 0; o < kNetOutputs; o++) {
int32_t acc = 0;
for (int i = 0; i < 96; i++) acc += hidden2_q[i] * kPitchNetWeights3[o * 96 + i];
out[o] = kPitchNetBias3[o] + (kPitchNetScale3[o] * kNetLayer3Scale) * static_cast<float>(acc);
}
// log2 softmax
float top = kMinusHuge;
for (int o = 0; o < kNetOutputs; o++) top = out[o] > top ? out[o] : top;
float sum = 0.0f;
for (int o = 0; o < kNetOutputs; o++) sum = sum + core::fast_exp2((out[o] - top) * kLog2E);
float offset = top + core::fast_log2(sum) * kLn2;
for (int o = 0; o < kNetOutputs; o++) out[o] = out[o] - offset;
}
} // namespace halfrate::enc
+37
View File
@@ -0,0 +1,37 @@
// Pitch tracking: a Viterbi tracker over the pitch grid that decides a frame's pitch a fixed number of frames
// late, and the pitch network that adds a learned log-posterior to the per-frame pitch costs.
#pragma once
#include <array>
#include <cstdint>
namespace halfrate::enc {
constexpr int kTrackerStates = 120;
constexpr int kTrackerMaxDepth = 4;
constexpr int kNetFeatures = 305; // per frame: 120 fit costs, 120 YIN values, 64 band log energies, level
constexpr int kNetFrames = 6;
constexpr int kNetInputs = kNetFeatures * kNetFrames;
constexpr int kNetOutputs = 121;
// Viterbi tracker: the best path cost ending in each state, and for each of the last depth + 1 frames the best
// predecessor of each state (a ring indexed by frame % (depth + 1)).
struct PitchTracker {
int32_t state_count = 0;
int32_t depth = 0;
int32_t frames = 0; // frames pushed
std::array<float, kTrackerStates> score{};
std::array<std::array<uint8_t, kTrackerStates>, kTrackerMaxDepth + 1> predecessor{};
};
void tracker_reset(PitchTracker& t, int states, int depth);
// Adds a frame: cost[state], transition[from * states + to]. Scores are kept relative to the best one.
void tracker_push(PitchTracker& t, const float* cost, const float* transition);
// The state on the best path, `lag` frames before the newest (lag <= depth).
int tracker_decide(const PitchTracker& t, int lag);
// The pitch network over six frames of features (oldest first): out[0..120] are log2 posteriors.
void pitch_net_forward(const float* in, float* out);
} // namespace halfrate::enc
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more