HTML

Az élet kódjai

Csináld maga. Senki nem csinálja meg helyetted.

Friss topikok

  • sdani: @Travis.CG: Nohát, nem is tudtam, hogy ilyen van... bár ahogy elnézem ezek a komponensek fizetősek... (2018.11.01. 10:14) Rossz beidegződések a bionformatikában
  • Csenge Tarnói: Ez érdekes. Most csinálok egy meta-analízist, életemben először, úgyhogy az én tudásom is felszíne... (2018.10.01. 21:39) Ez már nekem sok
  • robertherczeg: Nekem a kedvenc az volt, hogy: "Inkább eleve Mann-Whitney és/vagy Wilcoxon tesztet használjunk, m... (2018.09.04. 07:47) Ezért utálom a Wilcoxon-tesztet
  • Travis.CG: ÉÉÉÉÉs megjelent! (2018.08.24. 23:31) Nehéz szülés 2
  • Szedlák Ádám: Hogy én mennyire köszönöm ezt a posztot, arra nincs szó. A kódoljon mindenki / legyen mindenki olc... (2018.06.25. 03:37) Legyen mindenki programozó

Linux hangképzés

2011.05.26. 21:22 Travis.CG

GNU/Linux rendszeren sokféle módja van annak, hogy hangot csiholjunk ki a gépből. Itt is megfigyelhető az a tendencia, hogy az eszközök egymásra épülnek és mindegyik célja az, hogy elfedjék szegény programozók elől a problémás részeket.

Ha viszont a lehető leghordozhatóbb megoldásra van szükségünk, akkor célszerű átvágni magunkat az API-k dzsungelén, hogy megleljük a tiszta forrást. Ez nem más, mint az ALSA. Az ALSA segítségével előállíthatunk bármilyen hangot. Szerény véleményem szerint nem olyan bonyolult a használata. Ez azonban nem jelenti azt, hogy nem érhetnek minket meglepetések. Erről majd egy következő bejegyzésben részletesen is írok.

A zenét mi Ogg/Vorbisban tároljuk, ezért a példaprogramban a vorbis könyvtárat is használni fogjuk.

Inicializálás

Első lépés, hogy beállítsuk a kívánt paramétereket. Milyen eszközön, milyen típusú puffert használunk és milyen minőségű hangot kívánunk megszólaltatni.

snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
   snd_pcm_hw_params_malloc(&hw_params);
   snd_pcm_hw_params_any(playback_handle, hw_params);
   snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
   snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);
Minden parancs snd_pcm-el kezdődik. Ezt könnyű lesz megjegyezni. Először meg kell nyitni az eszközt. Az eszköz nevét egy sztring határozza meg. Ha több hangkártya is van a gépbe, akkor explicite megadhatjuk, hogy melyik eszközt használjuk. Ha hordozható megoldásra vágyunk, akkor legyen "default".

A második sorban lefoglaljuk a memóriát a paramétereknek, majd beállítjuk azokat. A dokumentáció részletesen leírja, hogy melyik mit jelent. Ezek a beállítások függetlenek a lejátszani kívánt zenétől.

music_file = fopen(argv[i+1], "r");
ov_open(music_file, &music, NULL, 0);
music_info = ov_info(&music, -1);

A zenefájl megnyitása ennyi. A zene jellemzői a music_info struktúrába kerülnek. Ezt fogjuk felhasználni, hogy beállítsuk az ALSA-nak a hangjellemzőket.

snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &music_info->rate, 0);
   snd_pcm_hw_params_set_channels(playback_handle, hw_params, music_info->channels);
   snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &frames, &dir);

   snd_pcm_hw_params(playback_handle, hw_params);
   snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir);
   size = frames * 4;
   buffer = (char *)malloc(size);
   snd_pcm_hw_params_get_period_time(hw_params, &music_info->rate, &dir);
   snd_pcm_hw_params_free(hw_params);
   snd_pcm_prepare(playback_handle);
Ez már kellően elrettentő. A lényege röviden, hogy mi nem adhatjuk meg pontosan a paramétereket, mert az egyes hangkártyák esetleg nem ismerik azt a beállítást. Ezért mi csak annyit mondunk, hogy körül-belül ilyen beállításokat akarunk (snd_pcm_hw_params_set_period_size_near), majd a meghajtóprogram kiválasztja azt, ami a legközelebb esik hozzá, amivel mi már beállíthatjuk a kívánt paramétert (snd_pcm_hw_params_set_period_size). Beállítjuk továbbá a hangpuffer jellemzőit is.

A size = frames * 4 sor egy kis magyarázatot igényel. Az ALSA a pufferben két adatot vár, mivel sztereóban játszunk le. De a másik kettő honnan jön? Azt nem tudom. Bármilyen más értékre átírom, a mintavételezés olyan rossz lesz, hogy élvezhetetlenné válik a zene.

sample_length = ov_read(&music, buffer + pcm_index, bytes_needed, 0, 2, 1, &bitstream);
ret = snd_pcm_writei(playback_handle, buffer, frames);

Gyakorlatilag ennyi a további kód. Az ov_read segítségével kiolvassuk a következő puffernyi adatot, majd az snd_pcm_writei-vel lejátszuk.

A teljes kód:

#include <vorbis/vorbisfile.h>
#include <vorbis/codec.h>
#include <alsa/asoundlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/*
   This program is try to play an ogg-vorbis file. This version
   is working with only in 1 bitstream files.
*/

int main(int argc, char *argv[]){
   int i;
   FILE *music_file;
   OggVorbis_File music;
   vorbis_info *music_info;
   snd_pcm_uframes_t frames = 128;
   snd_pcm_sframes_t ret;
   long sample_length;
   char *buffer;
   int size;
   int bitstream;
   int playing = 1;
   int bytes_needed;
   int pcm_index;
   int dir;

   snd_pcm_hw_params_t *hw_params;
   snd_pcm_t *playback_handle;

   for(i = 1; i < argc; i++){
      if(!strcmp(argv[i], "-f")) music_file = fopen(argv[i+1], "r");
   }

   /* Alsa initialization */
   snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
   snd_pcm_hw_params_malloc(&hw_params);
   snd_pcm_hw_params_any(playback_handle, hw_params);
   snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
   snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);

   /* Open the music*/
   ov_open(music_file, &music, NULL, 0);

   /* Get the sample rate information from vorbis file */
   music_info = ov_info(&music, -1);

   /* Set the alsa channels and rate */
   snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &music_info->rate, 0);
   snd_pcm_hw_params_set_channels(playback_handle, hw_params, music_info->channels);
   snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &frames, &dir);

   snd_pcm_hw_params(playback_handle, hw_params);
   snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir);
   size = frames * 2;
   buffer = (char *)malloc(size);
   snd_pcm_hw_params_get_period_time(hw_params, &music_info->rate, &dir);
   snd_pcm_hw_params_free(hw_params);
   snd_pcm_prepare(playback_handle);

  
   while( playing ){
      bytes_needed = size;
      pcm_index = 0;

      while(bytes_needed > 0){
         sample_length = ov_read(&music, buffer + pcm_index, bytes_needed, 0, 2, 1, &bitstream);
         switch(sample_length){
            case 0:
               playing = 0;
               bytes_needed = 0;
               break;
            default:
               pcm_index    += sample_length;
               bytes_needed -= sample_length;
         }
      }
      ret = snd_pcm_writei(playback_handle, buffer, frames);
      if(ret < 0){
         ret = snd_pcm_recover(playback_handle, ret, 0);
         printf("Recover\n");
      }
      if(ret < 0){
         printf("Underrun: %s\n", snd_strerror(ret));
      }
   }

   snd_pcm_close(playback_handle);
   ov_clear(&music);
   return(EXIT_SUCCESS);
}

Szólj hozzá!

Címkék: programozás demoscene

A bejegyzés trackback címe:

https://cybernetic.blog.hu/api/trackback/id/tr212935286

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.