[ALSA] WAVE-Player basteln

25/04/2008 - 12:25 von Markus Wichmann | Report spam
Hi all,

ich baue mir hier gerade auf Basis von ALSA einen WAVE-Player, der
dann spàter mal noch andere Sachen können soll. Bislang sieht mein
Code so aus:

|//C-Standard
|#include <stdio.h>
|#include <stdlib.h>
|#include <string.h>
|#include <errno.h>
|
|//POSIX
|#include <sys/types.h>
|#include <sys/stat.h>
|#include <fcntl.h>
|
|//Linux
|#define ALSA_PCM_NEW_HW_PARAMS_API
|#include <alsa/asoundlib.h>
|
|snd_pcm_t* handle;
|
|unsigned long int setup_alsa(unsigned short channels, unsigned samplesize, unsigned int samplerate)
|{
| int rc; unsigned int real_rate = samplerate, real_time = 500000;
| snd_pcm_hw_params_t* params;
| snd_pcm_format_t format;
| snd_pcm_uframes_t psize;
|
| // Open device
| rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
| if (rc < 0)
| {
| fprintf(stderr, "Unable to open PCM device: %s", snd_strerror(rc));
| return 0;
| }
|
| snd_pcm_hw_params_malloc(&params); // Speicher für Parameter reservieren
| snd_pcm_hw_params_any(handle, params); // und füllen
|
| snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
| switch (samplesize)
| {
| case 8: format = SND_PCM_FORMAT_S8;
| break;
| case 16: format = SND_PCM_FORMAT_S16_LE;
| break;
| case 24: format = SND_PCM_FORMAT_S24_LE;
| break;
| case 32: format = SND_PCM_FORMAT_S32_LE;
| break;
| default: format = SND_PCM_FORMAT_UNKNOWN;
| }
|
| snd_pcm_hw_params_set_format(handle, params, format); //Set Sample Size
|
| snd_pcm_hw_params_set_channels(handle, params, channels); //set channels
|
| snd_pcm_hw_params_set_rate_near(handle, params, &real_rate, NULL); //fetch rate
| if (real_rate != samplerate) {
| fprintf(stderr, "Failed setting sample rate: Got %u, expect %u.", real_rate, samplerate);
| return 0;
| }
|
| snd_pcm_hw_params_set_period_time_near(handle, params, &real_time, NULL);
|
| rc = snd_pcm_hw_params(handle, params);
| if (rc < 0)
| {
| fprintf(stderr, "Failed setting hardware parameters: %s", snd_strerror(rc));
| return 0;
| }
|
| snd_pcm_hw_params_get_period_size(params, &psize, NULL);
| snd_pcm_hw_params_free(params);
| snd_pcm_prepare(handle);
| return psize;
|}
|
|int main(int argc, char* argv[])
|{
| int fd,
| rc = 0,
| i;
| char* buf;
|
| unsigned short channels, samplesize;
| unsigned int samplerate, filelength;
| unsigned long psize, bsize;
| if (argc == 0) fd = 0; else {
| fd = open(argv[1], O_RDONLY);
| if (fd < 0)
| {
| perror("Failed opening input file");
| return 1;
| }
| }
|
| buf = malloc(44); //genug für den WAVE-Header
| if (!buf) {
| perror("Failed allocating memory for header");
| return 1;
| }
|
| //WAVE-Header lesen
| rc = read(fd, buf, 44);
| if (rc < 0) {
| perror("Failed reading input file");
| return 1;
| }
|
| if (strncmp(buf, "RIFF", 4) != 0) {
| fputs("Read error or malformed input file: No RIFF header found.", stderr);
| return 2;
| }
|
| if (strncmp(buf + 8, "WAVE", 4) != 0) {
| fputs("Read error or malformed input file: No WAVE header found.", stderr);
| return 2;
| }
|
| memcpy(&channels, buf + 22, 2);
| memcpy(&samplesize, buf + 34, 2);
| memcpy(&samplerate, buf + 24, 4);
| memcpy(&filelength, buf + 40, 4);
|
| printf("File format detected: channels:\t%usample size:\t%u bitssample rate:\t%u Hzlength of file:\t%u", channels, samplesize, samplerate, filelength + 44);
| psize = setup_alsa(channels, samplesize, samplerate);
| if (!psize) return 3;
|
| bsize = psize * channels * samplesize / 8; // Puffer soll eine ganze Periode aufnehmen können
| buf = realloc(buf, bsize);
| if (!buf) {
| perror("Failed allocating memory for ring buffer");
| }
|
| for (i = 0; i <= (int)(filelength / bsize) + 1; i++)
| {
| rc = read(fd, buf, bsize);
| if (rc < 0)
| {
| perror("Failed reading input file");
| return 1;
| }
|
| if ((unsigned)rc < bsize) memset(buf + rc, 0, bsize - rc);
|
| rc = snd_pcm_writei(handle, buf, psize);
| if (rc == -EPIPE)
| {
| fprintf(stderr, "underrun");
| snd_pcm_prepare(handle);
| } else if (rc < 0) {
| fprintf(stderr, "write error: %s", snd_strerror(rc));
| } else if (rc != (int)psize) {
| fprintf(stderr, "short write");
| }
|
| }
|
| snd_pcm_drain(handle);
| snd_pcm_close(handle);
| free(buf);
|
| return 0;
|}

Gut, das war jetzt vielleicht etwas viel, aber naja...

Die Daten, welche Header-Daten in einer Wave wo liegen, habe ich von
http://ccrma.stanford.edu/CCRMA/Cou...aveFormat/

Die Ausgaben, was so in den Headern drinsteht, das stimmt ja auch
alles. Aber mir macht die Tonqualitàt sorgen: Solange ich bei Stereo,
44,1 kHz und 16 bit samplesize bleibe, ist ja auch alles gut.
Problematisch wird es aber schon, sobald ich auf 8 bit runtergehe.
Dann wird die WAVE zu einem Gekreische (ein Rauschen umgibt das
eigentliche Signal der Datei). aplay spielt die Datei aber vorzüglich
ab (wenngleich man auch damit hört, dass es jetzt nur noch ein Byte
pro Sample gibt).

Folgende Fragen tun sich also auf:
1.) Wieso kreischt die WAVE rum, wenn man die Sample-Größe halbiert?
2.) Mit sox gibt es auch die Möglichkeit höherer Samplegrößen sowie
anderer Formate. (3, 4 und 8 Bytes, signed, unsigned, u-law und A-law)
Wo steht im Waveheader, welches Format es denn ist? Ich kann nàmlich
nur die Sample-Größe rauskriegen (Bei Offset 34), nicht aber deren
Kodierung.

3.) Irgendwelche Vorschlàge, wie man das Design noch prinzipiell
verbessern könnte? Die Dateityp-Erkennung kommt spàter noch aus main()
raus, das Abspielen dann auch, damit main() nicht zu unübersichtlich
wird und das hinzufügen neuer Formate nicht zu kompliziert.

Tschö,
Markus
Progress (n.): process through which USENET evolved from smart people in front
of dumb terminals to dumb people in front of smart terminals.
 

Lesen sie die antworten

#1 Stefan Reuther
25/04/2008 - 18:42 | Warnen spam
Hallo,

Markus Wichmann wrote:
Die Daten, welche Header-Daten in einer Wave wo liegen, habe ich von
http://ccrma.stanford.edu/CCRMA/Cou...aveFormat/



Wie üblich fehlen da die selteneren Teile einer Wave-Datei. Ich hab hier
z.B. irgendwo welche rumliegen, die noch so eine Art Tag in einem
'list'-Chunk haben.

Die Ausgaben, was so in den Headern drinsteht, das stimmt ja auch
alles. Aber mir macht die Tonqualitàt sorgen: Solange ich bei Stereo,
44,1 kHz und 16 bit samplesize bleibe, ist ja auch alles gut.
Problematisch wird es aber schon, sobald ich auf 8 bit runtergehe.
Dann wird die WAVE zu einem Gekreische (ein Rauschen umgibt das
eigentliche Signal der Datei).



Hast du überprüft, ob die Einrichtung der Tonparameter tatsàchlich
funktioniert hat? Eventuell rücklesen o.à. ALSA und die dazugehörigen
Libs kenne ich nicht, aber ich hab schon ein paar OSS-Programme gefixt,
die nicht damit klarkamen, dass mein Onboard-Sound-Dingens
ausschließlich 16-bit-44-kHz-Stereo kann. Manche Programme korrigieren
dann das Format korrekterweise (z.B. madplay), manche nicht (z.B.
mp3blaster).

2.) Mit sox gibt es auch die Möglichkeit höherer Samplegrößen sowie
anderer Formate. (3, 4 und 8 Bytes, signed, unsigned, u-law und A-law)
Wo steht im Waveheader, welches Format es denn ist? Ich kann nàmlich
nur die Sample-Größe rauskriegen (Bei Offset 34), nicht aber deren
Kodierung.



Auf der von dir zitierten Webseite, 'AudioFormat'. WAV ist prinzipiell
ein Container für beliebige Audiodaten, wenn's sein muss auch mp3 o.à.

3.) Irgendwelche Vorschlàge, wie man das Design noch prinzipiell
verbessern könnte?



Du könntest zur Decodierung von numerischen Werten statt memcpy
anstàndiges Un-Marshalling nutzen, dann funktioniert das auch mit
big-endian-Maschinen. Außerdem sollte ein anstàndiges Programm den
RIFF-Container vollstàndig parsen. Die Audiodaten beginnen zwar meistens
bei 44, aber nicht immer.


Stefan

Ähnliche fragen