linux声音处理 alsa & jack 是什么

发布时间 2023-12-26 09:40:32作者: 大风吹屁股

alsa(Advanced Linux Sound Architecture )

是什么:

简单的说,linux之前的内核里处理声音使用oss,后来大家觉得不好用,重写了声音部分,改名叫alsa(更高级的),并且提供了用户空间库(alsa-lib),供应用程序方便调用。

官方说明:

高级 Linux 声音架构 (ALSA) 为 Linux 操作系统提供音频和 MIDI 功能。ALSA具有以下显着特点:

  • 有效支持所有类型的音频接口,从消费级声卡到专业的多声道音频接口。
  • 完全模块化的声音驱动程序。
  • SMP 和线程安全设计(请阅读本文)。
  • 用户空间库 (alsa-lib) 可简化应用程序编程并提供更高级别的功能。
  • 支持旧的开放声音系统 (OSS) API,为大多数 OSS 程序提供二进制兼容性。

使用:

安装

apt-get(sudo apt-get install libasound2-dev libasound2)

 

这是实际使用的代码。请注意,我们在一个文件 alsatut1.cpp 中完成了所有操作,没有头文件:

g++ -oalsatut1 alsatut1.cpp -lasound

然后,您可以在命令行上使用 ./alsatut1 运行该程序。运气好的话,它会成功初始化。

 

#include <alsa/asoundlib.h>
#include <iostream>
using namespace std;
// Globals are generally a bad idea in code.  We're using one here to keep it simple.
snd_pcm_t * _soundDevice;

bool Init(const char *name)
{
  int i;
  int err;
  snd_pcm_hw_params_t *hw_params;

  if( name == NULL )
  {
      // Try to open the default device
      err = snd_pcm_open( &_soundDevice, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0 );
  }
  else
  {
      // Open the device we were told to open.
      err = snd_pcm_open (&_soundDevice, name, SND_PCM_STREAM_PLAYBACK, 0);
  }

  // Check for error on open.
  if( err < 0 )
  {
      cout << "Init: cannot open audio device " << name << " (" << snd_strerror (err) << ")" << endl;
      return false;
  }
  else
  {
      cout << "Audio device opened successfully." << endl;
  }

  // Allocate the hardware parameter structure.
  if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
  {
      cout << "Init: cannot allocate hardware parameter structure (" << snd_strerror (err) << ")" << endl;
      return false;
  }

  if ((err = snd_pcm_hw_params_any (_soundDevice, hw_params)) < 0)
  {
      cout << "Init: cannot initialize hardware parameter structure (" << snd_strerror (err) << ")" << endl;
      return false;
  }

  // Enable resampling.
  unsigned int resample = 1;
  err = snd_pcm_hw_params_set_rate_resample(_soundDevice, hw_params, resample);
  if (err < 0)
  {
      cout << "Init: Resampling setup failed for playback: " << snd_strerror(err) << endl;
      return err;
  }

  // Set access to RW interleaved.
  if ((err = snd_pcm_hw_params_set_access (_soundDevice, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
  {
      cout << "Init: cannot set access type (" << snd_strerror (err) << ")" << endl;
      return false;
  }

  if ((err = snd_pcm_hw_params_set_format (_soundDevice, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
  {
      cout << "Init: cannot set sample format (" << snd_strerror (err) << ")" << endl;
      return false;
  }

  // Set channels to stereo (2).
  if ((err = snd_pcm_hw_params_set_channels (_soundDevice, hw_params, 2)) < 0)
  {
      cout << "Init: cannot set channel count (" << snd_strerror (err) << ")" << endl;
      return false;
  }

  // Set sample rate.
  unsigned int actualRate = 44100;
  if ((err = snd_pcm_hw_params_set_rate_near (_soundDevice, hw_params, &actualRate, 0)) < 0)
  {
      cout << "Init: cannot set sample rate to 44100. (" << snd_strerror (err) << ")"  << endl;
      return false;
  }
  if( actualRate < 44100 )
  {
      cout << "Init: sample rate does not match requested rate. (" << "44100 requested, " << actualRate << " acquired)" << endl;
  }

  // Apply the hardware parameters that we've set.
  if ((err = snd_pcm_hw_params (_soundDevice, hw_params)) < 0)
  {
      cout << "Init: cannot set parameters (" << snd_strerror (err) << ")" << endl;
      return false;
  }
  else
  {
     cout << "Audio device parameters have been set successfully." << endl;
  }

  // Get the buffer size.
  snd_pcm_uframes_t bufferSize;
  snd_pcm_hw_params_get_buffer_size( hw_params, &bufferSize );
  // If we were going to do more with our sound device we would want to store
  // the buffer size so we know how much data we will need to fill it with.
  cout << "Init: Buffer size = " << bufferSize << " frames." << endl;

  // Display the bit size of samples.
  cout << "Init: Significant bits for linear samples = " << snd_pcm_hw_params_get_sbits(hw_params) << endl;

  // Free the hardware parameters now that we're done with them.
  snd_pcm_hw_params_free (hw_params);

  // Prepare interface for use.
  if ((err = snd_pcm_prepare (_soundDevice)) < 0)
  {
      cout << "Init: cannot prepare audio interface for use (" << snd_strerror (err) << ")" << endl;
      return false;
  }
  else
  {
      cout << "Audio device has been prepared for use." << endl;
  }

  return true;
}

bool UnInit()
{
  snd_pcm_close (_soundDevice);
  cout << "Audio device has been uninitialized." << endl;
  return true;
}

int main( int argv, char **argc )
{
        Init(NULL);
        UnInit();
        return 0;
}





 

jack:

 

是什么:

JACK 是一个低延迟音频服务器.

它专注于两个关键领域:所有客户端的同步执行和低延迟操作。

它是一个应用端程序,而不是操作系统的驱动,所以jack需要和读写alsa驱动,实际jack内部需要alsa-lib库来支持alsa硬件。

因为只是一个应用程序,所以jack可以支持多操作系统。

 

使用:

sudo apt-get install jack-dev

 

/** @file simple_client.c
 *
 * @brief This simple client demonstrates the most basic features of JACK
 * as they would be used by many applications.
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <jack/jack.h>

#define AMPLITUDE 0.01

jack_port_t *output_port1, *output_port2;
jack_client_t *client;


float randnorm = 1.0/(float)(RAND_MAX);

/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 *
 * This client does nothing more than copy data from its input
 * port to its output port. It will exit when stopped by 
 * the user (e.g. using Ctrl-C on a unix-ish operating system)
 */
int
process (jack_nframes_t nframes, void *arg)
{
  jack_default_audio_sample_t *out1, *out2;

  out1 = jack_port_get_buffer (output_port1, nframes);
  out2 = jack_port_get_buffer (output_port2, nframes);

  for (int i=0; i<nframes; i++) {
    float rand1 = 0.0;
    float rand2 = 0.0;

    for (int j=0;j<4;j++) {
      rand1 += ((float)rand()) * randnorm;
      rand2 += ((float)rand()) * randnorm;
    }
    out1[i] = AMPLITUDE * (rand1 - 2);
    out2[i] = AMPLITUDE * (rand2 - 2);
  }
	return 0;
}

/**
 * JACK calls this shutdown_callback if the server ever shuts down or
 * decides to disconnect the client.
 */
void
jack_shutdown (void *arg)
{
	exit (1);
}

int
main (int argc, char *argv[])
{
	const char *client_name = "whitenoise";
	const char *server_name = NULL;
	jack_options_t options = JackNullOption;
	jack_status_t status;
	
	/* open a client connection to the JACK server */

	client = jack_client_open (client_name, options, &status, server_name);
	if (client == NULL) {
		fprintf (stderr, "jack_client_open() failed, "
			 "status = 0x%2.0x\n", status);
		if (status & JackServerFailed) {
			fprintf (stderr, "Unable to connect to JACK server\n");
		}
		exit (1);
	}
	if (status & JackServerStarted) {
		fprintf (stderr, "JACK server started\n");
	}
	if (status & JackNameNotUnique) {
		client_name = jack_get_client_name(client);
		fprintf (stderr, "unique name `%s' assigned\n", client_name);
	}

	/* tell the JACK server to call `process()' whenever
	   there is work to be done.
	*/

	jack_set_process_callback (client, process, 0);

	/* tell the JACK server to call `jack_shutdown()' if
	   it ever shuts down, either entirely, or if it
	   just decides to stop calling us.
	*/

	jack_on_shutdown (client, jack_shutdown, 0);

	/* display the current sample rate. 
	 */

	printf ("engine sample rate: %" PRIu32 "\n",
		jack_get_sample_rate (client));

	output_port1 = jack_port_register (client, "output_1",
					  JACK_DEFAULT_AUDIO_TYPE,
					  JackPortIsOutput, 0);
	output_port2 = jack_port_register (client, "output_2",
					  JACK_DEFAULT_AUDIO_TYPE,
					  JackPortIsOutput, 0);

	if ((output_port1 == NULL) || (output_port2 == NULL)) {
		fprintf(stderr, "no more JACK ports available\n");
		exit (1);
	}

	/* Tell the JACK server that we are ready to roll.  Our
	 * process() callback will start running now. */

	if (jack_activate (client)) {
		fprintf (stderr, "cannot activate client");
		exit (1);
	}

	/* keep running until stopped by the user */

	sleep (-1);

	/* this is never reached but if the program
	   had some other way to exit besides being killed,
	   they would be important to call.
	*/

	jack_client_close (client);
	exit (0);
}

 

总结

alsa是linux内核驱动,并且有一个用户端库供用户使用。

jack是用户端程序,提供专业的低延时易用功能,供音频处理专业人士使用。

参考网址:

Tutorial: ALSA Tutorial 1 - Initialization

JACK Audio Connection Kit

 https://zhuanlan.zhihu.com/p/600338119

Demystifying JACK - A Beginners Guide to Getting Started with JACK