Start adding support for sound in Sudoku via ALSA.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.1@4684 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Michael R Sweet 2005-12-05 00:55:01 +00:00
parent 29a41762fb
commit 2f40b8b80b
5 changed files with 224 additions and 4 deletions

View File

@ -249,6 +249,11 @@
#undef HAVE_PTHREAD
#undef HAVE_PTHREAD_H
/*
* Do we have the ALSA library?
*/
#undef HAVE_ALSA_ASOUNDLIB_H
/*
* End of "$Id$".

View File

@ -389,6 +389,15 @@ AC_CHECK_FUNCS(localeconv)
dnl FLTK library uses math library functions...
AC_SEARCH_LIBS(pow, m)
dnl Check for audio libraries...
AUDIOLIBS=""
AC_CHECK_HEADER(alsa/asoundlib.h,
AC_DEFINE(HAVE_ALSA_ASOUNDLIB_H)
AUDIOLIBS="-lasound")
AC_SUBST(AUDIOLIBS)
dnl Check for image libraries...
SAVELIBS="$LIBS"
IMAGELIBS=""

View File

@ -73,6 +73,7 @@ IMGDSONAME = @IMGDSONAME@
DSOCOMMAND = @DSOCOMMAND@
# libraries to link with:
AUDIOLIBS = @AUDIOLIBS@
LDLIBS = @LDFLAGS@ @LIBS@
GLDLIBS = @LDFLAGS@ @GLLIB@ @LIBS@
LINKFLTK = @LINKFLTK@

View File

@ -335,14 +335,14 @@ subwindow$(EXEEXT): subwindow.o
sudoku: sudoku.o
echo Linking $@...
$(CXX) -I.. $(CXXFLAGS) sudoku.o -o $@ $(LINKFLTKIMG) $(LDLIBS)
$(CXX) -I.. $(CXXFLAGS) sudoku.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
$(CP) sudoku$(EXEEXT) sudoku.app/Contents/MacOS
$(POSTBUILD) $@ ../FL/mac.r
sudoku.exe: sudoku.o sudoku.rc
echo Linking $@...
windres sudoku.rc sudokures.o
$(CXX) -I.. $(CXXFLAGS) sudoku.o sudokures.o -o $@ $(LINKFLTKIMG) $(LDLIBS)
$(CXX) -I.. $(CXXFLAGS) sudoku.o sudokures.o -o $@ $(AUDIOLIBS) $(LINKFLTKIMG) $(LDLIBS)
symbols$(EXEEXT): symbols.o

View File

@ -40,6 +40,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#ifdef WIN32
# include "sudokurc.h"
@ -47,6 +48,13 @@
# include "sudoku.xbm"
#endif // WIN32
#include <config.h>
#ifdef HAVE_ALSA_ASOUNDLIB_H
# define ALSA_PCM_NEW_HW_PARAMS_API
# include <alsa/asoundlib.h>
#endif // HAVE_ALSA_ASOUNDLIB_H
//
// Default sizes...
@ -61,6 +69,181 @@
# define MENU_OFFSET 25
#endif // __APPLE__
// Sound class
class SudokuSound {
// Private, OS-specific data...
#ifdef __APPLE__
#elif defined(WIN32)
#else
# ifdef HAVE_ALSA_ASOUNDLIB_H
snd_pcm_t *handle;
# endif // HAVE_ALSA_ASOUNDLIB_H
#endif // __APPLE__
// Common data...
static int frequencies[9];
static short *sample_data[9];
static int sample_size;
public:
SudokuSound();
~SudokuSound();
void play(char note);
};
int SudokuSound::frequencies[9] = {
880, // A(5)
988, // B(5)
1046, // C(5)
1174, // D(5)
1318, // E(5)
1396, // F(5)
1568, // G(5)
1760, // H (A6)
1976 // I (B6)
};
short *SudokuSound::sample_data[9] = { 0 };
int SudokuSound::sample_size = 0;
// Initialize the SudokuSound class
SudokuSound::SudokuSound() {
sample_size = 0;
#ifdef __APPLE__
#elif defined(WIN32)
#else
# ifdef HAVE_ALSA_ASOUNDLIB_H
handle = NULL;
if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) {
// Initialize PCM sound stuff...
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, 2);
unsigned rate = 44100;
int dir;
snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
snd_pcm_uframes_t period = (int)rate / 4;
snd_pcm_hw_params_set_period_size_near(handle, params, &period, &dir);
sample_size = rate / 20;
if (snd_pcm_hw_params(handle, params) < 0) {
sample_size = 0;
snd_pcm_close(handle);
handle = NULL;
}
}
# endif // HAVE_ALSA_ASOUNDLIB_H
#endif // __APPLE__
if (sample_size) {
// Make each of the notes using a combination of sine and sawtooth waves
int attack = sample_size / 10;
int decay = 4 * sample_size / 5;
for (int i = 0; i < 9; i ++) {
sample_data[i] = new short[2 * sample_size];
short *sample_ptr = sample_data[i];
for (int j = 0; j < sample_size; j ++, sample_ptr += 2) {
double theta = 0.05 * frequencies[i] * j / sample_size;
double val = 0.5 * sin(2.0 * M_PI * theta) + theta - (int)theta - 0.5;
if (j < attack) {
*sample_ptr = (int)(32767 * val * j / attack);
} else if (j > decay) {
*sample_ptr = (int)(32767 * val * (sample_size - j + decay) /
sample_size);
} else *sample_ptr = (int)(32767 * val);
sample_ptr[1] = *sample_ptr;
}
}
}
}
// Cleanup the SudokuSound class
SudokuSound::~SudokuSound() {
#ifdef __APPLE__
#elif defined(WIN32)
#else
# ifdef HAVE_ALSA_ASOUNDLIB_H
if (handle) {
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
# endif // HAVE_ALSA_ASOUNDLIB_H
#endif // __APPLE__
if (sample_size) {
for (int i = 0; i < 9; i ++) {
delete[] sample_data[i];
}
}
}
// Play a note for 250ms...
void SudokuSound::play(char note) {
Fl::check();
#ifdef __APPLE__
// TODO
#elif defined(WIN32)
Beep(frequencies[note - 'A'], 50);
#else
# ifdef HAVE_ALSA_ASOUNDLIB_H
if (handle) {
// Use ALSA to play the sound...
if (snd_pcm_writei(handle, sample_data[note - 'A'], sample_size) < 0) {
snd_pcm_prepare(handle);
snd_pcm_writei(handle, sample_data[note - 'A'], sample_size);
}
usleep(50000);
return;
}
# endif // HAVE_ALSA_ASOUNDLIB_H
// Just use standard X11 stuff...
XKeyboardState state;
XKeyboardControl control;
// Get original pitch and duration...
XGetKeyboardControl(fl_display, &state);
// Sound a tone for the given note...
control.bell_percent = 100;
control.bell_pitch = frequencies[note - 'A'];
control.bell_duration = 50;
XChangeKeyboardControl(fl_display,
KBBellPercent | KBBellPitch | KBBellDuration,
&control);
XBell(fl_display, 100);
XFlush(fl_display);
// Restore original pitch and duration...
control.bell_percent = state.bell_percent;
control.bell_pitch = state.bell_pitch;
control.bell_duration = state.bell_duration;
XChangeKeyboardControl(fl_display,
KBBellPercent | KBBellPitch | KBBellDuration,
&control);
#endif // __APPLE__
}
// Sudoku cell class...
class SudokuCell : public Fl_Widget {
@ -228,6 +411,7 @@ class Sudoku : public Fl_Window {
SudokuCell *grid_cells_[9][9];
Fl_Group *grid_groups_[3][3];
int difficulty_;
SudokuSound *sound_;
static void check_cb(Fl_Widget *widget, void *);
static void close_cb(Fl_Widget *widget, void *);
@ -244,6 +428,7 @@ class Sudoku : public Fl_Window {
public:
Sudoku();
~Sudoku();
void check_game(bool highlight = true);
void load_game();
@ -287,6 +472,9 @@ Sudoku::Sudoku()
};
// Setup sound output...
sound_ = new SudokuSound();
// Menubar...
prefs_.get("difficulty", difficulty_, 0);
if (difficulty_ < 0 || difficulty_ > 3) difficulty_ = 0;
@ -349,12 +537,16 @@ Sudoku::Sudoku()
resize(X, X, W, H);
}
// Load the previous game...
load_game();
set_title();
}
// Destroy the sudoku window...
Sudoku::~Sudoku() {
delete sound_;
}
// Check for a solution to the game...
void
Sudoku::check_cb(Fl_Widget *widget, void *) {
@ -634,6 +826,8 @@ Sudoku::new_game(time_t seed) {
break;
}
}
sound_->play('A' + t - 1);
}
}
}
@ -727,10 +921,15 @@ Sudoku::solve_game() {
for (i = 0; i < 9; i ++)
for (j = 0; j < 9; j ++) {
SudokuCell *cell = grid_cells_[i][j];
bool play_note = false;
if (cell->value() != grid_values_[i][j]) play_note = true;
cell->value(grid_values_[i][j]);
cell->readonly(1);
cell->color(fl_color_average(FL_GRAY, FL_GREEN, 0.5f));
if (play_note) sound_->play('A' + cell->value() - 1);
}
}
@ -740,7 +939,13 @@ int
main(int argc, char *argv[]) {
Sudoku s;
// Show the game...
s.show(argc, argv);
// Load the previous game...
s.load_game();
// Run until the user quits...
return (Fl::run());
}