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这样的框架已经解决了所有这些复杂性。
谢谢,伊恩!要检查GStreamer。 – Cecil 2013-03-21 18:23:08