Forum: PC-Programmierung Aus PCM16BitStereo mach PCM8BitMono


von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

für ein Microcontrollerprojekt muss ich eine MP3-Datei stark abspecken, 
damit der kleine Controller nicht überfordert ist.
Mittels MP3Sharp dekodiere ich die MP3-Datei in ihre PCM-Rohdaten.
Dann durchlaufe ich diese Daten und versuche sie entsprechend zu 
filtern.

Der Schritt Stereo --> Mono hat nach einigen Testen auch gut geklappt, 
allerdings kriege ich das 16Bit --> 8 Bit nicht hin.

Wenn ich die Datei mit CoolEditPro öffne, wird sie im Intel 16bit-PCM 
Format (LSB, MSB) korrekt abgespielt, also dachte ich mir, dass mir im 
zweiten Schritt einfach nur jedes zweites Byte (immer das MSB) nehmen 
kann um damit eine 8Bit-Version zu erschaffen.

Hier mein Code (C#)
1
byte[] audioLinkBuffer = new byte[1000];
2
 
3
                    while (m_MP3Stream.Read(audioLinkBuffer, 0, audioLinkBuffer.Length) != 0)
4
                    {
5
                        byte[] b = new byte[audioLinkBuffer.Length / 2];
6
                        int k = 0;
7
                        for (int i = 1; i < audioLinkBuffer.Length; i += 4)
8
                        {
9
                            for (int j = 0; j <= 1; j++)
10
                            {
11
                                b[k] = audioLinkBuffer[i + j - 1];              // Aus Stereo mach Mono
12
                                k++;
13
                            }
14
                        }
15
                        k = 0;
16
                        byte[] c = new byte[b.Length / 2];
17
                        for (int i = 0; i + 1 < c.Length; i += 2)
18
                        {
19
                            c[k] = b[i + 1];                      // Nur High-Byte filtern (funktioniert nicht)
20
                            k++;
21
                        }
22
                        outputStream.Write(c, 0, c.Length);
23
                    }
24
                    outputStream.Flush();
25
                    outputStream.Close();

Habt ihr da vielleicht irgendwelche Ideen oder Vorschläge?

Die Version, die obiger Code liefert enthält fast nur volle Amplituden. 
Man hört viel Rauschen und im Hintergrund gaaanz gaaanz gaaanz schwach 
noch die (Krombacher-)Melodie...

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Habe herausgefunden, dass 16Bit-PCM im 2er-Komplement gespeichert wird.

Nun sieht mein Code so aus (ich weiß der ist nicht optimiert):
1
while (m_MP3Stream.Read(audioLinkBuffer, 0, audioLinkBuffer.Length) != 0)
2
                    {
3
                        byte[] b = new byte[audioLinkBuffer.Length / 2];
4
                        int k = 0;
5
                        for (int i = 1; i < audioLinkBuffer.Length; i += 4)
6
                        {
7
                            for (int j = 0; j <= 1; j++)
8
                            {
9
                                b[k] = audioLinkBuffer[i + j - 1];              // Aus Stereo mach Mono
10
                                k++;
11
                            }
12
                        }
13
                        for (int i = 0; i < b.Length; i++)
14
                        {
15
                            b[i] = (byte)((255 - b[i]) + 1);   // 2er Komplement
16
                        }
17
                        k = 0;
18
                        byte[] c = new byte[b.Length / 2];
19
                        for (int i = 0; i + 1 < b.Length; i+=2)
20
                        {
21
                            c[k] = (byte)(b[i + 1] / 1);  // High-Byte nehmen
22
                            k++;
23
                        }
24
                        outputStream.Write(c, 0, c.Length);
25
                    }
26
                    outputStream.Flush();
27
                    outputStream.Close();

Habe mal 2 Beispieldateien drangehangen, die das Ergebnis zeigen.
Ist schon ein heftiger Schritt von 16Bit zu 8Bit.

Habe das ganz mal parallel in einem Programm gemacht und da kam ich auf 
ein ähnliches Ergebnis.

Meint ihr die Umwandlung kann so stimmen? Oder stimmt da etwas mit dem 
Rauschen noch nicht?

von ./. (Gast)


Lesenswert?

Das waren wohl die falschen 8 Bit.

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Dachte ich zuerst auch. Dann hab ich in der letzten Schleife aus der +1 
eine +0 gemacht (in allen Vorkommen) und das ist das Ergebnis (siehe 
Anhang).

Das kanns ja wohl erst recht nicht sein...

Da läuft der Hase wohl doch noch irgendwo falsch, oder?

von ./. (Gast)


Angehängte Dateien:

Lesenswert?

So müsst es klingen...

von Frank M. (aktenasche)


Lesenswert?

finde deine schleifen sehr verwirrend :(

mich würde stutzig machen dass das ergebnis mit 22kHz angezeigt wird, 
die originaldatei aber mit 48kHz abgetastet wurde.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Das kanns ja wohl erst recht nicht sein...

Also irgenwie würde ich da nicht versuchen besonders "schlau" zu 
optimieren...

Nimm die 16bit, schiebe sie um 8 stellen nach rechts und nehme dann die 
untersten 8 bit, dann klappt es auch mit dem zweierkomplement. ALso etwa 
so:
1
int sample16bit = (byte[i+1] << 8) | (byte[i];
2
byte sample8bit = (byte)((sample16bit >> 8) & 0xFF)

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Hi,

erstmal Danke für die Antwort.

Wenn ich deinen Code quasi "nachschalte", dann krieg ich nur noch wirres 
piepsen raus, was wirklich gar nix mehr  mit der MP3 an sich zu tun hat.

Der Code (Davor wird aus Stereo noch Mono gemacht, was aber bisher 
geklappt hat):
1
                        byte[] c = new byte[b.Length / 2];
2
                        k = 0;
3
                        for (int i = 0; i + 1 < b.Length; i += 2)
4
                        {
5
                            int sample16Bit = (b[i + 1] << 8) | (b[i]);
6
                            byte sample8Bit = (byte)((sample16Bit >> 8) & 0xFF);
7
                            c[k] = sample8Bit;
8
                        }
9
                        outputStream.Write(c, 0, c.Length);

Das Ergebnis ist im Anhang.

Habe ich was falsch gemacht oder wo ist der Fehler?

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Sorry, mein Fehler. Ich habe vergessen meine Zählvariable in der 
For-Schleife zu erhöhen.

Habe das nun nachgeholt, aber das Resultat ist immer noch "erbärmlich" 
:-D

Mal zwei Versionen:
Einfach abschneiden:
1
                        byte[] c = new byte[b.Length / 2];
2
                        k = 0;
3
                        for (int i = 0; i + 1 < b.Length; i += 2)
4
                        {
5
                            int sample16Bit = (b[i + 1] << 8) | (b[i]);
6
                            int complement = 65535 - sample16Bit + 1;
7
                            byte sample8Bit = (byte)((sample16Bit >> 8) & 0xFF);
8
                            c[k] = sample8Bit;
9
                            k++;
10
                        }

65535 auf 255 abbilden:
1
                        byte[] c = new byte[b.Length / 2];
2
                        k = 0;
3
                        for (int i = 0; i + 1 < b.Length; i += 2)
4
                        {
5
                            int sample16Bit = (b[i + 1] << 8) | (b[i]);
6
                            int complement = 65535 - sample16Bit + 1;
7
                            byte sample8Bit = (byte)((float)((float)complement/(float)65535.0f) * 255.0f);
8
                            c[k] = sample8Bit;
9
                            k++;
10
                        }

Die Ergebnisse habe ich auch mal wieder angehangen.

Irgendwie liegt da irgendwo irgendetwas im Argen...

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Hmm, hab den Anhang oben vergessen (und kann ihn irgendwie nicht mehr 
reineditieren), deswegen hier nochmal beide Anhänge...

von Roland N. (eroli)


Lesenswert?

Keine weiteren Ideen?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Irgendwie liegt da irgendwo irgendetwas im Argen...

Ja wie gesagt das "arge" sind höchstwahrscheinlich deine Konvertierungen 
;)
Was macht den das Float da nun drinne?

dies
1
int sample16Bit = (b[i + 1] << 8) | (b[i]);
 muss eventuell auch
1
int sample16Bit = (b[i] << 8) | (b[i + 1]);
 heißen, je nach byteorder!

Das sit aber alles nur stochern im Nebel, mach dir doch eine Datei Mono 
16bit maximal 1 sek, dann machst du dir eine Datei Mono 8 bit, dann den 
Hexeditor deiner Wahl aufmachen, und einfach mal schauen wie es korrekt 
aussehen sollte.

Auch ist das hochladen von MP3s wenig zielführend, da sieht man dann ja 
nun überhaupt nicht mehr was du konvertiert hast (ich hoffe doch du 
versuchst tatsächlich ein RAW PCM WAV zu konvertieren).
Auch solltest du bedenken dass auf jedenfall der Header angepasst werden 
muß!

von ... (Gast)


Lesenswert?

Höhrt sich an, als ob da signed/unsigned durcheinander gekommen sind.
In welcher Form fallen denn die Daten aus Deinem Program, als raw oder 
als wav? Wenn raw, dann mal beim öffnen in CoolEditPro die signedness 
ändern. Wenn wav, dann entsprechend in Deinem Programm  mit reinrechnen.
Bei 16bit wav sind die Daten normalerweise signed während sie bei 8bit 
normalerweise unsigned sind.

von Roland N. (eroli)


Lesenswert?

Guten Morgen zusammen,

ich habe wirklich 16-bit-pcm Rohdaten. Keine WAVs oder sogar MP3s.

Die floats waren wirklich nur dazu da um einen Wert von 65536 auf 255 
abzubilden, damit keine Toninformationen verloren gehen (wie beim 
Abschneiden des Low-Bytes), sondern alle Informationen einfach 
runtergerechnet werden und es dadurch einfach nur "ungenauer" wird.

Aber es haben ja beide Beispiele nicht befriedigend genug funktioniert.

CoolEditPro hab ich schon benutzt. Dadurch weiß ich ja auch, dass mein 
Code, der aus Stereo Mono macht, richtig funktioniert.

Leider hab ich das Vorgehen mit der 1s-Datei noch nicht verstanden. Eine 
16-bit-pcm datei hat bei 1s laufzeit doch auch such schon 48.000*2 Byte 
Größe (Abtastrate von 48Khz). Wie soll ich da dann etwas im HexEditor 
erkennen?

Kannst du mir das Vorgehen vielleicht genauer schildern?

von ... (Gast)


Lesenswert?

Roland M. schrieb:
> ich habe wirklich 16-bit-pcm Rohdaten. Keine WAVs oder sogar MP3s.

Verstehe ich jetzt nich. Du benutzt doch MP3Sharp um MP3 zu lesen. Das 
liefert Dir 16Bit PCM Sereo. Dann schneidest Du einen Kanal weg und 
machst dadurch Mono draus. Dann versuchst Du aus den 16Bit 8Bit zu 
machen und gibst das Ergebnis dann über "outputStream" aus. Nur wohin? 
Ist das ein File, was Du dann mit Cooledit kontrollierst oder was? 
Schreibst Du vor der der Konvertiererei noch irgendwas in den 
"outputStream", irgendeinen Header eventuell? Häng doch mal direkt den 
Output, der da rauskommt ran.

Eigentlich müßte es reichen, wenn Du Deine Bytes nochmal konvertierst. 
Also etwa so:
1
byte[] c = new byte[b.Length / 2];
2
k = 0;
3
for (int i = 0; i + 1 < b.Length; i += 2)
4
{
5
    int sample16Bit = (b[i + 1] << 8) | (b[i]);
6
    int complement = 65535 - sample16Bit + 1;
7
    byte sample8Bit = (byte)((sample16Bit >> 8) & 0xFF);
8
    if(sample8Bit > 127)
9
    {
10
        c[k] = (byte)(sample8Bit - 128);
11
    } else {
12
        c[k] = (byte)(sample8Bit + 128);
13
    }
14
    k++;
15
}

von ... (Gast)


Lesenswert?

Oder vielleicht auch so:
1
byte[] c = new byte[b.Length / 2];
2
k = 0;
3
for (int i = 0; i + 1 < b.Length; i += 2)
4
{
5
    int sample16Bit = (b[i + 1] << 8) | (b[i]);
6
    int complement = 65535 - sample16Bit + 1;
7
    byte sample8Bit = (byte)(((sample16Bit >> 8) + 128) & 0xFF);
8
    c[k] = sample8Bit;
9
    k++;
10
}

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Die floats waren wirklich nur dazu da um einen Wert von 65536 auf 255
> abzubilden, damit keine Toninformationen verloren gehen (wie beim
> Abschneiden des Low-Bytes)

Eh... ich will dich ja nicht enttäuschen aber das ist bestenfalls 
maximal umständlich du erhältst dadurch nicht mehr Informationen!

Roland M. schrieb:
> hat bei 1s laufzeit doch auch such schon 48.000*2 Byte

Dann nimm halt eine halbe Sekunde, es geht nur darum, das man 
ausreichend wenig Daten hat um z.B. mal die Rohdaten hier hochzuladen.

Roland M. schrieb:
> Wie soll ich da dann etwas im HexEditor
> erkennen?

Es würde z.B. reichen wenn du die ersten paar bytes vergleichst, 
natürlich müssen dann Quelle und Ausgabe jeweils als RAW PCM vorliegen.

Wieoft willst du eigentlich konvertieren? Was spricht dagegen schon ein 
fertiges Programm für die Konvertierung zu nehmen?

von Roland N. (eroli)


Lesenswert?

Läubi .. schrieb:
> Eh... ich will dich ja nicht enttäuschen aber das ist bestenfalls
> maximal umständlich du erhältst dadurch nicht mehr Informationen!

Verstehe schon, aber es war ja sowieso nur eine Alternative. Kann man 
auch einfach weglassen...

> dies
>
> int sample16Bit = (b[i + 1] << 8) | (b[i]);
>
> muss eventuell auch
>
> int sample16Bit = (b[i] << 8) | (b[i + 1]);
>
> heißen, je nach byteorder!

Ich habe natürlich bereits beide Möglichkeiten ausprobiert. Bei der 
Möglichkeit, die im obigen Beispielcode nicht gelistet ist, war quasi 
nur Rauschen zu hören.

> Dann nimm halt eine halbe Sekunde, es geht nur darum, das man
> ausreichend wenig Daten hat um z.B. mal die Rohdaten hier hochzuladen.

Ich werde noch die oben beschriebenen Möglichkeiten testen und dann ein 
entsprechendes Beispiel hier hochladen.

> Wieoft willst du eigentlich konvertieren? Was spricht dagegen schon ein
> fertiges Programm für die Konvertierung zu nehmen?

Da ich die Konvertierung für ein Uni-Projekt benötige, möchte ich gerne 
auf externe Programme verzichten. Sollte ja auch eigentlich einfach 
möglich sein.

@ ... (Gast):
Ich werde deine Vorschläge gleich mal ausprobieren und mich dann mit 
Neuigkeiten zurückmelden.

Schonmal vielen Dank an alle helfenden Hände :-)

von Roland N. (eroli)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich bin sprachlos: ... (Gast) hatte wirklich Recht, es funktioniert so:
1
                for (int i = 0; i + 1 < b.Length; i += 2)
2
                {
3
                    int sample16Bit = (b[i + 1] << 8) | (b[i]);
4
                    byte sample8Bit = (byte)(((sample16Bit >> 8) +128) & 0xFF);
5
                    c[k] = sample8Bit;
6
                    k++;
7
                }

(Die andere Codealternative mit der If-Bedingung hat genau so 
funktioniert)

Erstmal vielen vielen Dank an Dich und auch alle anderen für eure Hilfe 
:-)

Hab euch nochmal das Beispiel angehangen.

Schönen Tag noch und nochmals Danke :-)

von ... (Gast)


Lesenswert?

Roland M. schrieb:
> (Die andere Codealternative mit der If-Bedingung hat genau so
> funktioniert)

Machen ja im Endeffekt auch beide das gleiche :)

Wie ich drauf gekommen bin? Ich hab Dein Original MP3 mittels Audacity 
in Raw 8Bit PCM Mono konvertiert, sowohl als signed als auch als 
unsigned Version. Danach hab ich diese raw-Files in CoolEditPro 
importiert, hierbai kann man ebenfalls wählen, ob die Daten signed oder 
unsigned sind. Wählt man das verkehrte, klingt es genau wie Deine 
KrombacherTruncated.mp3. Anschließend hab ich mir die Unterschiede der 
beiden raw-Files in hex angesehen und bin so auf die 128 gekommen.

von Roland N. (eroli)


Lesenswert?

Na das nenn ich mal Engagement um in einem Forum zu helfen.

Nochmals Danke (auch für den Lösungsweg) :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.