簡體   English   中英

linux如何播放音樂(ALSA)

[英]How does linux play a piece of music (ALSA)

我最近對“如何在Linux中演奏音色”感興趣,因為我想將數學和音樂聯系在一起。 我想使用系統調用來執行此操作,因為那樣就不必使用*.mp3*.wav等音樂文件。我已經在互聯網上進行了研究,但只有諸如“如何在程序中播放音樂文件”。

我認為Linux上有一個設備文件,如leds( /dev/usb/ sys /dev/usb/ class /dev/usb/ leds /sys/class/leds/.../brightness )或usbs( /dev/usb/ )。 但是我的計算機上沒有/dev/audio/dev/dsp/dev/sound

因此,我想知道linux如何播放音樂文件,並從那里開始實現我的目標。

我的問題不是 “如何在Linux中播放音樂[文件]”,而是INSTEAD “ Linux如何播放音樂(ALSA)”。 也可以回答“如何在程序中播放音調”。

ALSA是一個內核驅動程序,為許多聲卡提供支持。 通常由想要與聲音系統直接交互的低級應用程序使用。

ALSA提供了可以使用的庫API 查看文檔中的一些示例,並向正確的方向尋求幫助。

使用ALSA,您可以訪問一個緩沖區,並將樣本放入其中,該聲音將由聲音設備播放。 這是通過PCM(脈沖編碼調制)完成的。 隨着ALSA你有很多的配置(如看到這里 )。 您要配置通道數量(單聲道,立體聲等),樣本大小(8位,16位等),速率(8000 Hz,16000 Hz,44100 Hz等)。 例如,您可以使用snd_pcm_writei將這些樣本寫入PCM設備。

ALSA庫的定義位於alsa/asoundlib.h 如果使用的是GCC,則可以使用-lasound與ALSA庫-lasound

並非所有音樂播放器都會使用這些低級交互。 許多軟件都建立在ALSA之上,可以為聲音系統提供更多通用接口(甚至與平台無關)。 聲音服務器的示例包括JACKPulseAudio 這些聲音服務器的優點是它們通常更易於設置和使用,但是卻無法像使用ALSA那樣進行精細控制。

為了使LINUX能夠播放聲音(任何形式,例如mp3 / wave /等),它可以使用ALSA庫。 這里 ASLA項目支持許多聲卡,您可以在其WiKi中看到一些技巧,了解如何查看聲卡是否受支持以及如何對其進行測試。

如果要為新的聲卡添加驅動程序,則應記住,有兩個單獨的流程需要處理:

  1. 配置硬件(CODEC)-通常這是通過I2C總線完成的,並且配置了硬件。 例如:設置均衡器,設置/單聲道/立體聲,設置模擬放大器等。
  2. 數據流-數據是文件從linux堆棧到硬件的實際流式傳輸。 您將需要創建緩沖區等來處理流,並且可以使用ALSA API進行開始/停止記錄/播放

這是一個非常廣闊的領域,因此最好在嘗試編寫自己的驅動程序之前先看一些已經存在的示例。 嘗試沿着ALSA代碼添加打印件,以查看開始播放音頻文件時發生了什么。

在許多方面,您的問題就像“有人可以向我解釋如何捕撈魚嗎?”。 可用的方法和工具太多了,每個答案雖然在技術上都是正確的,但僅說明了回答者的方式,即從拖網漁船的工作人員到蠅釣者再到長矛漁夫。
linux上的音頻是一個主題,例如狂野西部的水,“威士忌是喝酒,水是爭奪水”。 只是為了好玩,請嘗試以下鏈接以了解復雜性:

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

http://alsa.opensrc.org/MultipleCards

但是,給您一個可以從命令行運行的“ Tone”示例(可以將其寫入代碼,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()

這是一些用音頻數據填充內存緩沖區的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();
}

也可以將其轉換為音頻服務器,以便在重復填充內存緩沖區時呈現音頻

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM