[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
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(¶ms); // 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
- Stefan Reuther (25.04.2008 18:42)
- Markus Wichmann (25.04.2008 21:37)
- Stefan Reuther (26.04.2008 12:43)
