PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 48kHz Sample auf 8kHz reduzieren



CrazyPlaya
16-10-2008, 14: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.

bischi
16-10-2008, 19:40
Ganz einfach: Nur jedes 6-te Sample nehmen! Nichts mitteln! Und die anderen 5 Samples wegwerfen (überleg dir folgendes: Zeichne ein zeitkontinuierliches Signal und sample dieses mit "48kHz". Sample das gleiche Signal von Anfang an mit "8kHz". Die mittleren Samples lässt du in diesem Fall auch einfach weg... Das Problem ist halt, dass du mit 8kHz nur gerade bandbegrenzte Signale mit höchster Frequenz 4kHz verlustfrei aufzeichnen kannst... Aber das liegt generell an den 8kHz Samplingrate und nicht am Verfahren (Nyquists Samplingtheorem)...)

MfG Bischi

PS: Für Sprache sind 4kHz Maximalfrequenz im Normalfall ausreichend... Falls das ursprüngliche Signal noch Komponenten drin hat, die höher liegen, solltest du das ursprüngliche Signal zuerst durch einen Tiefpassfilter mit Grenzfrequenz 4kHz lassen :)

PS2: Zur LUT: 2^12 = 4096. Anstatt jeden Wert berechnen zu müssen, berechnest du alle möglichen Werte und deren Resultate einmal im Voraus und schaust die dann in der LUT nach...

alainstgt
09-11-2008, 22:44
Hallo,
das, was Du machen möchtest nennt man in der Signalverarbeitung eine Unterabtastung bzw. Dezimierung (decimation auf english).
Diese besteht aus einer Tiefpassfilterung zur Vermeidung von Aliasing - dürfte in deinem Fall unbedingt notwendig sein, um eine gute Signalqualität zu erhalten - und einer Unterabtastung.
Hierfür gibt es effiziente Verfahren und fertigen Code auch in C++, google mal.
Viel Erfolg
Alain