3

私は非常にシンプルなオーディオプレーヤーを開発しています。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;
}

必要に応じて、サウンドを再生するためのコードを提供します。

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

1 に答える 1

2

あなたのコード

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

Gtk メインループの進行を止めています。GUI アプリケーションの場合、コールバックのループ中に長時間実行することはできません。そうしないと、GUI がフリーズします。いくつかの解決策があります

1)次のようなものを使用して、whileループ内で実行ループを駆動します

...

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

...

これは、オーディオ ループが実行されるたびに、蓄積されたすべての gtk イベントが処理されることを意味します。

2) アイドル状態のコールバックからオーディオを駆動します。これにより、GUI は通常どおり動作し、ビジー状態でないときにオーディオ コールバックが呼び出され、次のバッファを再生できます。

3) 別のスレッドでオーディオ ループを駆動します。これにより、メイン スレッドで GUI が通常どおり動作し、オーディオが再生されます。これには、スレッド化プログラミングの標準的な危険がすべて含まれています。

4) Portaudio には、非同期コールバック駆動モデルがあります。これを使用すると、Portaudio がさらにデータを必要とするときにオーディオ コールバックが呼び出されます。そのコールバックでファイル アクセスやメモリ割り当てを行うべきではないため、これも非常に困難です。詳細については、Portaudio コールバック ドキュメントを参照してください。

正直なところ、この基本レベルでのオーディオ プログラミングは複雑です。(たとえば) GStreamerのようなフレームワークを使用することをお勧めします。これらの複雑さはすべて既に解決されています。

于 2013-03-21T18:10:07.977 に答える