CrazyPlaya
16-10-2008, 15:48
Hi an alle,
um einen Audio Stream von 20ms mit 48000hz und 16bit mit dem uLaw Standard zu versenden muss ich diesen erstmal auf 8000Hz runter samplen und dann 4 LSB´s rausrotieren.
Mein Problem wie breche ich runter auf 8000Hz mit möglichst wenig Informationsverlust. 2 Wege habe ich bereits ausprobiert. Einmal die Berechnung eines Mittelwerts von jeweils 6bit.
signed long summe = 0;
for(int i = 0; i < size; i++)
{
summe += rawData[i];
if((i%RATIO) == 0)
{
downsampledAudio[i/RATIO] = summe/RATIO;
summe = 0;
}
}
Das Ergebnis ist zwar schon gut aber noch nicht wirklich annehmbar, da alles noch ein wenig verzerrt klingt und auch noch ein leichter Hall vorhanden ist.
Meine 2te Lösung ist die Berechung eines gewichteten Mittelwertes mit der Formel:
Z(p) = (1 / m) * (a0 Q(6p) + a1 Q(6p+1) + a2 Q(6p+2) + a3 Q(6p+3) + a4 Q(6p+4) + a5 Q(6p+5))
Wobei Z das Zielsample ist,
a die Fensterungskoeeffizienten(int a[6] = { 1, 4, 12, 12, 4, 1 })
m die Summe der Koeffizienten = 34
Q ist der Quellsample sprich die 20ms sample mit 48000Hz und 16bit
und p ist meine Laufvariable
Dies habe ich so umgesetzt
int CAudioOut::Reduce48to8kHz(short* pBuffer, int nBytesToReduce)
{
ASSERT((nBytesToReduce % 6) == 0);
short* pSrc = pBuffer;
short* pDst = pBuffer;
for(int i = 0; i < (nBytesToReduce / 6); i++)
{
long val = 0;
val = *pSrc++;
val += (long)*pSrc++ * 4;
val += (long)*pSrc++ * 12;
val += (long)*pSrc++ * 12;
val += (long)*pSrc++ *4;
val += *pSrc++;
*pDst++ = (short)(val/38);
}
return (pDst - pBuffer);
}
Die Qualität hiervon ist aber wesentlich schlechter und jetzt bin ich echt überfragt was man noch machen könnte dass die Sprache mit möglicst wenig Knacken und Verzerrung übertragen wird.
Um das ganze hinterher ins uLaw zu bringen rotier ich nun erst mal 4 LSB raus um auf 12bit zu kommen
for(int i =0; i < size2; i++) //size2 in diesem Fall 160
downsampledAudio[i] = downsampledAudio[i] >> 4;
und encodiere es dann in uLaw folgendermaßen
for(int i = 0; i < size2; i++)
newPacket.m_payload[i] = g711.encodeULaw(downsampledAudio[i]);
Wobei die Encode Methode so ausschaut
#define CLIP 8159
#define BIAS 0x84
static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
0x3FF, 0x7FF, 0xFFF, 0x1FFF};
static int search(int val, short *table, int size)
{
int i;
for (i = 0; i < size; i++)
{
if (val <= *table++)
return (i);
}
return (size);
}
unsigned char encodeULaw(short pcm_val)
{
short mask;
short seg;
unsigned char uval;
pcm_val = pcm_val >> 2;
if (pcm_val < 0)
{
pcm_val = -pcm_val;
mask = 0x7F;
}
else
{
mask = 0xFF;
}
if ( pcm_val > CLIP ) pcm_val = CLIP;
pcm_val += (BIAS >> 2);
seg = search(pcm_val, seg_uend, 8);
if (seg >= 8)
return (unsigned char) (0x7F ^ mask);
else
{
uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
return (uval ^ mask);
}
}
Würde mich freuen wenn jemand noch eine Idee hat oder evtl. einen groben Fehler bei mir findet.
Gruß
CrazyPlaya
PS: Achso und mir wurde gesagt anstatt der Funktion EncodeULaw sollte ich ein Lut in der Größe 4096 verwenden. Diese müsste ich mir selbst erstellen aber ich
ich habe keine Ahnung wie das funktioniert. Vllt. könnte mir da auch noch jemand unter die Arme greifen.
um einen Audio Stream von 20ms mit 48000hz und 16bit mit dem uLaw Standard zu versenden muss ich diesen erstmal auf 8000Hz runter samplen und dann 4 LSB´s rausrotieren.
Mein Problem wie breche ich runter auf 8000Hz mit möglichst wenig Informationsverlust. 2 Wege habe ich bereits ausprobiert. Einmal die Berechnung eines Mittelwerts von jeweils 6bit.
signed long summe = 0;
for(int i = 0; i < size; i++)
{
summe += rawData[i];
if((i%RATIO) == 0)
{
downsampledAudio[i/RATIO] = summe/RATIO;
summe = 0;
}
}
Das Ergebnis ist zwar schon gut aber noch nicht wirklich annehmbar, da alles noch ein wenig verzerrt klingt und auch noch ein leichter Hall vorhanden ist.
Meine 2te Lösung ist die Berechung eines gewichteten Mittelwertes mit der Formel:
Z(p) = (1 / m) * (a0 Q(6p) + a1 Q(6p+1) + a2 Q(6p+2) + a3 Q(6p+3) + a4 Q(6p+4) + a5 Q(6p+5))
Wobei Z das Zielsample ist,
a die Fensterungskoeeffizienten(int a[6] = { 1, 4, 12, 12, 4, 1 })
m die Summe der Koeffizienten = 34
Q ist der Quellsample sprich die 20ms sample mit 48000Hz und 16bit
und p ist meine Laufvariable
Dies habe ich so umgesetzt
int CAudioOut::Reduce48to8kHz(short* pBuffer, int nBytesToReduce)
{
ASSERT((nBytesToReduce % 6) == 0);
short* pSrc = pBuffer;
short* pDst = pBuffer;
for(int i = 0; i < (nBytesToReduce / 6); i++)
{
long val = 0;
val = *pSrc++;
val += (long)*pSrc++ * 4;
val += (long)*pSrc++ * 12;
val += (long)*pSrc++ * 12;
val += (long)*pSrc++ *4;
val += *pSrc++;
*pDst++ = (short)(val/38);
}
return (pDst - pBuffer);
}
Die Qualität hiervon ist aber wesentlich schlechter und jetzt bin ich echt überfragt was man noch machen könnte dass die Sprache mit möglicst wenig Knacken und Verzerrung übertragen wird.
Um das ganze hinterher ins uLaw zu bringen rotier ich nun erst mal 4 LSB raus um auf 12bit zu kommen
for(int i =0; i < size2; i++) //size2 in diesem Fall 160
downsampledAudio[i] = downsampledAudio[i] >> 4;
und encodiere es dann in uLaw folgendermaßen
for(int i = 0; i < size2; i++)
newPacket.m_payload[i] = g711.encodeULaw(downsampledAudio[i]);
Wobei die Encode Methode so ausschaut
#define CLIP 8159
#define BIAS 0x84
static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
0x3FF, 0x7FF, 0xFFF, 0x1FFF};
static int search(int val, short *table, int size)
{
int i;
for (i = 0; i < size; i++)
{
if (val <= *table++)
return (i);
}
return (size);
}
unsigned char encodeULaw(short pcm_val)
{
short mask;
short seg;
unsigned char uval;
pcm_val = pcm_val >> 2;
if (pcm_val < 0)
{
pcm_val = -pcm_val;
mask = 0x7F;
}
else
{
mask = 0xFF;
}
if ( pcm_val > CLIP ) pcm_val = CLIP;
pcm_val += (BIAS >> 2);
seg = search(pcm_val, seg_uend, 8);
if (seg >= 8)
return (unsigned char) (0x7F ^ mask);
else
{
uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
return (uval ^ mask);
}
}
Würde mich freuen wenn jemand noch eine Idee hat oder evtl. einen groben Fehler bei mir findet.
Gruß
CrazyPlaya
PS: Achso und mir wurde gesagt anstatt der Funktion EncodeULaw sollte ich ein Lut in der Größe 4096 verwenden. Diese müsste ich mir selbst erstellen aber ich
ich habe keine Ahnung wie das funktioniert. Vllt. könnte mir da auch noch jemand unter die Arme greifen.