nntp2http.com
Posting
Suche
Optionen
Hilfe & Kontakt

[ALSA] WAVE-Player basteln

Von: Markus Wichmann (nullplan@gmx.net) [Profil]
Datum: 25.04.2008 12:25
Message-ID: <e6o8e5-n7d.ln1@www.wichi.de.vu>
Newsgroup: de.comp.os.unix.programming
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
m
ein
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 samplesiz
e, 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\n", snd_strerror(
rc));
|        return 0;
|    }
|
|    snd_pcm_hw_params_malloc(&params); // Speicher für Parameter re
servieren
|    snd_pcm_hw_params_any(handle, params); // und füllen
|
|    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTER
LEAVED);
|
|    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 S
ize
|
|    snd_pcm_hw_params_set_channels(handle, params, channels); //set chan
nels
|
|    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.\
n", real_rate, samplerate);
|        return 0;
|    }
|
|    snd_pcm_hw_params_set_period_time_near(handle, params, &real_time, N
ULL);
|
|    rc = snd_pcm_hw_params(handle, params);
|    if (rc < 0)
|    {
|        fprintf(stderr, "Failed setting hardware parameters: %s\n", 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: \nchannels:\t%u\nsample size:\t%u bits
\nsample rate:\t%u Hz\nlength of file:\t%u\n", channels, samplesize, samp
lerate, filelength + 44);
|    psize = setup_alsa(channels, samplesize, samplerate);
|    if (!psize) return 3;
|
|    bsize = psize * channels * samplesize / 8; // Puffer soll eine gan
ze 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\n");
|            snd_pcm_prepare(handle);
|        } else if (rc < 0) {
|            fprintf(stderr, "write error: %s\n", snd_strerror(rc));
|        } else if (rc != (int)psize) {
|            fprintf(stderr, "short write\n");
|        }
|
|    }
|
|    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/Courses/422/projects/WaveFormat/

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üglic
h
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 halb
iert?
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 de
ren
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 termina
ls.

[ Auf dieses Posting antworten ]

Antworten