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

问题描述:

我正在开发一个非常简单的音频播放器。它使用Gtk和portaudio/libsndfile。我创建了一个简单的测试界面,包含浏览,播放等几个按钮。我的播放器正确选择文件名,按下播放按钮后开始播放。但是一切都等待播放完成。没有什么是积极的,我想知道如何在它自己完成之前阻止它。Gtk音频播放器。如何在歌曲结束前停止声音

我为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; 
} 

如果有必要,我给我的代码,用于播放声音:

#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; 
} 

您的代码

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

被停止GTK的主循环进展。对于GUI应用程序,当回调中出现循环或GUI将冻结时,不能长时间运行。有几个解决方案

1)使用类似

... 

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

... 

这意味着,每次执行音频循环时间,已经建立了所有GTK事件将驱动while循环中运行循环处理。

2)驱动闲置回调的音频。这可以使GUI像正常一样工作,然后当它不忙时,您的音频回调将被调用,并且您可以播放下一个缓冲区。

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

4)Portaudio有一个异步回调驱动模型。使用它,当Portaudio需要更多数据时,你的音频回调将被调用。这也很难,因为你不应该在该回调中进行文件访问或内存分配。一些更多细节在这里:Portaudio callback documentation

说实话,在这个基础层面上的音频编程是复杂的,我建议你使用一个像(例如)GStreamer这样的框架已经解决了所有这些复杂性。

+0

谢谢,伊恩!要检查GStreamer。 – Cecil 2013-03-21 18:23:08