text: /*
  	BASS simple playback test
  	Copyright (c) 1999-2012 Un4seen Developments Ltd.
  */
  
  #include <gtk/gtk.h>
  #include <glade/glade.h>
  #include <stdlib.h>
  #include <string.h>
  #include <regex.h>
  #include "bass.h"
  
  // path to glade file
  #ifndef GLADE_PATH
  #define GLADE_PATH ""
  #endif
  
  GladeXML *glade;
  GtkWidget *win = 0;
  GtkWidget *filesel;
  
  // display error messages
  void Error(const char *es)
  {
  	GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
  		GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s\n(error code: %d)", es, BASS_ErrorGetCode());
  	gtk_dialog_run(GTK_DIALOG(dialog));
  	gtk_widget_destroy(dialog);
  }
  
  #define GetWidget(id) glade_xml_get_widget(glade,id)
  
  void AddListEntry(const char *list, DWORD handle, const char *text)
  {
  	GtkTreeView *tree = GTK_TREE_VIEW(GetWidget(list));
  	GtkListStore *tm = GTK_LIST_STORE(gtk_tree_view_get_model(tree));
  	GtkTreeIter it;
  	gtk_list_store_append(tm, &it);
  	gtk_list_store_set(tm, &it, 0, text, 1, handle, -1);
  }
  
  DWORD GetSelectedHandle(int mode)
  {
  	DWORD handle;
  	char list[10];
  	GtkTreeSelection *ts;
  	GtkTreeModel *tm;
  	GtkTreeIter it;
  	sprintf(list, "list%d", mode);
  	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(GetWidget(list)));
  	if (!gtk_tree_selection_get_selected(ts, &tm, &it)) return 0;
  	gtk_tree_model_get(tm, &it, 1, &handle, -1);
  	return handle;
  }
  
  void WindowDestroy(GtkObject *obj, gpointer data)
  {
  	gtk_main_quit();
  }
  
  gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
  {
  	return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
  }
  
  void AddClicked(GtkButton *obj, gpointer data)
  {
  	int resp = gtk_dialog_run(GTK_DIALOG(filesel));
  	gtk_widget_hide(filesel);
  	if (resp == GTK_RESPONSE_ACCEPT) {
  		char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
  		const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
  		int mode = atoi(objname + 3); // get mode from button name ("addX")
  		switch (mode) {
  			case 1: // add a stream
  				{
  					HSTREAM handle;
  					if (handle = BASS_StreamCreateFile(FALSE, file, 0, 0, 0))
  						AddListEntry("list1", handle, strrchr(file, '/') + 1);
  					else
  						Error("Can't open stream");
  				}
  				break;
  			case 2: // add a music
  				{
  					HMUSIC handle;
  					if (handle = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMP, 1))
  						AddListEntry("list2", handle, strrchr(file, '/') + 1);
  					else
  						Error("Can't load music");
  				}
  				break;
  			case 3: // add a sample
  				{
  					HSAMPLE sam;
  					/* load a sample from "file" and give it a max of 3 simultaneous
  						playings using playback position as override decider */
  					if (sam = BASS_SampleLoad(FALSE, file, 0, 0, 3, BASS_SAMPLE_OVER_POS))
  						AddListEntry("list3", sam, strrchr(file, '/') + 1);
  					else
  						Error("Can't load sample");
  				}
  				break;
  		}
  		g_free(file);
  	}
  }
  
  void RemoveClicked(GtkButton *obj, gpointer data)
  {
  	const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
  	int mode = atoi(objname + 6); // get mode from button name ("removeX")
  	char list[10];
  	sprintf(list, "list%d", mode); // associated list name
  	GtkTreeView *tree = GTK_TREE_VIEW(GetWidget(list));
  	GtkTreeSelection *ts = gtk_tree_view_get_selection(tree);
  	GtkTreeModel *tm;
  	GtkTreeIter it;
  	if (gtk_tree_selection_get_selected(ts, &tm, &it)) {
  		DWORD handle;
  		gtk_tree_model_get(tm, &it, 1, &handle, -1);
  		switch (mode) {
  			case 1:
  				BASS_StreamFree(handle); // free stream
  				break;
  			case 2:
  				BASS_MusicFree(handle); // free MOD music
  				break;
  			case 3:
  				BASS_SampleFree(handle); // free sample
  				break;
  		}
  		gtk_list_store_remove(GTK_LIST_STORE(tm), &it); // remove list entry
  	}
  }
  
  void PlayClicked(GtkButton *obj, gpointer data)
  {
  	DWORD handle;
  	const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
  	int mode = atoi(objname + 4); // get mode from button name ("playX")
  	handle = GetSelectedHandle(mode); // get selected stream/music/sample handle
  	if (mode == 3) { // sample: get a channel and set volume=50% and pan=random
  		handle = BASS_SampleGetChannel(handle, FALSE);
  		BASS_ChannelSetAttribute(handle, BASS_ATTRIB_VOL, 0.5f);
  		BASS_ChannelSetAttribute(handle, BASS_ATTRIB_PAN, ((rand() % 201) - 100) / 100.f);
  	}
  	if (!BASS_ChannelPlay(handle, FALSE)) // play it
  		Error("Can't play channel");
  }
  
  void StopClicked(GtkButton *obj, gpointer data)
  {
  	DWORD handle;
  	const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
  	int mode = atoi(objname + 4); // get mode from button name ("playX")
  	handle = GetSelectedHandle(mode); // get selected stream/music/sample handle
  	BASS_ChannelStop(handle); // stop it
  }
  
  void RestartClicked(GtkButton *obj, gpointer data)
  {
  	DWORD handle;
  	const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
  	int mode = atoi(objname + 7); // get mode from button name ("restartX")
  	handle = GetSelectedHandle(mode); // get selected stream/music/sample handle
  	if (!BASS_ChannelPlay(handle, TRUE)) // restart it
  		Error("Can't play channel");
  }
  
  void GVolumeChanged(GtkRange *range, gpointer data)
  {
  	const gchar *objname = gtk_widget_get_name(GTK_WIDGET(range));
  	int mode = atoi(objname + 7); // get mode from name ("gvolumeX")
  	double value = gtk_range_get_value(range);
  	switch (mode) {
  		case 1:
  			BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, value * 100); // global stream volume (0-10000)
  			break;
  		case 2:
  			BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, value * 100); // global MOD volume (0-10000)
  			break;
  		case 3:
  			BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, value * 100); // global sample volume (0-10000)
  			break;
  	}
  }
  
  void VolumeChanged(GtkRange *range, gpointer data)
  {
  	double value = gtk_range_get_value(range);
  	BASS_SetVolume(value / 100.f); // output volume (0-1)
  }
  
  void StopAllClicked(GtkButton *obj, gpointer data)
  {
  	BASS_Pause(); // pause output
  }
  
  void ResumeAllClicked(GtkButton *obj, gpointer data)
  {
  	BASS_Start(); // resume output
  }
  
  void ThreadsToggled(GtkToggleButton *obj, gpointer data)
  {
  	BASS_SetConfig(BASS_CONFIG_UPDATETHREADS, obj->active ? 2 : 1); // set 1 or 2 update threads
  }
  
  gboolean TimerProc(gpointer data)
  {	// update the CPU usage % display
  	char text[16];
  	sprintf(text, "CPU%% %.2f", BASS_GetCPU());
  	gtk_label_set_text(GTK_LABEL(GetWidget("cpu")), text);
  	return TRUE;
  }
  
  int main(int argc, char* argv[])
  {
  	regex_t fregex[2];
  
  	gtk_init(&argc, &argv);
  
  	// check the correct BASS was loaded
  	if (HIWORD(BASS_GetVersion()) != BASSVERSION) {
  		Error("An incorrect version of BASS was loaded");
  		return 0;
  	}
  
  	// initialize default output device
  	if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
  		Error("Can't initialize device");
  		return 0;
  	}
  
  	// initialize GUI
  	glade = glade_xml_new(GLADE_PATH"basstest.glade", NULL, NULL);
  	if (!glade) return 0;
  	win = GetWidget("window1");
  	if (!win) return 0;
  	glade_xml_signal_autoconnect(glade);
  
  	{ // setup lists
  		int a;
  		for (a = 1; a <= 3; a++) {
  			char name[8];
  			sprintf(name, "list%d", a);
  			GtkTreeView *list = GTK_TREE_VIEW(GetWidget(name));
  			GtkTreeViewColumn *col = gtk_tree_view_column_new();
  			gtk_tree_view_append_column(list, col);
  			GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
  			gtk_tree_view_column_pack_start(col, renderer, TRUE);
  			gtk_tree_view_column_add_attribute(col, renderer, "text", 0);
  			GtkListStore *liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
  			gtk_tree_view_set_model(list, GTK_TREE_MODEL(liststore));
  			g_object_unref(liststore);
  		}
  	}
  
  	// initialize volume slider
  	gtk_range_set_value(GTK_RANGE(GetWidget("volume")), BASS_GetVolume() * 100);
  
  	{ // initialize file selector
  		GtkFileFilter *filter;
  		filesel = gtk_file_chooser_dialog_new("Open File", GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN,
  			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
  		filter = gtk_file_filter_new();
  		gtk_file_filter_set_name(filter, "Streamable files (wav/aif/mp3/mp2/mp1/ogg)");
  		regcomp(&fregex[0], "\\.(mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
  		gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex[0], NULL);
  		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filesel), filter);
  		filter = gtk_file_filter_new();
  		gtk_file_filter_set_name(filter, "MOD music files (mo3/xm/mod/s3m/it/mtm/umx)");
  		regcomp(&fregex[1], "\\.(mo3|xm|mod|s3m|it|umx)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
  		gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex[1], NULL);
  		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filesel), filter);
  		filter = gtk_file_filter_new();
  		gtk_file_filter_set_name(filter, "All files");
  		gtk_file_filter_add_pattern(filter, "*");
  		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filesel), filter);
  	}
  
  	g_timeout_add(500, TimerProc, NULL);
  
  	gtk_main();
  
  	gtk_widget_destroy(filesel);
  	regfree(&fregex[0]);
  	regfree(&fregex[1]);
  
  	BASS_Free(); // close output
  
  	return 0;
  }
  