2013-03-21 42 views
1

我正在開發一個非常簡單的音頻播放器。它使用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; 
} 

回答

2

您的代碼

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