如何在Linux上使用C语言产生声音?

12

我需要在Linux的C程序中播放某些音符。在Windows上,可以使用#include <dos.h>并使用直接的函数,例如sound(note/frequency)delay(time in ms)以及不言自明的nosound()

在Linux上是否有任何类似的功能?

谢谢。


2
可能是需要ALSA教程的重复问题。 - Cloud
2
这个链接的重复并没有真正回答问题,它只是链接到一堆教程。 - Uyghur Lives Matter
3
你可能会对libao documentation 感兴趣。这可以让你播放PCM音频数据。如果你只需要类似蜂鸣器的声音,那么也可以使用它。 - francis
1
libsdl 可能是一个选择。 - Basile Starynkevitch
3个回答

9

我喜欢上面关于libao的提示 - 我刚试了一下,效果很不错。这里有一个类似复杂度的示例,使用OpenAL来合成PCM格式的原始音频缓冲区,然后呈现为音频。

// sudo apt-get install libopenal-dev

// gcc -o openal_play_monday   openal_play_monday.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>
#include <unistd.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 = 100.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;

    // allocate PCM audio buffer        
    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;  //  change freq just to make things interesting

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

            incr_freq *= -1.0f;  // toggle direction of freq increment
        }
    }

    /* 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();
}

如果你想更深入地了解OpenAL技术,可以看看这个链接:https://github.com/scottstensland/render-audio-openal
默认情况下,OpenAL可以很好地播放PCM音频缓冲区,但是它需要实现播放流的能力。在这个Github仓库中,我编写了一个使用OpenAL实现播放流媒体的音频服务器。希望你喜欢。

1
alDeleteBuffers(16,&streaming_source [0]);行中的16是什么? - Minimus Heximus
你想使用缓存删除器删除源吗? - Minimus Heximus
请注意,OpenAL的新版本是专有的。 - martinkunev
@martinkunev 不是这样的,请看 https://dev59.com/k2XWa4cB1Zd3GeqPMWOs#11200103 中提到的开源 OpenAL GitHub 仓库,网址为 https://github.com/kcat/openal-soft。 - Scott Stensland
@ScottStensland 这个 Github 仓库是从最后一个开放版本分叉出来的(他们将其更名为 OpenAL Soft)。所以我说的是真的,只是不完整。 - martinkunev

3

Windows使用自己独特的声音架构,因此您可以访问sound()例程。

不同的Linux机器根据安装的软件包可能需要不同的方法。 也许实用程序beep(来自这个stackexchange问题)可以指导您朝着正确的方向前进。


好主意,选择使用 beep 工具。我记得很久以前用过它,但这确实是一个好点子——考虑其他的工具。不过我想知道还有多少人仍然拥有 PC 扬声器,或者甚至知道它们的存在。当机箱不再提供扬声器并且你必须另外购买时,这让我感到困惑,但现在却成为了现实。 - Pryftan

0

一种方法

包括 #include<conio.h> 在main()或者你想要使用的地方调用print("\a")

printf("\a");

第二种方法

包括头文件

#include <windows.h>

并调用函数Beep(500, 500);

Beep(freq, dur);其中freq = beep频率,为int类型,duration也是int类型


3
当然可以。但问题在于它涉及到Linux。Windows的方法是不可移植的。你回答中第二个头文件的名称已经说明了一切。但是,你说的那种方式在这样的系统中也是正确的。 - Pryftan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接