2016-08-23 174 views
1

我最近感兴趣的是'如何在Linux上播放音乐',因为我想将数学和音乐连接在一起。我想用系统调用来做到这一点,因为这样我就不必使用像*.mp3*.wav等音乐文件。我已经在互联网上进行了研究,但只有诸如“如何播放音乐文件在程序中'。linux如何播放音乐(ALSA)

我以为在linux上有一个设备文件,如LED(/sys/class/leds/.../brightness)或usbs(/dev/usb/)。但是在我的电脑上没有/dev/audio/dev/dsp,/dev/sound

所以我想知道linux如何播放音乐文件,并从那里开始实现我的目标。

我的问题是 “如何播放音乐[文件]在linux”,但INSTEAD “Linux下如何播放音乐(ALSA)”。关于“如何在节目中播放音调”的答案也是可以接受的。

回答

1

在很多方面你的问题就像“有人可以向我解释如何捕鱼?”。有很多方法和很多工具可用,每个答案虽然在技术上是正确的,但仅仅说明了从接触拖网渔船的那个人,通过飞行渔民到长矛渔民这样回答的人的方式。
在Linux上的音频是一个像狂野西部的水一样的主题,“威士忌的喝酒,水的战斗”。 只是为了好玩尝试以下链接为复杂的想法:

https://ubuntuforums.org/showthread.php?t=843012

http://alsa.opensrc.org/MultipleCards

但给你一个“音”的例子,可以在命令行中运行(和可写成代码,Python和C某些)加载的GStreamer-1.0在你的箱子,并运行以下命令:

gst-launch-1.0 audiotestsrc freq=329.63 volume=0.5 ! autoaudiosink 

gst-launch-1.0 audiotestsrc freq=987.77 ! autoaudiosink 

gst-launch-1.0 audiotestsrc wave=2 freq=200 volume=0.2 ! tee name=t ! queue ! audioconvert ! autoaudiosink t. ! queue ! audioconvert ! libvisual_lv_scope ! videoconvert ! autovideosink 

然后检查:
https://gstreamer.freedesktop.org/documentation/plugins.html

注意:gstreamer只是飞渔民的故事,这就是交谈!

下面是一些基于GTK代码你一起玩:

#!/usr/bin/env python 
import gi 
gi.require_version('Gst', '1.0') 
from gi.repository import Gst, GObject, Gtk 
class Tone(object): 

    def __init__(self): 
     window = Gtk.Window(Gtk.WindowType.TOPLEVEL) 
     window.set_title("Tone-Player") 
     window.set_default_size(500, 200) 
     window.connect("destroy", Gtk.main_quit, "WM destroy") 
     vbox = Gtk.VBox() 
     window.add(vbox) 
     self.tone_entry = Gtk.Entry() 
     self.tone_entry.set_text('300.00') 
     vbox.pack_start(self.tone_entry, False, False, 0) 
     self.button = Gtk.Button("Start") 
     vbox.add(self.button) 
     self.button.connect("clicked", self.start_stop) 
     window.show_all() 

     self.player = Gst.Pipeline.new("player") 
     source = Gst.ElementFactory.make("audiotestsrc", "tone-source") 
     audioconv = Gst.ElementFactory.make("audioconvert", "converter") 
     audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-output") 
     self.player.add(source) 
     self.player.add(audioconv) 
     self.player.add(audiosink) 
     source.link(audioconv) 
     audioconv.link(audiosink) 

    def start_stop(self, w): 
     if self.button.get_label() == "Start": 
       self.button.set_label("Stop") 
       tone = float(self.tone_entry.get_text()) 
       self.player.get_by_name("tone-source").set_property("freq", tone) 
       self.player.set_state(Gst.State.PLAYING) 
     else: 
      self.player.set_state(Gst.State.NULL) 
      self.button.set_label("Start") 

GObject.threads_init() 
Gst.init(None) 
Tone() 
Gtk.main() 
+0

有没有办法一次播放两个频率? – Chromium

+0

以最简单的方式创建self.player2,方式与self.player相同,它将提供组合输出。我想可能有办法将它们左右分开,但是你必须做自己的研究。 –

+0

'audioconv'和'audiosink'有什么用处? – Chromium

1

ALSA是一个支持众多声卡的内核驱动程序。它通常用于想要与声音系统直接交互的低级应用程序。

ALSA提供您可以使用的library API。看一看documentation的一些例子,并帮助正确的方向。

使用ALSA,您可以访问缓冲区并将样本放入该缓冲区,并由声音设备播放。这是用PCM(脉码调制)完成的。使用ALSA你有很多配置(见here)。您想配置通道数量(单声道,立体声等),采样大小(8位,16位等),速率(8000Hz,16000Hz,44100Hz等)。例如,您可以使用snd_pcm_writei将这些样本写入PCM设备。

ALSA库的定义位于alsa/asoundlib.h。如果您使用的是GCC,那么您可以使用-lasound与ALSA库链接。

并非所有的音乐播放器都会使用这些低级别的互动。许多软件都建立在ALSA之上,为声音系统提供更多通用接口(甚至平台独立)。声音服务器的示例包括JACKPulseAudio。这些声音服务器的优势在于它们通常更容易设置和使用,但不能给予您对ALSA的精确控制。

+0

我应该包括头'alsa/asoundlib.h'使用函数?或者是该文件?而且,这是大多数音乐播放器将如何使用的方法? – Chromium

+0

@Chromium我添加了更多信息。 – bzeaman

+0

明白了。既然你提到'pulseaudio',它是否也有一个库?你能提供这些信息吗? – Chromium

2

为了让LINUX能够播放声音(任何种类,如mp3/wave等),它使用一个库就可以使用ALSA。请参阅here。 ASLA项目支持许多声卡,您可以在他们的WiKi中看到如何查看声卡是否受支持以及如何测试声卡的一些技巧。

如果您要添加的驱动程序新声卡,你应该记住,有一些需要处理2个独立的流向:

  1. 配置HW(CODEC) - 通常这是由通过I2C总线,并配置h/w。例如:设置均衡器,设置/单声道/立体声,设置模拟放大器等。
  2. 数据流 - 数据是文件从linux堆栈到h/w的实际流式传输。您需要创建缓冲区等来处理数据流,你可以使用ALSA API来启动/停止录音/播放

这是极其广阔的领域,因此是更好地看到一些尝试之前已经存在的实例编写你自己的驱动程序。 尝试在ALSA代码中添加打印以查看开始播放音频文件时发生了什么。

0

下面是一些C代码中填入,然后使用OpenAL的呈现的音频数据的内存缓冲区 - 没有音频文件

// sudo apt-get install libopenal-dev 
// 
// gcc -o gen_tone gen_tone.c -lopenal -lm 
// 

#include <stdio.h> 
#include <stdlib.h> // gives malloc 
#include <math.h> 


#ifdef __APPLE__ 
#include <OpenAL/al.h> 
#include <OpenAL/alc.h> 
#elif __linux 
#include <AL/al.h> 
#include <AL/alc.h> 
#endif 

ALCdevice * openal_output_device; 
ALCcontext * openal_output_context; 

ALuint internal_buffer; 
ALuint streaming_source[1]; 

int al_check_error(const char * given_label) { 

    ALenum al_error; 
    al_error = alGetError(); 

    if(AL_NO_ERROR != al_error) { 

     printf("ERROR - %s (%s)\n", alGetString(al_error), given_label); 
     return al_error; 
    } 
    return 0; 
} 

void MM_init_al() { 

    const char * defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); 

    openal_output_device = alcOpenDevice(defname); 
    openal_output_context = alcCreateContext(openal_output_device, NULL); 
    alcMakeContextCurrent(openal_output_context); 

    // setup buffer and source 

    alGenBuffers(1, & internal_buffer); 
    al_check_error("failed call to alGenBuffers"); 
} 

void MM_exit_al() { 

    ALenum errorCode = 0; 

    // Stop the sources 
    alSourceStopv(1, & streaming_source[0]);  //  streaming_source 
    int ii; 
    for (ii = 0; ii < 1; ++ii) { 
     alSourcei(streaming_source[ii], AL_BUFFER, 0); 
    } 
    // Clean-up 
    alDeleteSources(1, &streaming_source[0]); 
    alDeleteBuffers(16, &streaming_source[0]); 
    errorCode = alGetError(); 
    alcMakeContextCurrent(NULL); 
    errorCode = alGetError(); 
    alcDestroyContext(openal_output_context); 
    alcCloseDevice(openal_output_device); 
} 

void MM_render_one_buffer() { 

    /* Fill buffer with Sine-Wave */ 
    // float freq = 440.f; 
    float freq = 850.f; 
    float incr_freq = 0.1f; 

    int seconds = 4; 
    // unsigned sample_rate = 22050; 
    unsigned sample_rate = 44100; 
    double my_pi = 3.14159; 
    size_t buf_size = seconds * sample_rate; 

    short * samples = malloc(sizeof(short) * buf_size); 

    printf("\nhere is freq %f\n", freq); 
    int i=0; 
    for(; i<buf_size; ++i) { 
     samples[i] = 32760 * sin((2.f * my_pi * freq)/sample_rate * i); 

     freq += incr_freq; 
     // incr_freq += incr_freq; 
     // freq *= factor_freq; 

     if (100.0 > freq || freq > 5000.0) { 

      incr_freq *= -1.0f; 
     } 
    } 

    /* upload buffer to OpenAL */ 
    alBufferData(internal_buffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate); 
    al_check_error("populating alBufferData"); 

    free(samples); 

    /* Set-up sound source and play buffer */ 
    // ALuint src = 0; 
    // alGenSources(1, &src); 
    // alSourcei(src, AL_BUFFER, internal_buffer); 
    alGenSources(1, & streaming_source[0]); 
    alSourcei(streaming_source[0], AL_BUFFER, internal_buffer); 
    // alSourcePlay(src); 
    alSourcePlay(streaming_source[0]); 

    // --------------------- 

    ALenum current_playing_state; 
    alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state); 
    al_check_error("alGetSourcei AL_SOURCE_STATE"); 

    while (AL_PLAYING == current_playing_state) { 

     printf("still playing ... so sleep\n"); 

     sleep(1); // should use a thread sleep NOT sleep() for a more responsive finish 

     alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state); 
     al_check_error("alGetSourcei AL_SOURCE_STATE"); 
    } 

    printf("end of playing\n"); 

    /* Dealloc OpenAL */ 
    MM_exit_al(); 

} // MM_render_one_buffer 

int main() { 

    MM_init_al(); 

    MM_render_one_buffer(); 
} 

它也有可能将其转换为音频服务器,呈现音频,而内存缓冲区是重复填充