Gtk音频播放器。如何在歌曲结束前停止声音

Gtk音频播放器。如何在歌曲结束前停止声音,gtk,audio-player,portaudio,libsndfile,Gtk,Audio Player,Portaudio,Libsndfile,我正在开发一个非常简单的音频播放器。它使用Gtk和portaudio/libsndfile。我已经创建了一个简单的测试界面,只有几个按钮,如浏览、播放等。我的播放器正确地选择了文件名,按下播放键后开始播放。但一切都在等待播放完成。没有任何东西是活动的,我想知道如何在它自己完成之前阻止它 我的Gtk代码是: #include <stdio.h> #include <gtk\gtk.h> static GtkWidget *window; const char *filena

我正在开发一个非常简单的音频播放器。它使用Gtk和portaudio/libsndfile。我已经创建了一个简单的测试界面,只有几个按钮,如浏览、播放等。我的播放器正确地选择了文件名,按下播放键后开始播放。但一切都在等待播放完成。没有任何东西是活动的,我想知道如何在它自己完成之前阻止它

我的Gtk代码是:

#include <stdio.h>
#include <gtk\gtk.h>
static GtkWidget *window;
const char *filename;

static void play_file (GtkButton *button, gpointer data)
{
    sndFile_play(filename);
}
static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data )
{
    return FALSE;
}

static void destroy( GtkWidget *widget,
                     gpointer   data )
{
    gtk_main_quit ();
}

int main( int   argc,
          char *argv[] )
{

    GtkWidget *button;
    GtkWidget *box1;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "MyPlayer");

    g_signal_connect (window, "delete-event",
              G_CALLBACK (delete_event), NULL);

    g_signal_connect (window, "destroy",
              G_CALLBACK (destroy), NULL);

    gtk_container_set_border_width (GTK_CONTAINER (window), 20);

    box1 = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), box1);

    button = gtk_button_new_with_label ("Browse...");
    g_signal_connect (button, "clicked",
        G_CALLBACK (browse_clicked), NULL); //not given here
    gtk_box_pack_start (GTK_BOX(box1), button, TRUE, TRUE, 0);
    gtk_widget_show (button);

    button = gtk_button_new_with_label ("Play");
    g_signal_connect (button, "clicked",
        G_CALLBACK (play_file), NULL);
    gtk_box_pack_start (GTK_BOX(box1), button, TRUE, TRUE, 0);
    gtk_widget_show (button);

    gtk_widget_show (box1);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}
#包括
#包括
静态GtkWidget*窗口;
常量字符*文件名;
静态无效播放文件(GTK按钮*按钮,gpointer数据)
{
sndFile_play(文件名);
}
静态gboolean delete_事件(GtkWidget*小部件,
GdkEvent*事件,
gpointer数据)
{
返回FALSE;
}
静态无效销毁(GtkWidget*小部件,
gpointer数据)
{
gtk_main_quit();
}
int main(int argc,
字符*argv[]
{
GtkWidget*按钮;
GtkWidget*框1;
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
gtk_窗口设置标题(gtk_窗口(窗口),“MyPlayer”);
g_信号连接(窗口,“删除事件”,
G_回调(删除_事件),NULL);
g_信号_连接(窗口,“销毁”,
G_回调(销毁),空);
gtk_容器设置_边框宽度(gtk_容器(窗口),20);
框1=gtk_hbox_新(假,0);
gtk_容器添加(gtk_容器(窗口),第1框);
按钮=gtk_按钮_新建_,带有标签(“浏览…”);
g_信号连接(按钮,“点击”,
G_回调(单击浏览),NULL);//此处未给出
gtk_-box_-pack_启动(gtk_-box(框1),按钮,真,真,0);
gtk_小部件_显示(按钮);
按钮=gtk_按钮_新_,带有标签(“播放”);
g_信号连接(按钮,“点击”,
G_回调(播放_文件),NULL);
gtk_-box_-pack_启动(gtk_-box(框1),按钮,真,真,0);
gtk_小部件_显示(按钮);
gtk_widget_show(框1);
gtk_widget_show(窗口);
gtk_main();
返回0;
}
如果有必要,我会给出播放声音的代码:

#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#include <sndfile.h>

#define FRAMES_PER_BUFFER (1024)
#define PA_SAMPLE_TYPE  paInt16
typedef short SAMPLE;
#define BUFFER_LEN  128
#define MAX_CHANNELS 2

int sndFile_play (const char *infilename){
    PaStreamParameters outputParameters;
    PaStream *stream;
    PaError err;
    static short data[BUFFER_LEN];
    SNDFILE      *infile;
    SF_INFO     sfinfo;
    sf_count_t  readcount;
    int channels;
    int srate;

    err = Pa_Initialize();

    if ( !( infile = sf_open( infilename, SFM_READ, &sfinfo ) ) ) {
        printf ("Not able to open input file %s.\n", infilename);
        puts (sf_strerror (NULL));
        return  1;
    }

    if ( sfinfo.channels > MAX_CHANNELS ){
        printf ("Not able to process more than %d channels\n", MAX_CHANNELS);
        return  1;
    }
    /* FILE INFO */

    channels = sfinfo.channels; // убрать channels
    printf("number of channels %d\n", sfinfo.channels);

    srate = sfinfo.samplerate; //убрать 
    printf("sample rate %d\n", sfinfo.samplerate);

    outputParameters.device = Pa_GetDefaultOutputDevice();
    outputParameters.channelCount = sfinfo.channels;
    outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    printf("Begin playback.....\n"); fflush(stdout);
    err = Pa_OpenStream(
        &stream,
        NULL,
        &outputParameters,
        sfinfo.samplerate,
        FRAMES_PER_BUFFER,
        paClipOff,
        NULL,
        NULL );

    if( stream ) {

        err = Pa_StartStream( stream );
        printf("Waiting for playback to finish....\n"); fflush(stdout);

        while ( ( readcount = sf_read_short( infile, data,  BUFFER_LEN*sfinfo.channels ) ) ){
            err = Pa_WriteStream( stream, data, BUFFER_LEN );
        }

        err = Pa_CloseStream( stream );
        printf("Done.\n"); fflush(stdout);

    }

    sf_close( infile );

    Pa_Terminate();
    return 0;
}
#包括
#包括
#包括
#包括
#为每个缓冲区定义帧(1024)
#定义PAU样本类型16
typedef短样本;
#定义缓冲区长度128
#定义最大通道2
int sndFile_play(常量字符*填充名){
Pastream参数输出参数;
泛流*流;
错误;
静态短数据[BUFFER_LEN];
SNDFILE*infle;
SF_信息sfinfo;
sf_计数\u t读取计数;
int通道;
int srate;
err=Pa_Initialize();
如果(!(填充=sf_打开(填充名称、SFM_读取和sfinfo))){
printf(“无法打开输入文件%s。\n”,infilename);
put(sf_strerror(NULL));
返回1;
}
如果(sfinfo.channels>MAX_channels){
printf(“无法处理超过%d个通道”,最大\u个通道);
返回1;
}
/*文件信息*/
channels=sfinfo.channels;//бааааааchannels
printf(“通道数%d\n”,sfinfo.channels);
srate=sfinfo.samplerate;//бааааа
printf(“采样率%d\n”,sfinfo.samplerate);
outputParameters.device=Pa_GetDefaultOutputDevice();
outputParameters.channelCount=sfinfo.channels;
outputParameters.sampleFormat=PA_样本_类型;
outputParameters.suggestedLatency=Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo=NULL;
printf(“开始播放…\n”);fflush(标准输出);
err=Pa_OpenStream(
&溪流,
无效的
&输出参数,
sfinfo.samplerate,
每个缓冲区的帧数,
帕利波夫,
无效的
无效);
如果(流){
err=Pa_开始流(流);
printf(“等待播放完成…”\n”);fflush(stdout);
而((readcount=sf_read_short(填充、数据、缓冲区长度*sfinfo.channels))){
err=Pa_WriteStream(流、数据、缓冲区长度);
}
err=Pa_CloseStream(流);
printf(“完成”。\n”);fflush(标准输出);
}
sf_关闭(填充);
Pa_Terminate();
返回0;
}
您的代码

while ((readcount = sf_read_short( infile, data,  BUFFER_LEN*sfinfo.channels ) ) ){
    err = Pa_WriteStream( stream, data, BUFFER_LEN );
}
正在停止Gtk主循环的进行。对于GUI应用程序,在回调中循环时不能长时间运行,否则GUI将冻结。有几种解决办法

1) 在while循环中驱动run循环,方法如下

...

while (gtk_events_pending ()) {
    gtk_main_iteration ();
}

...
这意味着每次执行音频循环时,将处理所有已建立的gtk事件

2) 从空闲回调驱动音频。这允许GUI正常工作,然后当它不忙时,将调用音频回调,您可以播放下一个缓冲区

3) 在单独的线程中驱动音频循环。这将允许GUI在主线程上正常工作,并且音频只会播放。这带来了线程编程的所有标准危险

4) Portaudio有一个异步回调驱动模型。使用该选项,当Portaudio需要更多数据时,您的音频回调将被调用。这也是相当困难的,因为您不应该在回调中进行文件访问或内存分配。有关更多详细信息,请参见:

老实说,这个基础级别上的音频编程是复杂的,我建议您使用一个框架,例如,它已经解决了所有这些复杂问题