Add linux bass

This commit is contained in:
Michael Zhang 2021-01-07 21:37:50 -06:00
parent c46545401e
commit ca6a819267
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
289 changed files with 10933 additions and 0 deletions

View file

@ -0,0 +1,315 @@
/*
BASS 3D test
Copyright (c) 1999-2012 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <math.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;
// channel (sample/music) info structure
typedef struct {
DWORD channel; // the channel
BASS_3DVECTOR pos, vel; // position,velocity
} Channel;
Channel *chans = NULL; // the channels
int chanc = 0, chan = -1; // number of channels, current channel
#define TIMERPERIOD 50 // timer period (ms)
#define MAXDIST 50 // maximum distance of the channels (m)
#define SPEED 12 // speed of the channels' movement (m/s)
// 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 WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
// Update the button states
void UpdateButtons()
{
gtk_widget_set_sensitive(GetWidget("remove"), chan == -1 ? FALSE : TRUE);
gtk_widget_set_sensitive(GetWidget("play"), chan == -1 ? FALSE : TRUE);
gtk_widget_set_sensitive(GetWidget("stop"), chan == -1 ? FALSE : TRUE);
gtk_widget_set_sensitive(GetWidget("movex"), chan == -1 ? FALSE : TRUE);
gtk_widget_set_sensitive(GetWidget("movez"), chan == -1 ? FALSE : TRUE);
gtk_widget_set_sensitive(GetWidget("movereset"), chan == -1 ? FALSE : TRUE);
if (chan != -1) {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GetWidget("movex")), fabs(chans[chan].vel.x));
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GetWidget("movez")), fabs(chans[chan].vel.z));
}
}
gboolean ListSelectionChange(GtkTreeView *treeview, gpointer data)
{ // the selected channel has (probably) changed
GtkTreeSelection *ts;
GtkTreeModel *tm;
GtkTreeIter it;
ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(GetWidget("channels")));
if (gtk_tree_selection_get_selected(ts, &tm, &it)) {
char *rows = gtk_tree_model_get_string_from_iter(tm, &it);
chan = atoi(rows);
g_free(rows);
} else
chan = -1;
UpdateButtons();
return TRUE;
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
void AddClicked(GtkButton *obj, gpointer data)
{ // add a channel
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));
DWORD newchan;
// Load a music or sample from "file"
if ((newchan = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMP | BASS_SAMPLE_LOOP | BASS_SAMPLE_3D, 1))
|| (newchan = BASS_SampleLoad(FALSE, file, 0, 0, 1, BASS_SAMPLE_LOOP | BASS_SAMPLE_3D | BASS_SAMPLE_MONO))) {
Channel *c;
chanc++;
chans = (Channel*)realloc((void*)chans, chanc * sizeof(Channel));
c = chans + chanc - 1;
memset(c, 0, sizeof(Channel));
c->channel = newchan;
BASS_SampleGetChannel(newchan, FALSE); // initialize sample channel
{ // add it to the list
GtkTreeView *tree = GTK_TREE_VIEW(GetWidget("channels"));
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, strrchr(file, '/') + 1, -1);
}
} else
Error("Can't load file (note samples must be mono)");
g_free(file);
}
}
void RemoveClicked(GtkButton *obj, gpointer data)
{ // remove a channel
GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(GetWidget("channels")));
GtkTreeIter it;
if (gtk_tree_model_iter_nth_child(tm, &it, NULL, chan)) {
Channel *c = chans + chan;
// free both MOD and stream, it must be one of them! :)
BASS_SampleFree(c->channel);
BASS_MusicFree(c->channel);
chanc--;
memmove(c, c + 1, (chanc - chan) * sizeof(Channel));
gtk_list_store_remove(GTK_LIST_STORE(tm), &it);
}
}
void PlayClicked(GtkButton *obj, gpointer data)
{
BASS_ChannelPlay(chans[chan].channel, FALSE);
}
void StopClicked(GtkButton *obj, gpointer data)
{
BASS_ChannelPause(chans[chan].channel);
}
void MoveXChanged(GtkSpinButton *spinbutton, gpointer data)
{ // X velocity
float value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(GetWidget("movex")));
if (fabs(chans[chan].vel.x) != value) chans[chan].vel.x = value;
}
void MoveZChanged(GtkSpinButton *spinbutton, gpointer data)
{ // Y velocity
float value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(GetWidget("movez")));
if (fabs(chans[chan].vel.z) != value) chans[chan].vel.z = value;
}
void MoveResetClicked(GtkButton *obj, gpointer data)
{ // reset the position and velocity to 0
memset(&chans[chan].pos, 0, sizeof(chans[chan].pos));
memset(&chans[chan].vel, 0, sizeof(chans[chan].vel));
UpdateButtons();
}
void RolloffChanged(GtkRange *range, gpointer data)
{
// change the rolloff factor
double value = gtk_range_get_value(range);
BASS_Set3DFactors(-1, pow(2, (value - 10) / 5.0), -1);
}
void DopplerChanged(GtkRange *range, gpointer data)
{
// change the doppler factor
double value = gtk_range_get_value(range);
BASS_Set3DFactors(-1, -1, pow(2, (value - 10) / 5.0));
}
gboolean Update(gpointer data)
{
int c, x, y, cx, cy;
GtkWidget *dc = GetWidget("drawingarea1");
GdkGC *gc = dc->style->fg_gc[GTK_WIDGET_STATE(dc)];
GdkGCValues gcsave;
gdk_gc_get_values(gc, &gcsave);
cx = dc->allocation.width / 2;
cy = dc->allocation.height / 2;
{ // clear the display
GdkColor c = { 0,0xffff,0xffff,0xffff };
gdk_gc_set_rgb_fg_color(gc, &c);
gdk_draw_rectangle(dc->window, gc, TRUE, 0, 0, dc->allocation.width, dc->allocation.height);
}
{ // Draw the listener
GdkColor c = { 0,0x8000,0x8000,0x8000 };
gdk_gc_set_rgb_fg_color(gc, &c);
gdk_draw_arc(dc->window, gc, TRUE, cx - 4, cy - 4, 8, 8, 0, 360 * 64);
}
for (c = 0; c < chanc; c++) {
// If the channel's playing then update it's position
if (BASS_ChannelIsActive(chans[c].channel) == BASS_ACTIVE_PLAYING) {
// Check if channel has reached the max distance
if (chans[c].pos.z >= MAXDIST || chans[c].pos.z <= -MAXDIST)
chans[c].vel.z = -chans[c].vel.z;
if (chans[c].pos.x >= MAXDIST || chans[c].pos.x <= -MAXDIST)
chans[c].vel.x = -chans[c].vel.x;
// Update channel position
chans[c].pos.z += chans[c].vel.z * TIMERPERIOD / 1000;
chans[c].pos.x += chans[c].vel.x * TIMERPERIOD / 1000;
BASS_ChannelSet3DPosition(chans[c].channel, &chans[c].pos, NULL, &chans[c].vel);
}
{ // Draw the channel position indicator
static GdkColor cols[2] = { {0,0xffff,0xc000,0xc000},{0,0xffff,0,0} };
gdk_gc_set_rgb_fg_color(gc, &cols[chan == c]);
x = cx + (int)((cx - 10) * chans[c].pos.x / MAXDIST);
y = cy - (int)((cy - 10) * chans[c].pos.z / MAXDIST);
gdk_draw_arc(dc->window, gc, TRUE, x - 4, y - 4, 8, 8, 0, 360 * 64);
}
}
// Apply the 3D changes
BASS_Apply3D();
gdk_gc_set_values(gc, &gcsave, GDK_GC_FOREGROUND);
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 the output device with 3D support
if (!BASS_Init(-1, 44100, BASS_DEVICE_3D, NULL, NULL)) {
Error("Can't initialize device");
return 0;
}
{
BASS_INFO i;
BASS_GetInfo(&i);
if (i.speakers > 2) {
GtkWidget *dialog = gtk_message_dialog_new(NULL, 0,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Multiple speakers were detected. Would you like to use them?");
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_NO)
BASS_SetConfig(BASS_CONFIG_3DALGORITHM, BASS_3DALG_OFF);
gtk_widget_destroy(dialog);
}
}
// Use meters as distance unit, real world rolloff, real doppler effect
BASS_Set3DFactors(1, 1, 1);
// initialize GUI
glade = glade_xml_new(GLADE_PATH"3dtest.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(GetWidget("channels"))), "changed", G_CALLBACK(ListSelectionChange), NULL);
{ // setup list
GtkTreeView *list = GTK_TREE_VIEW(GetWidget("channels"));
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(1, G_TYPE_STRING);
gtk_tree_view_set_model(list, GTK_TREE_MODEL(liststore));
g_object_unref(liststore);
}
{ // 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(TIMERPERIOD, Update, NULL);
UpdateButtons();
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex[0]);
regfree(&fregex[1]);
BASS_Free(); // close output
return 0;
}

View file

@ -0,0 +1,334 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS - 3D test</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkTreeView" id="channels">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="add">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Add...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="AddClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Remove</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RemoveClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="play">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Play</property>
<property name="response_id">0</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="stop">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Stop</property>
<property name="response_id">0</property>
<signal name="clicked" handler="StopClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Channels</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkHBox" id="hbox4">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">x:</property>
</widget>
</child>
<child>
<widget class="GtkSpinButton" id="movex">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 100 1 10 0</property>
<property name="numeric">True</property>
<signal name="value_changed" handler="MoveXChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="label" translatable="yes">z:</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="movez">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 100 1 10 0</property>
<property name="numeric">True</property>
<signal name="value_changed" handler="MoveZChanged"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="movereset">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Reset</property>
<property name="response_id">0</property>
<signal name="clicked" handler="MoveResetClicked"/>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">Movement</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame5">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment5">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkDrawingArea" id="drawingarea1">
<property name="width_request">200</property>
<property name="height_request">200</property>
<property name="visible">True</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkHScale" id="rolloff">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">10 0 20 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="RolloffChanged"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">Rolloff factor</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkHScale" id="doppler">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">10 0 20 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="DopplerChanged"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="label" translatable="yes">Doppler factor</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = 3dtest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,294 @@
/*
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;
}

View file

@ -0,0 +1,580 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS - simple playback test</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkTreeView" id="list1">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="play1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Play</property>
<property name="response_id">0</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="stop1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Stop</property>
<property name="response_id">0</property>
<signal name="clicked" handler="StopClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="restart1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Restart</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RestartClicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="add1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Add..</property>
<property name="response_id">0</property>
<signal name="clicked" handler="AddClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Remove</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RemoveClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">global volume:</property>
</widget>
</child>
<child>
<widget class="GtkHScale" id="gvolume1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">100 0 100 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="GVolumeChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Stream</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkTreeView" id="list2">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox4">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="play2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Play</property>
<property name="response_id">0</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="stop2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Stop</property>
<property name="response_id">0</property>
<signal name="clicked" handler="StopClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="restart2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Restart</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RestartClicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="add2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Add..</property>
<property name="response_id">0</property>
<signal name="clicked" handler="AddClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Remove</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RemoveClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">global volume:</property>
</widget>
</child>
<child>
<widget class="GtkHScale" id="gvolume2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">100 0 100 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="GVolumeChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">MOD Music</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="bottom_padding">5</property>
<property name="left_padding">5</property>
<property name="right_padding">5</property>
<child>
<widget class="GtkVBox" id="vbox6">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkTreeView" id="list3">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox6">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="play3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Play</property>
<property name="response_id">0</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="add3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Add..</property>
<property name="response_id">0</property>
<signal name="clicked" handler="AddClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Remove</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RemoveClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox7">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">global volume:</property>
</widget>
</child>
<child>
<widget class="GtkHScale" id="gvolume3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">100 0 100 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="GVolumeChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="label" translatable="yes">Sample</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<property name="spacing">10</property>
<child>
<widget class="GtkHBox" id="hbox9">
<property name="visible">True</property>
<property name="spacing">10</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="stop">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Stop Output</property>
<property name="response_id">0</property>
<signal name="clicked" handler="StopAllClicked"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="resume">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Resume</property>
<property name="response_id">0</property>
<signal name="clicked" handler="ResumeAllClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox9">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">volume:</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHScale" id="volume">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 100 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="VolumeChanged"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox10">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkCheckButton" id="threads">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">2 update threads</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="ThreadsToggled"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="cpu">
<property name="visible">True</property>
<property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = basstest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,8 @@
TARGET = contest
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,271 @@
/*
BASS custom looping example
Copyright (c) 2004-2014 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glib/gthread.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
#define WIDTH 600 // display width
#define HEIGHT 201 // height (odd number for centre line)
#pragma pack(1)
typedef struct {
BYTE rgbRed, rgbGreen, rgbBlue;
} RGB;
#pragma pack()
GtkWidget *win = 0;
GThread *scanthread = 0;
BOOL killscan = FALSE;
DWORD chan;
DWORD bpp; // bytes per pixel
QWORD loop[2] = { 0 }; // loop start & end
HSYNC lsync; // looping sync
GtkWidget *waveda;
GdkPixbuf *wavepb;
RGB palette[HEIGHT / 2 + 1];
// 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);
}
void WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void CALLBACK LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
if (!BASS_ChannelSetPosition(channel, loop[0], BASS_POS_BYTE)) // try seeking to loop start
BASS_ChannelSetPosition(channel, 0, BASS_POS_BYTE); // failed, go to start of file instead
}
void SetLoopStart(QWORD pos)
{
loop[0] = pos;
}
void SetLoopEnd(QWORD pos)
{
loop[1] = pos;
BASS_ChannelRemoveSync(chan, lsync); // remove old sync
lsync = BASS_ChannelSetSync(chan, BASS_SYNC_POS | BASS_SYNC_MIXTIME, loop[1], LoopSyncProc, 0); // set new sync
}
// scan the peaks
void *ScanPeaks(void *p)
{
DWORD decoder = (uintptr_t)p;
RGB *wavebuf = (RGB*)gdk_pixbuf_get_pixels(wavepb);
DWORD pos = 0;
float spp = BASS_ChannelBytes2Seconds(decoder, bpp); // seconds per pixel
while (!killscan) {
float peak[2];
if (spp > 1) { // more than 1 second per pixel, break it down...
float todo = spp;
peak[1] = peak[0] = 0;
do {
float level[2], step = (todo < 1 ? todo : 1);
BASS_ChannelGetLevelEx(decoder, level, step, BASS_LEVEL_STEREO); // scan peaks
if (peak[0] < level[0]) peak[0] = level[0];
if (peak[1] < level[1]) peak[1] = level[1];
todo -= step;
} while (todo > 0);
} else
BASS_ChannelGetLevelEx(decoder, peak, spp, BASS_LEVEL_STEREO); // scan peaks
{
DWORD a;
for (a = 0; a < peak[0] * (HEIGHT / 2); a++)
wavebuf[(HEIGHT / 2 - 1 - a) * WIDTH + pos] = palette[1 + a]; // draw left peak
for (a = 0; a < peak[1] * (HEIGHT / 2); a++)
wavebuf[(HEIGHT / 2 + 1 + a) * WIDTH + pos] = palette[1 + a]; // draw right peak
}
pos++;
if (pos >= WIDTH) break; // reached end of display
if (!BASS_ChannelIsActive(decoder)) break; // reached end of channel
}
if (!killscan) {
DWORD size;
BASS_ChannelSetPosition(decoder, (QWORD)-1, BASS_POS_BYTE | BASS_POS_SCAN); // build seek table (scan to end)
size = BASS_ChannelGetAttributeEx(decoder, BASS_ATTRIB_SCANINFO, 0, 0); // get seek table size
if (size) { // got it
void *info = malloc(size); // allocate a buffer
BASS_ChannelGetAttributeEx(decoder, BASS_ATTRIB_SCANINFO, info, size); // get the seek table
BASS_ChannelSetAttributeEx(chan, BASS_ATTRIB_SCANINFO, info, size); // apply it to the playback channel
free(info);
}
}
BASS_StreamFree(decoder); // free the decoder
return NULL;
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
// select a file to play, and start scanning it
BOOL PlayFile()
{
BOOL ret = FALSE;
regex_t fregex;
GtkWidget *filesel; // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
gtk_widget_hide(filesel);
if (!(chan = BASS_StreamCreateFile(FALSE, file, 0, 0, 0))
&& !(chan = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMP | BASS_MUSIC_POSRESET | BASS_MUSIC_PRESCAN, 1))) {
Error("Can't play file");
} else {
// create the bitmap
wavepb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, WIDTH, HEIGHT);
{ // setup palette
RGB *pal = (RGB*)palette;
int a;
memset(palette, 0, sizeof(palette));
for (a = 1; a <= HEIGHT / 2; a++) {
pal[a].rgbRed = (255 * a) / (HEIGHT / 2);
pal[a].rgbGreen = 255 - pal[a].rgbRed;
}
}
bpp = BASS_ChannelGetLength(chan, BASS_POS_BYTE) / WIDTH; // bytes per pixel
{
DWORD bpp1 = BASS_ChannelSeconds2Bytes(chan, 0.001); // minimum 1ms per pixel
if (bpp < bpp1) bpp = bpp1;
}
BASS_ChannelSetSync(chan, BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, LoopSyncProc, 0); // set sync to loop at end
BASS_ChannelPlay(chan, FALSE); // start playing
{ // create another channel to scan
DWORD chan2 = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_STREAM_DECODE);
if (!chan2) chan2 = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_DECODE, 1);
#if GLIB_CHECK_VERSION(2,32,0)
scanthread = g_thread_new(NULL, ScanPeaks, (void*)(uintptr_t)chan2); // start scanning in a new thread
#else
scanthread = g_thread_create(ScanPeaks, (void*)(uintptr_t)chan2, TRUE, NULL); // start scanning in a new thread
#endif
}
ret = TRUE;
}
g_free(file);
}
gtk_widget_destroy(filesel);
return ret;
}
void WindowButtonRelease(GtkWidget *obj, GdkEventButton *event, gpointer data)
{
if (event->type == GDK_BUTTON_RELEASE) {
if (event->button == 1) // set loop start
SetLoopStart(event->x * bpp);
else if (event->button == 3) // set loop end
SetLoopEnd(event->x * bpp);
}
}
void DrawTimeLine(QWORD pos, DWORD col, DWORD y)
{
GdkGC *gc = waveda->style->fg_gc[GTK_WIDGET_STATE(waveda)];
DWORD wpos = pos / bpp;
GdkColor c = { 0,(col & 0xff) << 8,col & 0xff00,(col >> 8) & 0xff00 };
gdk_gc_set_rgb_fg_color(gc, &c);
gdk_draw_line(waveda->window, gc, wpos, 0, wpos, HEIGHT);
}
gboolean AreaExpose(GtkWidget *widget, GdkEventExpose *event, gpointer user)
{
GdkGC *gc = widget->style->fg_gc[GTK_WIDGET_STATE(waveda)];
GdkGCValues gcsave;
gdk_gc_get_values(gc, &gcsave);
gdk_draw_pixbuf(widget->window, gc, wavepb, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
DrawTimeLine(loop[0], 0xffff00, 12); // loop start
DrawTimeLine(loop[1], 0x00ffff, 24); // loop end
DrawTimeLine(BASS_ChannelGetPosition(chan, BASS_POS_BYTE), 0xffffff, 0); // current pos
gdk_gc_set_values(gc, &gcsave, GDK_GC_FOREGROUND);
return FALSE;
}
gboolean TimerProc(gpointer data)
{
// refresh window
gtk_widget_queue_draw(waveda);
return TRUE;
}
int main(int argc, char* argv[])
{
#if !GLIB_CHECK_VERSION(2,32,0)
g_thread_init(NULL);
#endif
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 BASS
if (!BASS_Init(-1, 44100, 0, win, NULL)) {
Error("Can't initialize device");
return 0;
}
if (!PlayFile()) { // start a file playing
BASS_Free();
return 0;
}
// create the window
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
gtk_window_set_title(GTK_WINDOW(win), "BASS custom looping example (left-click to set loop start, right-click to set end)");
g_signal_connect(win, "destroy", GTK_SIGNAL_FUNC(WindowDestroy), NULL);
GtkWidget *ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(win), ebox);
g_signal_connect(ebox, "button-release-event", GTK_SIGNAL_FUNC(WindowButtonRelease), NULL);
waveda = gtk_drawing_area_new();
gtk_widget_set_size_request(waveda, WIDTH, HEIGHT);
gtk_container_add(GTK_CONTAINER(ebox), waveda);
g_signal_connect(waveda, "expose-event", GTK_SIGNAL_FUNC(AreaExpose), NULL);
// setup update timer (10hz)
g_timeout_add(100, TimerProc, NULL);
gtk_widget_show_all(win);
gtk_main();
killscan = TRUE;
g_thread_join(scanthread); // wait for the scan thread
g_object_unref(wavepb);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,9 @@
TARGET = custloop
PACKAGES = gtk+-2.0 gthread-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,8 @@
TARGET = devlist
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,250 @@
/*
BASS simple DSP test
Copyright (c) 2000-2017 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
GtkWidget *filesel;
DWORD chan; // the channel... HMUSIC or HSTREAM
// 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);
}
// "rotate"
HDSP rotdsp = 0; // DSP handle
float rotpos; // cur.pos
void CALLBACK Rotate(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
float *d = (float*)buffer;
DWORD a;
for (a = 0; a < length / 4; a += 2) {
d[a] *= fabs(sin(rotpos));
d[a + 1] *= fabs(cos(rotpos));
rotpos += 0.00003;
}
rotpos = fmod(rotpos, 2 * M_PI);
}
// "echo"
HDSP echdsp = 0; // DSP handle
#define ECHBUFLEN 1200 // buffer length
float echbuf[ECHBUFLEN][2]; // buffer
int echpos; // cur.pos
void CALLBACK Echo(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
float *d = (float*)buffer;
DWORD a;
for (a = 0; a < length / 4; a += 2) {
float l = d[a] + (echbuf[echpos][1] / 2);
float r = d[a + 1] + (echbuf[echpos][0] / 2);
#if 1 // 0=echo, 1=basic "bathroom" reverb
echbuf[echpos][0] = d[a] = l;
echbuf[echpos][1] = d[a + 1] = r;
#else
echbuf[echpos][0] = d[a];
echbuf[echpos][1] = d[a + 1];
d[a] = l;
d[a + 1] = r;
#endif
echpos++;
if (echpos == ECHBUFLEN) echpos = 0;
}
}
// "flanger"
HDSP fladsp = 0; // DSP handle
#define FLABUFLEN 350 // buffer length
float flabuf[FLABUFLEN][2]; // buffer
int flapos; // cur.pos
float flas, flasinc; // sweep pos/increment
void CALLBACK Flange(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
float *d = (float*)buffer;
DWORD a;
for (a = 0; a < length / 4; a += 2) {
int p1 = (flapos + (int)flas) % FLABUFLEN;
int p2 = (p1 + 1) % FLABUFLEN;
float f = flas - (int)flas;
float s;
s = (d[a] + ((flabuf[p1][0] * (1 - f)) + (flabuf[p2][0] * f))) * 0.7;
flabuf[flapos][0] = d[a];
d[a] = s;
s = (d[a + 1] + ((flabuf[p1][1] * (1 - f)) + (flabuf[p2][1] * f))) * 0.7;
flabuf[flapos][1] = d[a + 1];
d[a + 1] = s;
flapos++;
if (flapos == FLABUFLEN) flapos = 0;
flas += flasinc;
if (flas<0 || flas>FLABUFLEN - 1) {
flasinc = -flasinc;
flas += flasinc;
}
}
}
#define GetWidget(id) glade_xml_get_widget(glade,id)
void WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void RotateToggled(GtkToggleButton *obj, gpointer data)
{ // toggle "rotate"
if (obj->active) {
rotpos = M_PI / 4;
rotdsp = BASS_ChannelSetDSP(chan, &Rotate, 0, 2);
} else
BASS_ChannelRemoveDSP(chan, rotdsp);
}
void EchoToggled(GtkToggleButton *obj, gpointer data)
{ // toggle "echo"
if (obj->active) {
memset(echbuf, 0, sizeof(echbuf));
echpos = 0;
echdsp = BASS_ChannelSetDSP(chan, &Echo, 0, 1);
} else
BASS_ChannelRemoveDSP(chan, echdsp);
}
void FlangerToggled(GtkToggleButton *obj, gpointer data)
{ // toggle "flanger"
if (obj->active) {
memset(flabuf, 0, sizeof(flabuf));
flapos = 0;
flas = FLABUFLEN / 2;
flasinc = 0.002f;
fladsp = BASS_ChannelSetDSP(chan, &Flange, 0, 0);
} else
BASS_ChannelRemoveDSP(chan, fladsp);
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
void OpenClicked(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));
// free both MOD and stream, it must be one of them! :)
BASS_MusicFree(chan);
BASS_StreamFree(chan);
if (!(chan = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_SAMPLE_LOOP | BASS_SAMPLE_FLOAT))
&& !(chan = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMPS | BASS_SAMPLE_LOOP | BASS_SAMPLE_FLOAT, 1))) {
// whatever it is, it ain't playable
gtk_button_set_label(obj, "click here to open a file...");
Error("Can't play the file");
} else {
BASS_CHANNELINFO info;
BASS_ChannelGetInfo(chan, &info);
if (info.chans != 2) { // the DSP expects stereo
gtk_button_set_label(obj, "click here to open a file...");
BASS_MusicFree(chan);
BASS_StreamFree(chan);
Error("only stereo sources are supported");
} else {
gtk_button_set_label(obj, file);
// setup DSPs on new channel and play it
RotateToggled(GTK_TOGGLE_BUTTON(GetWidget("rotate")), 0);
EchoToggled(GTK_TOGGLE_BUTTON(GetWidget("echo")), 0);
FlangerToggled(GTK_TOGGLE_BUTTON(GetWidget("flanger")), 0);
BASS_ChannelPlay(chan, FALSE);
}
}
g_free(file);
}
}
int main(int argc, char* argv[])
{
regex_t fregex;
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;
}
// check for floating-point capability
if (!BASS_GetConfig(BASS_CONFIG_FLOAT)) {
Error("This example requires floating-point support");
return 0;
}
// enable floating-point DSP (not really necessary as the channels will be floating-point anyway)
BASS_SetConfig(BASS_CONFIG_FLOATDSP, TRUE);
// initialize default device
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
Error("Can't initialize device");
return 0;
}
// initialize GUI
glade = glade_xml_new(GLADE_PATH"dsptest.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
{ // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
}
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS simple DSP test</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">10</property>
<child>
<widget class="GtkButton" id="open">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="rotate">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">rotate</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="RotateToggled"/>
</widget>
<packing>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="echo">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">echo</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="EchoToggled" after="yes"/>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="flanger">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">flanger</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FlangerToggled"/>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = dsptest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,191 @@
/*
BASS DX8 effects test
Copyright (c) 2001-2017 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
GtkWidget *filesel;
DWORD floatable = BASS_SAMPLE_FLOAT; // floating-point channel support?
DWORD chan; // channel handle
DWORD fxchan = 0; // output stream handle
HFX fx[4]; // 3 eq bands + reverb
// 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 WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void UpdateFX(const char *id)
{
GtkRange *obj = GTK_RANGE(GetWidget(id));
double v = gtk_range_get_value(obj);
int b = atoi(id + 2) - 1;
if (b < 3) {
BASS_DX8_PARAMEQ p;
BASS_FXGetParameters(fx[b], &p);
p.fGain = v;
BASS_FXSetParameters(fx[b], &p);
} else {
BASS_DX8_REVERB p;
BASS_FXGetParameters(fx[3], &p);
p.fReverbMix = (v ? log(v / 20.0) * 20 : -96);
BASS_FXSetParameters(fx[3], &p);
}
}
void SetupFX()
{
// setup the effects
BASS_DX8_PARAMEQ p;
DWORD ch = fxchan ? fxchan : chan; // set on output stream if enabled, else file stream
fx[0] = BASS_ChannelSetFX(ch, BASS_FX_DX8_PARAMEQ, 0);
fx[1] = BASS_ChannelSetFX(ch, BASS_FX_DX8_PARAMEQ, 0);
fx[2] = BASS_ChannelSetFX(ch, BASS_FX_DX8_PARAMEQ, 0);
fx[3] = BASS_ChannelSetFX(ch, BASS_FX_DX8_REVERB, 0);
p.fGain = 0;
p.fBandwidth = 18;
p.fCenter = 125;
BASS_FXSetParameters(fx[0], &p);
p.fCenter = 1000;
BASS_FXSetParameters(fx[1], &p);
p.fCenter = 8000;
BASS_FXSetParameters(fx[2], &p);
UpdateFX("fx1");
UpdateFX("fx2");
UpdateFX("fx3");
UpdateFX("fx4");
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
void OpenClicked(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));
// free both MOD and stream, it must be one of them! :)
BASS_MusicFree(chan);
BASS_StreamFree(chan);
if (!(chan = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_SAMPLE_LOOP | floatable))
&& !(chan = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMPS | BASS_SAMPLE_LOOP | floatable, 1))) {
// whatever it is, it ain't playable
gtk_button_set_label(obj, "click here to open a file...");
Error("Can't play the file");
} else {
gtk_button_set_label(obj, file);
if (!fxchan) SetupFX(); // set effects on file if not using output stream
BASS_ChannelPlay(chan, FALSE);
}
g_free(file);
}
}
gboolean FXChange(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data)
{
UpdateFX(gtk_widget_get_name(GTK_WIDGET(range)));
return FALSE;
}
void OutputToggled(GtkToggleButton *obj, gpointer data)
{
// remove current effects
DWORD ch = fxchan ? fxchan : chan;
BASS_ChannelRemoveFX(ch, fx[0]);
BASS_ChannelRemoveFX(ch, fx[1]);
BASS_ChannelRemoveFX(ch, fx[2]);
BASS_ChannelRemoveFX(ch, fx[3]);
if (obj->active)
fxchan = BASS_StreamCreate(0, 0, 0, STREAMPROC_DEVICE, 0); // get device output stream
else
fxchan = 0; // stop using device output stream
SetupFX();
}
int main(int argc, char* argv[])
{
regex_t fregex;
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;
}
// check for floating-point capability
if (!BASS_GetConfig(BASS_CONFIG_FLOAT)) {
// no floating-point channel support
floatable = 0;
// enable floating-point (8.24 fixed-point in this case) DSP instead
BASS_SetConfig(BASS_CONFIG_FLOATDSP, TRUE);
}
// initialize default device
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
Error("Can't initialize device");
return 0;
}
// initialize GUI
glade = glade_xml_new(GLADE_PATH"fxtest.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
{ // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
}
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,194 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="window1">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS "DX8" effects test</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<widget class="GtkButton" id="open">
<property name="label" translatable="yes">click here to open a file...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkVScale" id="fx1">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="orientation">vertical</property>
<property name="adjustment">0 -10 10 1 10 0</property>
<property name="inverted">True</property>
<property name="restrict_to_fill_level">False</property>
<property name="fill_level">0</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="FXChange"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">125 hz</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkVScale" id="fx2">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="orientation">vertical</property>
<property name="adjustment">0 -10 10 1 10 0</property>
<property name="inverted">True</property>
<property name="restrict_to_fill_level">False</property>
<property name="fill_level">0</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="FXChange"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">1 khz</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkVScale" id="fx3">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="orientation">vertical</property>
<property name="adjustment">0 -10 10 1 10 0</property>
<property name="inverted">True</property>
<property name="restrict_to_fill_level">False</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="FXChange"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">8 khz</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkVScale" id="fx4">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="orientation">vertical</property>
<property name="adjustment">0 0 20 1 10 0</property>
<property name="inverted">True</property>
<property name="restrict_to_fill_level">False</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="FXChange"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">reverb</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="output">
<property name="label" translatable="yes">Apply effects to final output instead of file</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="OutputToggled"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = fxtest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

Binary file not shown.

View file

@ -0,0 +1,229 @@
/*
BASS spectrum analyser example
Copyright (c) 2002-2010 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
#define SPECWIDTH 368 // display width (should be multiple of 4)
#define SPECHEIGHT 127 // height (changing requires palette adjustments too)
#pragma pack(1)
typedef struct {
BYTE rgbRed, rgbGreen, rgbBlue;
} RGB;
#pragma pack()
GtkWidget *win = 0;
HRECORD chan;
GtkWidget *speci, *textarea;
GdkPixbuf *specpb;
RGB palette[256];
int specmode = 0, specpos = 0; // spectrum mode (and marker pos for 2nd mode)
// 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);
}
void WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void WindowButtonRelease(GtkWidget *obj, GdkEventButton *event, gpointer data)
{
if (event->type == GDK_BUTTON_RELEASE) {
RGB *specbuf = (RGB*)gdk_pixbuf_get_pixels(specpb);
specmode = (specmode + 1) % 4; // next spectrum mode
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf)); // clear display
}
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
// update the spectrum display - the interesting bit :)
gboolean UpdateSpectrum(gpointer data)
{
static DWORD quietcount = 0;
int x, y, y1;
RGB *specbuf = (RGB*)gdk_pixbuf_get_pixels(specpb);
if (specmode == 3) { // waveform
int c;
float *buf;
BASS_CHANNELINFO ci;
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
BASS_ChannelGetInfo(chan, &ci); // get number of channels
buf = alloca(ci.chans * SPECWIDTH * sizeof(float)); // allocate buffer for data
BASS_ChannelGetData(chan, buf, (ci.chans * SPECWIDTH * sizeof(float)) | BASS_DATA_FLOAT); // get the sample data (floating-point to avoid 8 & 16 bit processing)
for (c = 0; c < ci.chans; c++) {
for (x = 0; x < SPECWIDTH; x++) {
int v = (1 - buf[x * ci.chans + c]) * SPECHEIGHT / 2; // invert and scale to fit display
if (v < 0) v = 0;
else if (v >= SPECHEIGHT) v = SPECHEIGHT - 1;
if (!x) y = v;
do { // draw line from previous sample...
if (y < v) y++;
else if (y > v) y--;
specbuf[y * SPECWIDTH + x] = palette[c & 1 ? 127 : 1]; // left=green, right=red (could add more colours to palette for more chans)
} while (y != v);
}
}
} else {
float fft[1024];
BASS_ChannelGetData(chan, fft, BASS_DATA_FFT2048); // get the FFT data
if (!specmode) { // "normal" FFT
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
for (x = 0; x < SPECWIDTH / 2; x++) {
#if 1
y = sqrt(fft[x + 1]) * 3 * SPECHEIGHT - 4; // scale it (sqrt to make low values more visible)
#else
y = fft[x + 1] * 10 * SPECHEIGHT; // scale it (linearly)
#endif
if (y > SPECHEIGHT) y = SPECHEIGHT; // cap it
if (x && (y1 = (y + y1) / 2)) // interpolate from previous to make the display smoother
while (--y1 >= 0) specbuf[(SPECHEIGHT - 1 - y1) * SPECWIDTH + x * 2 - 1] = palette[y1 + 1];
y1 = y;
while (--y >= 0) specbuf[(SPECHEIGHT - 1 - y) * SPECWIDTH + x * 2] = palette[y + 1]; // draw level
}
} else if (specmode == 1) { // logarithmic, acumulate & average bins
int b0 = 0;
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
#define BANDS 28
for (x = 0; x < BANDS; x++) {
float peak = 0;
int b1 = pow(2, x * 10.0 / (BANDS - 1));
if (b1 > 1023) b1 = 1023;
if (b1 <= b0) b1 = b0 + 1; // make sure it uses at least 1 FFT bin
for (; b0 < b1; b0++)
if (peak < fft[1 + b0]) peak = fft[1 + b0];
y = sqrt(peak) * 3 * SPECHEIGHT - 4; // scale it (sqrt to make low values more visible)
if (y > SPECHEIGHT) y = SPECHEIGHT; // cap it
while (--y >= 0)
for (y1 = 0; y1 < SPECWIDTH / BANDS - 2; y1++)
specbuf[(SPECHEIGHT - 1 - y) * SPECWIDTH + x * (SPECWIDTH / BANDS) + y1] = palette[y + 1]; // draw bar
}
} else { // "3D"
for (x = 0; x < SPECHEIGHT; x++) {
y = sqrt(fft[x + 1]) * 3 * 127; // scale it (sqrt to make low values more visible)
if (y > 127) y = 127; // cap it
specbuf[(SPECHEIGHT - 1 - x) * SPECWIDTH + specpos] = palette[128 + y]; // plot it
}
// move marker onto next position
specpos = (specpos + 1) % SPECWIDTH;
for (x = 0; x < SPECHEIGHT; x++) specbuf[x * SPECWIDTH + specpos] = palette[255];
}
}
if (LOWORD(BASS_ChannelGetLevel(chan)) < 500) { // check if it's quiet
quietcount++;
if (quietcount > 40) // it's been quiet for over a second
gtk_label_set(GTK_LABEL(textarea), quietcount & 16 ? "make some noise!" : "");
} else { // not quiet
quietcount = 0;
gtk_label_set(GTK_LABEL(textarea), "click to toggle mode");
}
gtk_image_set_from_pixbuf(GTK_IMAGE(speci), specpb); // update the display
return TRUE;
}
// Recording callback - not doing anything with the data
BOOL CALLBACK DuffRecording(HRECORD handle, const void *buffer, DWORD length, void *user)
{
return TRUE; // continue recording
}
int main(int argc, char* argv[])
{
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 BASS recording (default device)
if (!BASS_RecordInit(-1)) {
Error("Can't initialize device");
return -1;
}
// start recording (44100hz mono 16-bit)
if (!(chan = BASS_RecordStart(44100, 1, 0, &DuffRecording, 0))) {
Error("Can't start recording");
return -1;
}
// create the window
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
gtk_window_set_title(GTK_WINDOW(win), "BASS \"live\" spectrum");
g_signal_connect(GTK_WINDOW(win), "destroy", GTK_SIGNAL_FUNC(WindowDestroy), NULL);
GtkWidget *ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(win), ebox);
g_signal_connect(ebox, "button-release-event", GTK_SIGNAL_FUNC(WindowButtonRelease), NULL);
GtkWidget *box = gtk_vbox_new(FALSE, 2);
gtk_container_add(GTK_CONTAINER(ebox), box);
// create the bitmap
specpb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, SPECWIDTH, SPECHEIGHT);
speci = gtk_image_new_from_pixbuf(specpb);
gtk_container_add(GTK_CONTAINER(box), speci);
textarea = gtk_label_new("click to toggle mode");
gtk_container_add(GTK_CONTAINER(box), textarea);
{ // setup palette
RGB *pal = palette;
int a;
memset(palette, 0, sizeof(palette));
for (a = 1; a < 128; a++) {
pal[a].rgbGreen = 256 - 2 * a;
pal[a].rgbRed = 2 * a;
}
for (a = 0; a < 32; a++) {
pal[128 + a].rgbBlue = 8 * a;
pal[128 + 32 + a].rgbBlue = 255;
pal[128 + 32 + a].rgbRed = 8 * a;
pal[128 + 64 + a].rgbRed = 255;
pal[128 + 64 + a].rgbBlue = 8 * (31 - a);
pal[128 + 64 + a].rgbGreen = 8 * a;
pal[128 + 96 + a].rgbRed = 255;
pal[128 + 96 + a].rgbGreen = 255;
pal[128 + 96 + a].rgbBlue = 8 * a;
}
}
// setup update timer (40hz)
g_timeout_add(25, UpdateSpectrum, NULL);
gtk_widget_show_all(win);
gtk_main();
g_object_unref(specpb);
BASS_RecordFree();
return 0;
}

View file

@ -0,0 +1,9 @@
TARGET = livespec
PACKAGES = gtk+-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,11 @@
EXAMPLES = 3dtest basstest contest custloop devlist dsptest fxtest livespec modtest multi netradio plugins rectest speakers spectrum synth writewav
.PHONY: all clean $(EXAMPLES)
all: $(EXAMPLES)
clean:
@$(foreach x,$(EXAMPLES),$(MAKE) -C $(x) clean;)
$(EXAMPLES):
$(MAKE) -C $@

View file

@ -0,0 +1,12 @@
FLAGS = -Os -I.. -L.. -L../x64 -lbass -lm -Wl,--no-warn-search-mismatch,-rpath,'$$ORIGIN/..:$$ORIGIN/../x64'
FLAGS += $(foreach pkg,$(PACKAGES),`pkg-config $(pkg) --cflags --libs`)
ifneq (,$(findstring libglade,$(PACKAGES)))
FLAGS += -export-dynamic
# path to glade file
FLAGS += -D'GLADE_PATH="$(shell pwd)/"'
endif
.PHONY: all clean
%: %.c
$(CC) $*.c $(FLAGS) -o $@

View file

@ -0,0 +1,9 @@
TARGET = modtest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,208 @@
/*
BASS MOD music test
Copyright (c) 1999-2014 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.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;
DWORD music; // the HMUSIC channel
// 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 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);
}
DWORD GetFlags()
{
DWORD flags = BASS_MUSIC_POSRESET; // stop notes when seeking
switch (gtk_combo_box_get_active(GTK_COMBO_BOX(GetWidget("interpolation")))) {
case 0:
flags |= BASS_MUSIC_NONINTER; // no interpolation
break;
case 2:
flags |= BASS_MUSIC_SINCINTER; // sinc interpolation
break;
}
switch (gtk_combo_box_get_active(GTK_COMBO_BOX(GetWidget("ramping")))) {
case 1:
flags |= BASS_MUSIC_RAMP; // ramping
break;
case 2:
flags |= BASS_MUSIC_RAMPS; // "sensitive" ramping
break;
}
switch (gtk_combo_box_get_active(GTK_COMBO_BOX(GetWidget("surround")))) {
case 1:
flags |= BASS_MUSIC_SURROUND; // surround
break;
case 2:
flags |= BASS_MUSIC_SURROUND2; // "mode2"
break;
}
return flags;
}
void OpenClicked(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));
BASS_MusicFree(music); // free the current music
music = BASS_MusicLoad(FALSE, file, 0, 0, GetFlags(), 1); // load the new music
if (music) { // success
DWORD length = BASS_ChannelGetLength(music, BASS_POS_MUSIC_ORDER); // get the order length
gtk_button_set_label(obj, file);
{
char text[100], *ctype = "";
BASS_CHANNELINFO info;
int channels = 0;
while (BASS_ChannelGetAttributeEx(music, BASS_ATTRIB_MUSIC_VOL_CHAN + channels, 0, 0)) channels++; // count channels
BASS_ChannelGetInfo(music, &info);
switch (info.ctype & ~BASS_CTYPE_MUSIC_MO3) {
case BASS_CTYPE_MUSIC_MOD:
ctype = "MOD";
break;
case BASS_CTYPE_MUSIC_MTM:
ctype = "MTM";
break;
case BASS_CTYPE_MUSIC_S3M:
ctype = "S3M";
break;
case BASS_CTYPE_MUSIC_XM:
ctype = "XM";
break;
case BASS_CTYPE_MUSIC_IT:
ctype = "IT";
break;
}
snprintf(text, sizeof(text), "name: %s, format: %dch %s%s", BASS_ChannelGetTags(music, BASS_TAG_MUSIC_NAME), channels, ctype, info.ctype & BASS_CTYPE_MUSIC_MO3 ? " (MO3)" : "");
gtk_label_set_text(GTK_LABEL(GetWidget("info")), text);
}
gtk_range_set_range(GTK_RANGE(GetWidget("position")), 0, length - 1); // update scroller range
BASS_ChannelPlay(music, FALSE); // start it
} else { // failed
gtk_button_set_label(obj, "click here to open a file...");
gtk_label_set_text(GTK_LABEL(GetWidget("info")), "");
gtk_label_set_text(GTK_LABEL(GetWidget("posdisplay")), "");
Error("Can't play the file");
}
g_free(file);
}
}
void PlayClicked(GtkButton *obj, gpointer data)
{
if (BASS_ChannelIsActive(music) == BASS_ACTIVE_PLAYING)
BASS_ChannelPause(music);
else
BASS_ChannelPlay(music, FALSE);
}
gboolean PositionChange(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data)
{
BASS_ChannelSetPosition(music, value, BASS_POS_MUSIC_ORDER); // set the position
return FALSE;
}
void FlagChanged(GtkComboBox *obj, gpointer data)
{
BASS_ChannelFlags(music, GetFlags(), -1); // update flags
}
gboolean TimerProc(gpointer data)
{
char text[16];
QWORD pos = BASS_ChannelGetPosition(music, BASS_POS_MUSIC_ORDER);
if (pos != (QWORD)-1) {
gtk_range_set_value(GTK_RANGE(GetWidget("position")), LOWORD(pos));
sprintf(text, "%03d.%03d", LOWORD(pos), HIWORD(pos));
gtk_label_set_text(GTK_LABEL(GetWidget("posdisplay")), text);
}
return TRUE;
}
int main(int argc, char* argv[])
{
regex_t fregex;
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"modtest.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
gtk_combo_box_set_active(GTK_COMBO_BOX(GetWidget("interpolation")), 1);
gtk_combo_box_set_active(GTK_COMBO_BOX(GetWidget("ramping")), 2);
gtk_combo_box_set_active(GTK_COMBO_BOX(GetWidget("surround")), 0);
{ // 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, "mo3/it/xm/s3m/mtm/mod/umx");
regcomp(&fregex, "\\.(mo3|it|xm|s3m|mtm|mod|umx)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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(100, TimerProc, NULL);
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,183 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="window1">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS MOD music test</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<widget class="GtkButton" id="open">
<property name="label" translatable="yes">click here to open a file...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="info">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">100</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkLabel" id="posdisplay">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="play">
<property name="label" translatable="yes">Play / Pause</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHScale" id="position">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 0 1 10 0</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="PositionChange"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">10</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Interpolation</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="interpolation">
<property name="visible">True</property>
<property name="items" translatable="yes">off
linear
sinc</property>
<signal name="changed" handler="FlagChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">Ramping</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="ramping">
<property name="visible">True</property>
<property name="items" translatable="yes">off
normal
sensitive</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">Surround</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="surround">
<property name="visible">True</property>
<property name="items" translatable="yes">off
mode 1
mode 2</property>
<signal name="changed" handler="FlagChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

Binary file not shown.

View file

@ -0,0 +1,9 @@
TARGET = multi
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,255 @@
/*
BASS multiple output example
Copyright (c) 2001-2008 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
GtkWidget *filesel;
DWORD outdev[2]; // output devices
DWORD latency[2]; // latencies
HSTREAM chan[2]; // the streams
// 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 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 OpenClicked(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 devn = atoi(objname + 4) - 1; // get device number from button name ("openX")
BASS_StreamFree(chan[devn]); // free old stream before opening new
BASS_SetDevice(outdev[devn]); // set the device to create stream on
if (!(chan[devn] = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_SAMPLE_LOOP))) {
gtk_button_set_label(obj, "click here to open a file...");
Error("Can't play the file");
} else {
gtk_button_set_label(obj, file);
BASS_ChannelPlay(chan[devn], FALSE);
}
g_free(file);
}
}
// Cloning DSP function
void CALLBACK CloneDSP(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
BASS_StreamPutData((uintptr_t)user, buffer, length); // user = clone
}
void CloneClicked(GtkButton *obj, gpointer data)
{
const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
int devn = atoi(objname + 5) - 1; // get device number from button name ("cloneX")
BASS_CHANNELINFO i;
if (!BASS_ChannelGetInfo(chan[devn ^ 1], &i)) {
Error("Nothing to clone");
} else {
BASS_StreamFree(chan[devn]); // free old stream
BASS_SetDevice(outdev[devn]); // set the device to create stream on
if (!(chan[devn] = BASS_StreamCreate(i.freq, i.chans, i.flags, STREAMPROC_PUSH, 0))) { // create a "push" stream
char oname[10];
sprintf(oname, "open%d", devn + 1);
gtk_button_set_label(GTK_BUTTON(GetWidget(oname)), "click here to open a file...");
Error("Can't create clone");
} else {
BASS_ChannelLock(chan[devn ^ 1], TRUE); // lock source stream to synchonise buffer contents
BASS_ChannelSetDSP(chan[devn ^ 1], CloneDSP, (void*)(uintptr_t)chan[devn], 0); // set DSP to feed data to clone
{ // copy buffered data to clone
DWORD d = BASS_ChannelSeconds2Bytes(chan[devn], latency[devn] / 1000.f); // playback delay
DWORD c = BASS_ChannelGetData(chan[devn ^ 1], 0, BASS_DATA_AVAILABLE);
BYTE *buf = (BYTE*)malloc(c);
c = BASS_ChannelGetData(chan[devn ^ 1], buf, c);
if (c > d) BASS_StreamPutData(chan[devn], buf + d, c - d);
free(buf);
}
BASS_ChannelLock(chan[devn ^ 1], FALSE); // unlock source stream
BASS_ChannelPlay(chan[devn], FALSE); // play clone
{
char oname[10];
sprintf(oname, "open%d", devn + 1);
gtk_button_set_label(GTK_BUTTON(GetWidget(oname)), "clone");
}
}
}
}
void SwapClicked(GtkButton *obj, gpointer data)
{
{ // swap handles
HSTREAM temp = chan[0];
chan[0] = chan[1];
chan[1] = temp;
}
{ // swap text
GtkButton *open1 = GTK_BUTTON(GetWidget("open1")), *open2 = GTK_BUTTON(GetWidget("open2"));
char *temp = strdup(gtk_button_get_label(open1));
gtk_button_set_label(open1, gtk_button_get_label(open2));
gtk_button_set_label(open2, temp);
free(temp);
}
// update the channel devices
BASS_ChannelSetDevice(chan[0], outdev[0]);
BASS_ChannelSetDevice(chan[1], outdev[1]);
}
// Simple device selector dialog stuff begins here
void ListRowActivated(GtkTreeView *obj, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
{
int sel = 1 + *gtk_tree_path_get_indices(path); // get selection
gtk_dialog_response(GTK_DIALOG(GetWidget("dialog1")), sel);
}
int SelectDevice(const char *title)
{
int sel = 0;
GtkWidget *win = GetWidget("dialog1");
gtk_window_set_title(GTK_WINDOW(win), title);
GtkTreeView *list = GTK_TREE_VIEW(GetWidget("list"));
if (!gtk_tree_view_get_column(list, 0)) { // no column yet, so add one...
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(1, G_TYPE_STRING);
{
BASS_DEVICEINFO i;
int c;
for (c = 1; BASS_GetDeviceInfo(c, &i); c++) { // device 1 = 1st real device
if (i.flags & BASS_DEVICE_ENABLED) { // enabled, so add it...
GtkTreeIter it;
gtk_list_store_append(liststore, &it);
gtk_list_store_set(liststore, &it, 0, i.name, -1);
}
}
}
gtk_tree_view_set_model(list, GTK_TREE_MODEL(liststore));
g_object_unref(liststore);
{ // pre-select 1st entry
GtkTreeSelection *tsel = gtk_tree_view_get_selection(list);
GtkTreePath *tpath = gtk_tree_path_new_first();
gtk_tree_selection_select_path(tsel, tpath);
gtk_tree_path_free(tpath);
}
sel = gtk_dialog_run(GTK_DIALOG(win));
gtk_widget_hide(win);
return sel;
}
// Simple device selector dialog stuff ends here
int main(int argc, char* argv[])
{
regex_t fregex;
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 GUI
glade = glade_xml_new(GLADE_PATH"multi.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
// Let the user choose the output devices
outdev[0] = SelectDevice("Select output device #1");
outdev[1] = SelectDevice("Select output device #2");
{ // setup output devices
BASS_INFO info;
if (!BASS_Init(outdev[0], 44100, 0, NULL, NULL)) {
Error("Can't initialize device 1");
return 0;
}
BASS_GetInfo(&info);
latency[0] = info.latency;
if (!BASS_Init(outdev[1], 44100, 0, NULL, NULL)) {
Error("Can't initialize device 2");
return 0;
}
BASS_GetInfo(&info);
latency[1] = info.latency;
}
{
BASS_DEVICEINFO i;
BASS_GetDeviceInfo(outdev[0], &i);
gtk_frame_set_label(GTK_FRAME(GetWidget("device1")), i.name);
BASS_GetDeviceInfo(outdev[1], &i);
gtk_frame_set_label(GTK_FRAME(GetWidget("device2")), i.name);
}
{ // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
}
gtk_widget_show(win);
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
// release both devices
BASS_SetDevice(outdev[0]);
BASS_Free();
BASS_SetDevice(outdev[1]);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="width_request">450</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS multiple output example</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="device1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="clone1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">clone #2</property>
<property name="response_id">0</property>
<signal name="clicked" handler="CloneClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="swap">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">swap</property>
<property name="response_id">0</property>
<signal name="clicked" handler="SwapClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">10</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="device2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="clone2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">clone #1</property>
<property name="response_id">0</property>
<signal name="clicked" handler="CloneClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="dialog1">
<property name="width_request">300</property>
<property name="border_width">5</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkTreeView" id="list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
<signal name="row_activated" handler="ListRowActivated"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = netradio
PACKAGES = gtk+-2.0 libglade-2.0 gthread-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,283 @@
/*
BASS internet radio example
Copyright (c) 2002-2019 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <glib/gthread.h>
#include <stdlib.h>
#include <string.h>
#include "bass.h"
// HLS definitions (copied from BASSHLS.H)
#define BASS_SYNC_HLS_SEGMENT 0x10300
#define BASS_TAG_HLS_EXTINF 0x14000
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
DWORD req = 0; // request number/counter
HSTREAM chan;
guint buftimer = 0;
const char *urls[10] = { // preset stream URLs
"http://stream-dc1.radioparadise.com/rp_192m.ogg", "http://www.radioparadise.com/m3u/mp3-32.m3u",
"http://network.absoluteradio.co.uk/core/audio/mp3/live.pls?service=a8bb", "http://network.absoluteradio.co.uk/core/audio/aacplus/live.pls?service=a8",
"http://somafm.com/secretagent.pls", "http://somafm.com/secretagent32.pls",
"http://somafm.com/suburbsofgoa.pls", "http://somafm.com/suburbsofgoa32.pls",
"http://ai-radio.org/256.ogg", "http://ai-radio.org/48.aacp"
};
// 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 WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void gtk_label_set_text_8859(GtkLabel *label, const gchar *text)
{
gsize s;
char *utf = g_convert(text, -1, "UTF-8", "ISO-8859-1", NULL, &s, NULL);
if (utf) {
gtk_label_set_text(label, utf);
g_free(utf);
} else
gtk_label_set_text(label, text);
}
// update stream title from metadata
void DoMeta()
{
GtkLabel *label = GTK_LABEL(GetWidget("status1"));
const char *meta = BASS_ChannelGetTags(chan, BASS_TAG_META);
if (meta) { // got Shoutcast metadata
char *p = strstr(meta, "StreamTitle='");
if (p) {
p = strdup(p + 13);
strchr(p, ';')[-1] = 0;
gtk_label_set_text_8859(label, p);
free(p);
}
} else {
meta = BASS_ChannelGetTags(chan, BASS_TAG_OGG);
if (meta) { // got Icecast/OGG tags
const char *artist = NULL, *title = NULL, *p = meta;
for (; *p; p += strlen(p) + 1) {
if (!strncasecmp(p, "artist=", 7)) // found the artist
artist = p + 7;
if (!strncasecmp(p, "title=", 6)) // found the title
title = p + 6;
}
if (title) {
if (artist) {
char text[100];
snprintf(text, sizeof(text), "%s - %s", artist, title);
gtk_label_set_text(label, text);
} else
gtk_label_set_text(label, title);
}
} else {
meta = BASS_ChannelGetTags(chan, BASS_TAG_HLS_EXTINF);
if (meta) { // got HLS segment info
const char *p = strchr(meta, ',');
if (p) gtk_label_set_text(label, p + 1);
}
}
}
}
void CALLBACK MetaSync(HSYNC handle, DWORD channel, DWORD data, void *user)
{
gdk_threads_enter();
DoMeta();
gdk_threads_leave();
}
gboolean BufferTimerProc(gpointer data)
{ // monitor buffering progress
if (BASS_ChannelIsActive(chan) == BASS_ACTIVE_PLAYING) {
buftimer = 0;
gtk_label_set_text(GTK_LABEL(GetWidget("status2")), "playing");
{ // get the broadcast name and URL
const char *icy = BASS_ChannelGetTags(chan, BASS_TAG_ICY);
if (!icy) icy = BASS_ChannelGetTags(chan, BASS_TAG_HTTP); // no ICY tags, try HTTP
if (icy) {
for (; *icy; icy += strlen(icy) + 1) {
if (!strncasecmp(icy, "icy-name:", 9))
gtk_label_set_text_8859(GTK_LABEL(GetWidget("status2")), icy + 9);
if (!strncasecmp(icy, "icy-url:", 8))
gtk_label_set_text_8859(GTK_LABEL(GetWidget("status3")), icy + 8);
}
}
}
// get the stream title
DoMeta();
return FALSE; // stop monitoring
} else {
char text[32];
sprintf(text, "buffering... %d%%", 100 - (int)BASS_StreamGetFilePosition(chan, BASS_FILEPOS_BUFFERING));
gtk_label_set_text(GTK_LABEL(GetWidget("status2")), text);
return TRUE; // continue monitoring
}
}
void CALLBACK StallSync(HSYNC handle, DWORD channel, DWORD data, void *user)
{
if (!data) { // stalled
if (!buftimer) buftimer = g_timeout_add(50, BufferTimerProc, NULL); // start buffer monitoring
}
}
void CALLBACK EndSync(HSYNC handle, DWORD channel, DWORD data, void *user)
{
if (buftimer) {
g_source_remove(buftimer); // stop buffer monitoring
buftimer = 0;
}
gdk_threads_enter();
gtk_label_set_text(GTK_LABEL(GetWidget("status1")), "");
gtk_label_set_text(GTK_LABEL(GetWidget("status2")), "not playing");
gtk_label_set_text(GTK_LABEL(GetWidget("status3")), "");
gdk_threads_leave();
}
void CALLBACK StatusProc(const void *buffer, DWORD length, void *user)
{
if (buffer && !length && (uintptr_t)user == req) { // got HTTP/ICY tags, and this is still the current request
gdk_threads_enter();
gtk_label_set_text(GTK_LABEL(GetWidget("status3")), buffer); // display connection status
gdk_threads_leave();
}
}
void *OpenURL(void *url)
{
G_LOCK_DEFINE_STATIC(req);
DWORD c, r;
G_LOCK(req); // make sure only 1 thread at a time can do the following
r = ++req; // increment the request counter for this request
G_UNLOCK(req);
if (buftimer) {
g_source_remove(buftimer); // stop buffer monitoring
buftimer = 0;
}
BASS_StreamFree(chan); // close old stream
gdk_threads_enter();
gtk_label_set_text(GTK_LABEL(GetWidget("status1")), "");
gtk_label_set_text(GTK_LABEL(GetWidget("status2")), "connecting...");
gtk_label_set_text(GTK_LABEL(GetWidget("status3")), "");
gdk_threads_leave();
c = BASS_StreamCreateURL(url, 0, BASS_STREAM_BLOCK | BASS_STREAM_STATUS | BASS_STREAM_AUTOFREE, StatusProc, (void*)(uintptr_t)r);
free(url); // free temp URL buffer
G_LOCK(req);
if (r != req) { // there is a newer request, discard this stream
G_UNLOCK(req);
if (c) BASS_StreamFree(c);
return NULL;
}
chan = c; // this is now the current stream
G_UNLOCK(req);
if (!chan) {
gdk_threads_enter();
gtk_label_set_text(GTK_LABEL(GetWidget("status2")), "not playing");
Error("Can't play the stream");
gdk_threads_leave();
} else {
// start buffer monitoring
buftimer = g_timeout_add(50, BufferTimerProc, NULL);
// set syncs for stream title updates
BASS_ChannelSetSync(chan, BASS_SYNC_META, 0, MetaSync, 0); // Shoutcast
BASS_ChannelSetSync(chan, BASS_SYNC_OGG_CHANGE, 0, MetaSync, 0); // Icecast/OGG
BASS_ChannelSetSync(chan, BASS_SYNC_HLS_SEGMENT, 0, MetaSync, 0); // HLS
// set sync for stalling/buffering
BASS_ChannelSetSync(chan, BASS_SYNC_STALL, 0, StallSync, 0);
// set sync for end of stream
BASS_ChannelSetSync(chan, BASS_SYNC_END, 0, EndSync, 0);
// play it!
BASS_ChannelPlay(chan, FALSE);
}
return NULL;
}
void PresetClicked(GtkButton *obj, gpointer data)
{
const char *url;
const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
if (!strcmp(objname, "customopen")) { // play a custom URL
url = gtk_entry_get_text(GTK_ENTRY(GetWidget("customurl"))); // get the URL
} else { // play a preset
int preset = atoi(objname + 6) - 1; // get preset from button name ("presetX")
url = urls[preset];
}
if (GTK_TOGGLE_BUTTON(GetWidget("proxydirect"))->active)
BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY, NULL); // disable proxy
else
BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY, gtk_entry_get_text(GTK_ENTRY(GetWidget("proxyurl")))); // set proxy server
// open URL in a new thread (so that main thread is free)
#if GLIB_CHECK_VERSION(2,32,0)
{
GThread *thread = g_thread_new(NULL, OpenURL, strdup(url));
g_thread_unref(thread);
}
#else
g_thread_create(OpenURL, strdup(url), FALSE, NULL);
#endif
}
int main(int argc, char* argv[])
{
#if !GLIB_CHECK_VERSION(2,32,0)
g_thread_init(NULL);
#endif
gdk_threads_init();
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;
}
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); // enable playlist processing
BASS_SetConfig(BASS_CONFIG_NET_PREBUF_WAIT, 0); // disable BASS_StreamCreateURL pre-buffering
BASS_PluginLoad("libbass_aac.so", 0); // load BASS_AAC (if present) for AAC support
BASS_PluginLoad("libbassflac.so", 0); // load BASSFLAC (if present) for FLAC support
BASS_PluginLoad("libbasshls.so", 0); // load BASSHLS (if present) for HLS support
// initialize GUI
glade = glade_xml_new(GLADE_PATH"netradio.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
BASS_Free();
return 0;
}

View file

@ -0,0 +1,432 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="window1">
<property name="width_request">450</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS internet radio tuner</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">High bitrate</property>
<property name="ellipsize">end</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Low bitrate</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="preset1">
<property name="label" translatable="yes">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset3">
<property name="label" translatable="yes">2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset5">
<property name="label" translatable="yes">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset7">
<property name="label" translatable="yes">4</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset9">
<property name="label" translatable="yes">5</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="preset2">
<property name="label" translatable="yes">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset4">
<property name="label" translatable="yes">2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset6">
<property name="label" translatable="yes">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset8">
<property name="label" translatable="yes">4</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="preset10">
<property name="label" translatable="yes">5</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Presets</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox4">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkEntry" id="customurl">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">http://</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="customopen">
<property name="label" translatable="yes">Open</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="PresetClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">Custom</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkVBox" id="status">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="status1">
<property name="visible">True</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="status2">
<property name="visible">True</property>
<property name="label" translatable="yes">not playing</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="status3">
<property name="visible">True</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="label" translatable="yes">Currently playing</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkEntry" id="proxyurl">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="proxydirect">
<property name="label" translatable="yes">Direct connection</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="yalign">0</property>
<property name="label" translatable="yes">[user:pass@]server:port</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="label" translatable="yes">proxy</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
TARGET = plugins
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,225 @@
/*
BASS plugin test
Copyright (c) 2005-2009 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <regex.h>
#include <glob.h>
#include "bass.h"
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
GtkWidget *filesel;
DWORD chan; // the channel... HMUSIC or HSTREAM
// 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);
}
// translate a CTYPE value to text
const char *GetCTypeString(DWORD ctype, HPLUGIN plugin)
{
if (plugin) { // using a plugin
const BASS_PLUGININFO *pinfo = BASS_PluginGetInfo(plugin); // get plugin info
int a;
for (a = 0; a < pinfo->formatc; a++) {
if (pinfo->formats[a].ctype == ctype) // found a "ctype" match...
return pinfo->formats[a].name; // return it's name
}
}
// check built-in stream formats...
if (ctype == BASS_CTYPE_STREAM_OGG) return "Ogg Vorbis";
if (ctype == BASS_CTYPE_STREAM_MP1) return "MPEG layer 1";
if (ctype == BASS_CTYPE_STREAM_MP2) return "MPEG layer 2";
if (ctype == BASS_CTYPE_STREAM_MP3) return "MPEG layer 3";
if (ctype == BASS_CTYPE_STREAM_AIFF) return "Audio IFF";
if (ctype == BASS_CTYPE_STREAM_WAV_PCM) return "PCM WAVE";
if (ctype == BASS_CTYPE_STREAM_WAV_FLOAT) return "Floating-point WAVE";
if (ctype & BASS_CTYPE_STREAM_WAV) return "WAVE";
return "?";
}
#define GetWidget(id) glade_xml_get_widget(glade,id)
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 OpenClicked(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));
BASS_StreamFree(chan); // free old stream before opening new
if (!(chan = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_SAMPLE_LOOP))) {
gtk_button_set_label(obj, "click here to open a file...");
gtk_label_set_text(GTK_LABEL(GetWidget("info")), "");
gtk_range_set_range(GTK_RANGE(GetWidget("position")), 0, 0);
Error("Can't play the file");
} else {
gtk_button_set_label(obj, file);
{ // display the file type and length
char text[100];
QWORD bytes = BASS_ChannelGetLength(chan, BASS_POS_BYTE);
DWORD time = BASS_ChannelBytes2Seconds(chan, bytes);
BASS_CHANNELINFO info;
BASS_ChannelGetInfo(chan, &info);
sprintf(text, "channel type = %x (%s)\nlength = %llu (%u:%02u)",
info.ctype, GetCTypeString(info.ctype, info.plugin), bytes, time / 60, time % 60);
gtk_label_set_text(GTK_LABEL(GetWidget("info")), text);
gtk_range_set_range(GTK_RANGE(GetWidget("position")), 0, time); // update scroller range
}
BASS_ChannelPlay(chan, FALSE);
}
g_free(file);
}
}
gboolean PositionChange(GtkRange *range, GtkScrollType scroll, gdouble value, gpointer data)
{
BASS_ChannelSetPosition(chan, BASS_ChannelSeconds2Bytes(chan, value), BASS_POS_BYTE);
return FALSE;
}
gboolean TimerProc(gpointer data)
{
gtk_range_set_value(GTK_RANGE(GetWidget("position")), BASS_ChannelBytes2Seconds(chan, BASS_ChannelGetPosition(chan, BASS_POS_BYTE))); // update position
return TRUE;
}
int main(int argc, char* argv[])
{
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"plugins.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
{ // setup plugin list and file selector
GtkTreeView *list = GTK_TREE_VIEW(GetWidget("plugins"));
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(1, G_TYPE_STRING);
gtk_tree_view_set_model(list, GTK_TREE_MODEL(liststore));
g_object_unref(liststore);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(list), GTK_SELECTION_NONE);
GtkFileFilter *filter;
regex_t *fregex;
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, "BASS built-in (*.wav;*.aif;*.mp3;*.mp2;*.mp1;*.ogg)");
fregex = malloc(sizeof(*fregex));
regcomp(fregex, "\\.(mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, fregex, NULL);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filesel), filter);
{ // look for plugins (in the executable's directory)
glob_t g;
char path[300];
if (readlink("/proc/self/exe", path, 300) <= 0) {
Error("Can't locate executable");
return 0;
}
strcpy(strrchr(path, '/') + 1, "libbass*.so");
if (!glob(path, 0, 0, &g)) {
int a;
for (a = 0; a < g.gl_pathc; a++) {
HPLUGIN plug;
if (plug = BASS_PluginLoad(g.gl_pathv[a], 0)) { // plugin loaded...
// add it to the list
char *file = strrchr(g.gl_pathv[a], '/') + 1;
GtkTreeIter it;
gtk_list_store_append(liststore, &it);
gtk_list_store_set(liststore, &it, 0, file, -1);
// get plugin info to add to the file selector filter
const BASS_PLUGININFO *pinfo = BASS_PluginGetInfo(plug);
int b;
for (b = 0; b < pinfo->formatc; b++) {
char buf[300], *p;
filter = gtk_file_filter_new();
sprintf(buf, "%s (%s) - %s", pinfo->formats[b].name, pinfo->formats[b].exts, file);
gtk_file_filter_set_name(filter, buf);
// build filter regex
sprintf(buf, "\\.(%s)$", pinfo->formats[b].exts);
while (p = strchr(buf, '*')) { // find an extension
if (p[-1] == ';') // not the first...
p[-1] = '|'; // add an alternation
memmove(p, p + 2, strlen(p + 2) + 1); // remove the "*."
}
fregex = malloc(sizeof(*fregex));
regcomp(fregex, buf, REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, fregex, NULL);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filesel), filter);
}
}
}
globfree(&g);
}
{
GtkTreeIter it;
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &it)) { // no plugins...
gtk_list_store_append(liststore, &it);
gtk_list_store_set(liststore, &it, 0, "no plugins - visit the BASS webpage to get some", -1);
}
}
}
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);
// "free" the output device and all plugins
BASS_Free();
BASS_PluginFree(0);
return 0;
}

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS plugin test</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkTreeView" id="plugins">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">True</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Loaded plugins</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="open">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">10</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="info">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHScale" id="position">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 0 1 10 0</property>
<property name="draw_value">False</property>
<signal name="change_value" handler="PositionChange"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = rectest
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,276 @@
/*
BASS recording example
Copyright (c) 2002-2019 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <string.h>
#include <malloc.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; // file selector
#define FREQ 44100
#define CHANS 2
#define BUFSTEP 200000 // memory allocation unit
int input; // current input source
char *recbuf = NULL; // recording buffer
DWORD reclen; // recording length
HRECORD rchan = 0; // recording channel
HSTREAM chan = 0; // playback channel
// 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 WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
// buffer the recorded data
BOOL CALLBACK RecordingCallback(HRECORD handle, const void *buffer, DWORD length, void *user)
{
// increase buffer size if needed
if ((reclen % BUFSTEP) + length >= BUFSTEP) {
recbuf = realloc(recbuf, ((reclen + length) / BUFSTEP + 1) * BUFSTEP);
if (!recbuf) {
rchan = 0;
Error("Out of memory!");
gtk_button_set_label(GTK_BUTTON(GetWidget("record")), "Record");
return FALSE; // stop recording
}
}
// buffer the data
memcpy(recbuf + reclen, buffer, length);
reclen += length;
return TRUE; // continue recording
}
void StartRecording()
{
WAVEFORMATEX *wf;
if (recbuf) { // free old recording
BASS_StreamFree(chan);
chan = 0;
free(recbuf);
recbuf = NULL;
gtk_widget_set_sensitive(GetWidget("play"), FALSE);
gtk_widget_set_sensitive(GetWidget("save"), FALSE);
}
// allocate initial buffer and make space for WAVE header
recbuf = malloc(BUFSTEP);
reclen = 44;
// fill the WAVE header
memcpy(recbuf, "RIFF\0\0\0\0WAVEfmt \20\0\0\0", 20);
memcpy(recbuf + 36, "data\0\0\0\0", 8);
wf = (WAVEFORMATEX*)(recbuf + 20);
wf->wFormatTag = 1;
wf->nChannels = CHANS;
wf->wBitsPerSample = 16;
wf->nSamplesPerSec = FREQ;
wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample / 8;
wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
// start recording
rchan = BASS_RecordStart(FREQ, CHANS, 0, RecordingCallback, 0);
if (!rchan) {
Error("Can't start recording");
free(recbuf);
recbuf = 0;
return;
}
gtk_button_set_label(GTK_BUTTON(GetWidget("record")), "Stop");
}
void StopRecording()
{
BASS_ChannelStop(rchan);
rchan = 0;
gtk_button_set_label(GTK_BUTTON(GetWidget("record")), "Record");
// complete the WAVE header
*(DWORD*)(recbuf + 4) = reclen - 8;
*(DWORD*)(recbuf + 40) = reclen - 44;
// enable "save" button
gtk_widget_set_sensitive(GetWidget("save"), TRUE);
// create a stream from the recording
if (chan = BASS_StreamCreateFile(TRUE, recbuf, 0, reclen, 0))
gtk_widget_set_sensitive(GetWidget("play"), TRUE); // enable "play" button
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
// write the recorded data to disk
void WriteToDisk()
{
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));
FILE *fp = fopen(file, "wb");
if (fp) {
fwrite(recbuf, reclen, 1, fp);
fclose(fp);
} else
Error("Can't create the file");
g_free(file);
}
}
void UpdateInputInfo()
{
float level;
int r = BASS_RecordGetInput(input, &level); // get info on the input
if (r == -1 || level < 0) { // failed
r = BASS_RecordGetInput(-1, &level); // try master input instead
if (r == -1 || level < 0) level = 1; // that failed too, just display 100%
}
gtk_range_set_value(GTK_RANGE(GetWidget("level")), level * 100); // set the level slider
}
void RecordClicked(GtkButton *obj, gpointer data)
{
if (!rchan)
StartRecording();
else
StopRecording();
}
void PlayClicked(GtkButton *obj, gpointer data)
{
BASS_ChannelPlay(chan, TRUE); // play the recorded data
}
void SaveClicked(GtkButton *obj, gpointer data)
{
WriteToDisk();
}
void InputChanged(GtkComboBox *obj, gpointer data)
{
int i;
input = gtk_combo_box_get_active(obj); // get the selection
// enable the selected input
for (i = 0; BASS_RecordSetInput(i, BASS_INPUT_OFF, -1); i++); // 1st disable all inputs, then...
BASS_RecordSetInput(input, BASS_INPUT_ON, -1); // enable the selected
UpdateInputInfo(); // update info
}
void LevelChanged(GtkRange *range, gpointer data)
{
double level = gtk_range_get_value(range) / 100;
if (!BASS_RecordSetInput(input, 0, level)) // failed to set input level
BASS_RecordSetInput(-1, 0, level); // try master level instead
}
gboolean TimerProc(gpointer data)
{ // update the recording/playback counter
char text[30] = "";
if (rchan) { // recording
if (!BASS_ChannelIsActive(rchan)) { // the recording has stopped, eg. unplugged device
StopRecording();
Error("The recording stopped");
return TRUE;
}
sprintf(text, "%d", reclen - 44);
} else if (chan) {
if (BASS_ChannelIsActive(chan)) // playing
sprintf(text, "%lld / %lld", BASS_ChannelGetPosition(chan, BASS_POS_BYTE), BASS_ChannelGetLength(chan, BASS_POS_BYTE));
else
sprintf(text, "%lld", BASS_ChannelGetLength(chan, BASS_POS_BYTE));
}
gtk_label_set(GTK_LABEL(GetWidget("status")), text);
return TRUE;
}
int main(int argc, char* argv[])
{
regex_t fregex;
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;
}
// initalize default recording device
if (!BASS_RecordInit(-1)) {
Error("Can't initialize recording device");
return 0;
}
// initialize default output device
if (!BASS_Init(-1, FREQ, 0, NULL, NULL))
Error("Can't initialize output device");
// initialize GUI
glade = glade_xml_new(GLADE_PATH"rectest.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
{ // get list of inputs
int c;
const char *i;
GtkComboBox *list = GTK_COMBO_BOX(GetWidget("input"));
for (c = 0; i = BASS_RecordGetInputName(c); c++) {
gtk_combo_box_append_text(list, i);
if (!(BASS_RecordGetInput(c, NULL) & BASS_INPUT_OFF)) { // this 1 is currently "on"
input = c;
gtk_combo_box_set_active(list, input);
UpdateInputInfo(); // display info
}
}
}
{ // initialize file selector
GtkFileFilter *filter;
filesel = gtk_file_chooser_dialog_new("Save File", GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filesel), TRUE);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "WAV files");
regcomp(&fregex, "\\.wav$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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(200, TimerProc, NULL);
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
// release all BASS stuff
BASS_RecordFree();
BASS_Free();
return 0;
}

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS recording test</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkComboBox" id="input">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<signal name="changed" handler="InputChanged"/>
</widget>
</child>
<child>
<widget class="GtkHScale" id="level">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">0 0 100 1 10 0</property>
<property name="draw_value">False</property>
<signal name="value_changed" handler="LevelChanged"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="record">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Record</property>
<property name="response_id">0</property>
<signal name="clicked" handler="RecordClicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="play">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Play</property>
<property name="response_id">0</property>
<signal name="clicked" handler="PlayClicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="save">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">Save</property>
<property name="response_id">0</property>
<signal name="clicked" handler="SaveClicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="status">
<property name="width_request">250</property>
<property name="visible">True</property>
</widget>
</child>
</widget>
</child>
<child>
<placeholder/>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = speakers
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,158 @@
/*
BASS multi-speaker example
Copyright (c) 2003-2009 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;
DWORD flags[4] = { BASS_SPEAKER_FRONT,BASS_SPEAKER_REAR,BASS_SPEAKER_CENLFE,BASS_SPEAKER_REAR2 };
HSTREAM chan[4];
// 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 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 OpenClicked(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 speaker = atoi(objname + 4) - 1; // get speaker pair number from button name ("openX")
BASS_StreamFree(chan[speaker]); // free old stream before opening new
if (!(chan[speaker] = BASS_StreamCreateFile(FALSE, file, 0, 0, flags[speaker] | BASS_SAMPLE_LOOP))) {
gtk_button_set_label(obj, "click here to open a file...");
Error("Can't play the file");
} else {
gtk_button_set_label(obj, file);
BASS_ChannelPlay(chan[speaker], FALSE);
}
g_free(file);
}
}
void SwapClicked(GtkButton *obj, gpointer data)
{
const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
int speaker = atoi(objname + 4) - 1; // get speaker pair number from button name ("swapX")
{ // swap handles
HSTREAM temp = chan[speaker];
chan[speaker] = chan[speaker + 1];
chan[speaker + 1] = temp;
}
{ // swap text
GtkButton *open1, *open2;
char bname[10], *temp;
sprintf(bname, "open%d", 1 + speaker);
open1 = GTK_BUTTON(GetWidget(bname));
sprintf(bname, "open%d", 1 + speaker + 1);
open2 = GTK_BUTTON(GetWidget(bname));
temp = strdup(gtk_button_get_label(open1));
gtk_button_set_label(open1, gtk_button_get_label(open2));
gtk_button_set_label(open2, temp);
free(temp);
}
// update the channel devices
BASS_ChannelFlags(chan[speaker], flags[speaker], BASS_SPEAKER_FRONT);
BASS_ChannelFlags(chan[speaker + 1], flags[speaker + 1], BASS_SPEAKER_FRONT);
}
int main(int argc, char* argv[])
{
regex_t fregex;
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 device
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
Error("Can't initialize device");
return 0;
}
// initialize GUI
glade = glade_xml_new(GLADE_PATH"speakers.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
{ // check how many speakers the device supports
BASS_INFO i;
BASS_GetInfo(&i);
if (i.speakers < 8) {
gtk_widget_set_sensitive(GetWidget("open4"), FALSE);
gtk_widget_set_sensitive(GetWidget("swap3"), FALSE);
}
if (i.speakers < 6) {
gtk_widget_set_sensitive(GetWidget("open3"), FALSE);
gtk_widget_set_sensitive(GetWidget("swap2"), FALSE);
}
if (i.speakers < 4) {
gtk_widget_set_sensitive(GetWidget("open2"), FALSE);
gtk_widget_set_sensitive(GetWidget("swap1"), FALSE);
}
}
{ // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
}
gtk_widget_show(win);
gtk_main();
gtk_widget_destroy(filesel);
regfree(&fregex);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="width_request">450</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS multi-speaker example</property>
<property name="resizable">False</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">1 - front</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="swap1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">swap</property>
<property name="response_id">0</property>
<signal name="clicked" handler="SwapClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">10</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">2 - rear</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox4">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="swap2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">swap</property>
<property name="response_id">0</property>
<signal name="clicked" handler="SwapClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">10</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox6">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">3 - center/FLE</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="swap3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">swap</property>
<property name="response_id">0</property>
<signal name="clicked" handler="SwapClicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">10</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">5</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="open4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">click here to open a file...</property>
<property name="response_id">0</property>
<signal name="clicked" handler="OpenClicked"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">4 - rear center</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,9 @@
TARGET = spectrum
PACKAGES = gtk+-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,241 @@
/*
BASS spectrum analyser example
Copyright (c) 2002-2012 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <string.h>
#include <regex.h>
#include <math.h>
#include "bass.h"
#define SPECWIDTH 368 // display width (should be multiple of 4)
#define SPECHEIGHT 127 // height (changing requires palette adjustments too)
#pragma pack(1)
typedef struct {
BYTE rgbRed, rgbGreen, rgbBlue;
} RGB;
#pragma pack()
GtkWidget *win = 0;
DWORD chan;
GtkWidget *speci;
GdkPixbuf *specpb;
RGB palette[256];
int specmode = 0, specpos = 0; // spectrum mode (and marker pos for 2nd mode)
// 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);
}
void WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
void WindowButtonRelease(GtkWidget *obj, GdkEventButton *event, gpointer data)
{
if (event->type == GDK_BUTTON_RELEASE) {
RGB *specbuf = (RGB*)gdk_pixbuf_get_pixels(specpb);
specmode = (specmode + 1) % 4; // next spectrum mode
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf)); // clear display
}
}
gboolean FileExtensionFilter(const GtkFileFilterInfo *info, gpointer data)
{
return !regexec((regex_t*)data, info->filename, 0, NULL, 0);
}
// select a file to play, and play it
BOOL PlayFile()
{
BOOL ret = FALSE;
regex_t fregex;
GtkWidget *filesel; // 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, "Playable files");
regcomp(&fregex, "\\.(mo3|xm|mod|s3m|it|umx|mp[1-3]|ogg|wav|aif)$", REG_ICASE | REG_NOSUB | REG_EXTENDED);
gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, FileExtensionFilter, &fregex, 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);
if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
gtk_widget_hide(filesel);
if (!(chan = BASS_StreamCreateFile(FALSE, file, 0, 0, BASS_SAMPLE_LOOP))
&& !(chan = BASS_MusicLoad(FALSE, file, 0, 0, BASS_MUSIC_RAMP | BASS_SAMPLE_LOOP, 1))) {
Error("Can't play file");
} else {
BASS_ChannelPlay(chan, FALSE);
ret = TRUE;
}
g_free(file);
}
gtk_widget_destroy(filesel);
return ret;
}
// update the spectrum display - the interesting bit :)
gboolean UpdateSpectrum(gpointer data)
{
int x, y, y1;
RGB *specbuf = (RGB*)gdk_pixbuf_get_pixels(specpb);
if (specmode == 3) { // waveform
int c;
float *buf;
BASS_CHANNELINFO ci;
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
BASS_ChannelGetInfo(chan, &ci); // get number of channels
buf = alloca(ci.chans * SPECWIDTH * sizeof(float)); // allocate buffer for data
BASS_ChannelGetData(chan, buf, (ci.chans * SPECWIDTH * sizeof(float)) | BASS_DATA_FLOAT); // get the sample data (floating-point to avoid 8 & 16 bit processing)
for (c = 0; c < ci.chans; c++) {
for (x = 0; x < SPECWIDTH; x++) {
int v = (1 - buf[x * ci.chans + c]) * SPECHEIGHT / 2; // invert and scale to fit display
if (v < 0) v = 0;
else if (v >= SPECHEIGHT) v = SPECHEIGHT - 1;
if (!x) y = v;
do { // draw line from previous sample...
if (y < v) y++;
else if (y > v) y--;
specbuf[y * SPECWIDTH + x] = palette[c & 1 ? 127 : 1]; // left=green, right=red (could add more colours to palette for more chans)
} while (y != v);
}
}
} else {
float fft[1024];
BASS_ChannelGetData(chan, fft, BASS_DATA_FFT2048); // get the FFT data
if (!specmode) { // "normal" FFT
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
for (x = 0; x < SPECWIDTH / 2; x++) {
#if 1
y = sqrt(fft[x + 1]) * 3 * SPECHEIGHT - 4; // scale it (sqrt to make low values more visible)
#else
y = fft[x + 1] * 10 * SPECHEIGHT; // scale it (linearly)
#endif
if (y > SPECHEIGHT) y = SPECHEIGHT; // cap it
if (x && (y1 = (y + y1) / 2)) // interpolate from previous to make the display smoother
while (--y1 >= 0) specbuf[(SPECHEIGHT - 1 - y1) * SPECWIDTH + x * 2 - 1] = palette[y1 + 1];
y1 = y;
while (--y >= 0) specbuf[(SPECHEIGHT - 1 - y) * SPECWIDTH + x * 2] = palette[y + 1]; // draw level
}
} else if (specmode == 1) { // logarithmic, acumulate & average bins
int b0 = 0;
memset(specbuf, 0, SPECWIDTH * SPECHEIGHT * sizeof(*specbuf));
#define BANDS 28
for (x = 0; x < BANDS; x++) {
float peak = 0;
int b1 = pow(2, x * 10.0 / (BANDS - 1));
if (b1 > 1023) b1 = 1023;
if (b1 <= b0) b1 = b0 + 1; // make sure it uses at least 1 FFT bin
for (; b0 < b1; b0++)
if (peak < fft[1 + b0]) peak = fft[1 + b0];
y = sqrt(peak) * 3 * SPECHEIGHT - 4; // scale it (sqrt to make low values more visible)
if (y > SPECHEIGHT) y = SPECHEIGHT; // cap it
while (--y >= 0)
for (y1 = 0; y1 < SPECWIDTH / BANDS - 2; y1++)
specbuf[(SPECHEIGHT - 1 - y) * SPECWIDTH + x * (SPECWIDTH / BANDS) + y1] = palette[y + 1]; // draw bar
}
} else { // "3D"
for (x = 0; x < SPECHEIGHT; x++) {
y = sqrt(fft[x + 1]) * 3 * 127; // scale it (sqrt to make low values more visible)
if (y > 127) y = 127; // cap it
specbuf[(SPECHEIGHT - 1 - x) * SPECWIDTH + specpos] = palette[128 + y]; // plot it
}
// move marker onto next position
specpos = (specpos + 1) % SPECWIDTH;
for (x = 0; x < SPECHEIGHT; x++) specbuf[x * SPECWIDTH + specpos] = palette[255];
}
}
gtk_image_set_from_pixbuf(GTK_IMAGE(speci), specpb); // update the display
return TRUE;
}
int main(int argc, char* argv[])
{
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 BASS
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
Error("Can't initialize device");
return 0;
}
if (!PlayFile()) { // start a file playing
BASS_Free();
return 0;
}
// create the window
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
gtk_window_set_title(GTK_WINDOW(win), "BASS spectrum example (click to toggle mode)");
g_signal_connect(win, "destroy", GTK_SIGNAL_FUNC(WindowDestroy), NULL);
GtkWidget *ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(win), ebox);
g_signal_connect(ebox, "button-release-event", GTK_SIGNAL_FUNC(WindowButtonRelease), NULL);
// create the bitmap
specpb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, SPECWIDTH, SPECHEIGHT);
speci = gtk_image_new_from_pixbuf(specpb);
gtk_container_add(GTK_CONTAINER(ebox), speci);
{ // setup palette
RGB *pal = palette;
int a;
memset(palette, 0, sizeof(palette));
for (a = 1; a < 128; a++) {
pal[a].rgbGreen = 256 - 2 * a;
pal[a].rgbRed = 2 * a;
}
for (a = 0; a < 32; a++) {
pal[128 + a].rgbBlue = 8 * a;
pal[128 + 32 + a].rgbBlue = 255;
pal[128 + 32 + a].rgbRed = 8 * a;
pal[128 + 64 + a].rgbRed = 255;
pal[128 + 64 + a].rgbBlue = 8 * (31 - a);
pal[128 + 64 + a].rgbGreen = 8 * a;
pal[128 + 96 + a].rgbRed = 255;
pal[128 + 96 + a].rgbGreen = 255;
pal[128 + 96 + a].rgbBlue = 8 * a;
}
}
// setup update timer (40hz)
g_timeout_add(25, UpdateSpectrum, NULL);
gtk_widget_show_all(win);
gtk_main();
g_object_unref(specpb);
BASS_Free();
return 0;
}

View file

@ -0,0 +1,9 @@
TARGET = synth
PACKAGES = gtk+-2.0 libglade-2.0
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

View file

@ -0,0 +1,146 @@
/*
BASS simple synth
Copyright (c) 2001-2017 Un4seen Developments Ltd.
*/
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "bass.h"
// path to glade file
#ifndef GLADE_PATH
#define GLADE_PATH ""
#endif
GladeXML *glade;
GtkWidget *win = 0;
BASS_INFO info;
HSTREAM stream; // the stream
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define KEYS 20
const guint keys[KEYS] = {
'Q','2','W','3','E','R','5','T','6','Y','7','U',
'I','9','O','0','P', GDK_bracketleft, GDK_equal, GDK_bracketright
};
#define MAXVOL 0.22
#define DECAY (MAXVOL/4000)
float vol[KEYS] = { 0 }, pos[KEYS]; // keys' volume and pos
const DWORD fxtype[5] = { BASS_FX_DX8_CHORUS,BASS_FX_DX8_DISTORTION,BASS_FX_DX8_ECHO,BASS_FX_DX8_FLANGER,BASS_FX_DX8_REVERB };
HFX fx[5] = { 0 }; // effect handles
// 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 WindowDestroy(GtkObject *obj, gpointer data)
{
gtk_main_quit();
}
DWORD CALLBACK WriteStream(HSTREAM handle, float *buffer, DWORD length, void *user)
{
int k, c;
float omega, s;
memset(buffer, 0, length);
for (k = 0; k < KEYS; k++) {
if (!vol[k]) continue;
omega = 2 * M_PI * pow(2.0, (k + 3) / 12.0) * 440.0 / info.freq;
for (c = 0; c < length / sizeof(float); c += 2) {
buffer[c] += sin(pos[k]) * vol[k];
buffer[c + 1] = buffer[c]; // left and right channels are the same
pos[k] += omega;
if (vol[k] < MAXVOL) {
vol[k] -= DECAY;
if (vol[k] <= 0) { // faded-out
vol[k] = 0;
break;
}
}
}
pos[k] = fmod(pos[k], 2 * M_PI);
}
return length;
}
void FXToggled(GtkToggleButton *obj, gpointer data)
{ // toggle effects
const gchar *objname = gtk_widget_get_name(GTK_WIDGET(obj));
int n = atoi(objname + 2);
if (fx[n]) {
BASS_ChannelRemoveFX(stream, fx[n]);
fx[n] = 0;
} else
fx[n] = BASS_ChannelSetFX(stream, fxtype[n], 0);
}
gboolean KeyHandler(GtkWidget *grab_widget, GdkEventKey *event, gpointer data)
{
int key, kv = event->keyval;
if (kv >= 'a' && kv <= 'z') kv -= 0x20;
for (key = 0; key < KEYS; key++) {
if (kv == keys[key]) {
if (event->type == GDK_KEY_PRESS && vol[key] < MAXVOL) {
pos[key] = 0;
vol[key] = MAXVOL + DECAY / 2; // start key (setting "vol" slightly higher than MAXVOL to cover any rounding-down)
} else if (event->type == GDK_KEY_RELEASE && vol[key])
vol[key] -= DECAY; // trigger key fadeout
break;
}
}
return FALSE;
}
int main(int argc, char* argv[])
{
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"synth.glade", NULL, NULL);
if (!glade) return 0;
win = GetWidget("window1");
if (!win) return 0;
glade_xml_signal_autoconnect(glade);
BASS_GetInfo(&info);
stream = BASS_StreamCreate(info.freq, 2, BASS_SAMPLE_FLOAT, (STREAMPROC*)WriteStream, 0); // create a stream (stereo for effects)
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_BUFFER, 0); // no buffering for minimum latency
BASS_ChannelPlay(stream, FALSE); // start it
g_signal_connect(win, "key-press-event", G_CALLBACK(KeyHandler), NULL);
g_signal_connect(win, "key-release-event", G_CALLBACK(KeyHandler), NULL);
gtk_main();
BASS_Free();
return 0;
}

View file

@ -0,0 +1,174 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.8 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="window1">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">BASS simple sinewave synth</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="WindowDestroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">20</property>
<property name="right_padding">20</property>
<child>
<widget class="GtkLabel" id="keys">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;big&gt;&lt;tt&gt; 2 3 5 6 7 9 0 =
Q W ER T Y UI O P[ ]&lt;/tt&gt;&lt;/big&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Press these keys to play</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">out</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="fx4">
<property name="label" translatable="yes">reverb</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FXToggled"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="fx2">
<property name="label" translatable="yes">echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FXToggled"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="fx0">
<property name="label" translatable="yes">chorus</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FXToggled"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="fx3">
<property name="label" translatable="yes">flanger</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FXToggled"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="fx1">
<property name="label" translatable="yes">distortion</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="FXToggled"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="label" translatable="yes">Effects</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,8 @@
TARGET = writewav
include ../makefile.in
all: $(TARGET)
clean:
$(RM) $(TARGET)

Binary file not shown.

Binary file not shown.

Binary file not shown.

2127
bass-sys/win/bass24/bass.txt Normal file

File diff suppressed because it is too large Load diff

1160
bass-sys/win/bass24/c/bass.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,163 @@
/*
BASS simple console player
Copyright (c) 1999-2019 Un4seen Developments Ltd.
*/
#include <stdlib.h>
#include <stdio.h>
#include "bass.h"
#ifdef _WIN32 // Windows
#include <conio.h>
#else // OSX/Linux
#include <sys/time.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#define Sleep(x) usleep(x*1000)
int _kbhit()
{
int r;
fd_set rfds;
struct timeval tv = { 0 };
struct termios term, oterm;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
cfmakeraw(&term);
tcsetattr(0, TCSANOW, &term);
FD_ZERO(&rfds);
FD_SET(0, &rfds);
r = select(1, &rfds, NULL, NULL, &tv);
tcsetattr(0, TCSANOW, &oterm);
return r;
}
#endif
// display error messages
void Error(const char *text)
{
printf("Error(%d): %s\n", BASS_ErrorGetCode(), text);
BASS_Free();
exit(0);
}
void ListDevices()
{
BASS_DEVICEINFO di;
int a;
for (a = 0; BASS_GetDeviceInfo(a, &di); a++) {
if (di.flags & BASS_DEVICE_ENABLED) // enabled output device
printf("dev %d: %s\n", a, di.name);
}
}
void main(int argc, char **argv)
{
DWORD chan, act, time, level;
BOOL ismod;
QWORD pos;
int a, filep, device = -1;
printf("BASS simple console player\n"
"--------------------------\n");
// check the correct BASS was loaded
if (HIWORD(BASS_GetVersion()) != BASSVERSION) {
printf("An incorrect version of BASS was loaded");
return;
}
for (filep = 1; filep < argc; filep++) {
if (!strcmp(argv[filep], "-l")) {
ListDevices();
return;
} else if (!strcmp(argv[filep], "-d") && filep + 1 < argc) device = atoi(argv[++filep]);
else break;
}
if (filep == argc) {
printf("\tusage: contest [-l] [-d #] <file>\n"
"\t-l = list devices\n"
"\t-d = device number\n");
return;
}
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); // enable playlist processing
BASS_SetConfig(BASS_CONFIG_NET_PREBUF_WAIT, 0); // disable BASS_StreamCreateURL pre-buffering
// initialize output device
if (!BASS_Init(device, 44100, 0, 0, NULL))
Error("Can't initialize device");
// try streaming the file/url
if ((chan = BASS_StreamCreateFile(FALSE, argv[filep], 0, 0, BASS_SAMPLE_LOOP))
|| (chan = BASS_StreamCreateURL(argv[filep], 0, BASS_SAMPLE_LOOP, 0, 0))) {
pos = BASS_ChannelGetLength(chan, BASS_POS_BYTE);
if (BASS_StreamGetFilePosition(chan, BASS_FILEPOS_DOWNLOAD) != -1) {
// streaming from the internet
if (pos != -1)
printf("streaming internet file [%lld bytes]", pos);
else
printf("streaming internet file");
} else
printf("streaming file [%lld bytes]", pos);
ismod = FALSE;
} else {
// try loading the MOD (with looping, sensitive ramping, and calculate the duration)
if (!(chan = BASS_MusicLoad(FALSE, argv[filep], 0, 0, BASS_SAMPLE_LOOP | BASS_MUSIC_RAMPS | BASS_MUSIC_PRESCAN, 1)))
// not a MOD either
Error("Can't play the file");
{ // count channels
float dummy;
for (a = 0; BASS_ChannelGetAttribute(chan, BASS_ATTRIB_MUSIC_VOL_CHAN + a, &dummy); a++);
}
printf("playing MOD music \"%s\" [%u chans, %u orders]",
BASS_ChannelGetTags(chan, BASS_TAG_MUSIC_NAME), a, (DWORD)BASS_ChannelGetLength(chan, BASS_POS_MUSIC_ORDER));
pos = BASS_ChannelGetLength(chan, BASS_POS_BYTE);
ismod = TRUE;
}
// display the time length
if (pos != -1) {
time = (DWORD)BASS_ChannelBytes2Seconds(chan, pos);
printf(" %u:%02u\n", time / 60, time % 60);
} else // no time length available
printf("\n");
BASS_ChannelPlay(chan, FALSE);
while (!_kbhit() && (act = BASS_ChannelIsActive(chan))) {
// display some stuff and wait a bit
level = BASS_ChannelGetLevel(chan);
pos = BASS_ChannelGetPosition(chan, BASS_POS_BYTE);
time = BASS_ChannelBytes2Seconds(chan, pos);
printf("pos %09llu", pos);
if (ismod) {
pos = BASS_ChannelGetPosition(chan, BASS_POS_MUSIC_ORDER);
printf(" (%03u:%03u)", LOWORD(pos), HIWORD(pos));
}
printf(" - %u:%02u - L ", time / 60, time % 60);
if (act == BASS_ACTIVE_STALLED) { // playback has stalled
printf("- buffering: %3u%% -", 100 - (DWORD)BASS_StreamGetFilePosition(chan, BASS_FILEPOS_BUFFERING));
} else {
for (a = 27204; a > 200; a = a * 2 / 3) putchar(LOWORD(level) >= a ? '*' : '-');
putchar(' ');
for (a = 210; a < 32768; a = a * 3 / 2) putchar(HIWORD(level) >= a ? '*' : '-');
}
printf(" R - cpu %.2f%% \r", BASS_GetCPU());
fflush(stdout);
Sleep(50);
}
printf(" \r");
// wind the frequency down...
BASS_ChannelSlideAttribute(chan, BASS_ATTRIB_FREQ, 1000, 500);
Sleep(400);
// ...and fade-out to avoid a "click"
BASS_ChannelSlideAttribute(chan, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, -1, 100);
// wait for slide to finish (could use BASS_SYNC_SLIDE instead)
while (BASS_ChannelIsSliding(chan, 0)) Sleep(1);
BASS_Free();
}

Some files were not shown because too many files have changed in this diff Show more