2016-08-23 163 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(); 
} 

它也有可能將其轉換爲音頻服務器,呈現音頻,而內存緩衝區是重複填充